link-scope3/serialocd.cpp

285 lines
9.1 KiB
C++
Raw Normal View History

#include "serialocd.h"
SerialOCD::SerialOCD(QObject *parent) : QThread(parent)
{
}
//启动OCD由主线程调用
void SerialOCD::startConnect(const QString &serial, int port)
{
serialName=serial;//保存串口号和端口号后启动子线程
listenPort=port;
start();//启动线程
}
//停止OCD(由主线程调用)
void SerialOCD::stopConnect()
{
emit onStopConnect();//发送停止信号
QTimer *exitTimer=new QTimer();//定时500ms退出子线程保证事件循环关闭tcp和串口
exitTimer->singleShot(500,this,&SerialOCD::quit);
connect(exitTimer,&QTimer::timeout,exitTimer,&QTimer::deleteLater);
}
//子线程函数
void SerialOCD::run()
{
if(startSerial(serialName) && startServer(listenPort))//串口和tcp都连接成功才进入事件循环
exec();//进行事件循环
}
//启动tcp服务器返回是否成功
bool SerialOCD::startServer(int port)
{
server=new QTcpServer();//创建服务器对象
if(server->listen(QHostAddress::Any,port))
{
connect(server,&QTcpServer::newConnection,[=]{//连接槽函数,在有新连接时进入
socket=server->nextPendingConnection();//获取套接字
connect(socket,SIGNAL(readyRead()),this,SLOT(slotSocketReadyRead()),Qt::DirectConnection);
});
//退出时关闭服务器
connect(this,&SerialOCD::onStopConnect,server,&QTcpServer::close,Qt::QueuedConnection);
connect(this,&SerialOCD::onStopConnect,server,&QTcpServer::deleteLater,Qt::QueuedConnection);
}
else
{
emit onErrorOccur(QString("端口%1监听失败").arg(port));
server->deleteLater();
return false;
}
return true;
}
//停止tcp服务器
void SerialOCD::stopServer()
{
if(server)
{
if(server->isListening())
server->close();//关闭服务器
server->deleteLater();//销毁服务器对象
}
}
//tcp接收槽函数
void SerialOCD::slotSocketReadyRead()
{
//固定指令回复表,若匹配则直接回复
const QList<QList<QString>> fixReplyTable={
{"qSupported","PacketSize=30"},
{"H","OK"},
{"?","S05"},
{"qC","QC0"},
{"qAttached","1"},
{"g","00000000"},
{"p","00"},
{"qOffsets","TextSeg=0"},
{"qTStatus","T1"},
{"qSymbol","OK"},
{"m0,1","00"},
{"qRcmd,726573756d65","OK"}
};
QString input=socket->readAll();//获取接收数据
QRegExp inputRx("\\$([^#]+)#");//正则表达式截取指令部分
inputRx.indexIn(input);
input=inputRx.cap(1);
if(input.isEmpty())//若截取失败则直接退出
return;
for(int i=0;i<fixReplyTable.length();i++)//依次在回复表中匹配
{
const QList<QString> &pair=fixReplyTable.at(i);
if(input.startsWith(pair.at(0)))//若匹配成功则直接返回表中字符串
{
sendToClient(pair.at(1));
return;
}
}
if(input.startsWith("m"))//读取内存指令
{
QRegExp rx("m([0-9a-f]+),([0-9a-f]+)");//正则截取地址和字节数量
rx.indexIn(input);
sendSerialReadMem(rx.cap(1).toUInt(NULL,16),rx.cap(2).toUInt(NULL,16));//发送串口指令
}
else if(input.startsWith("M"))//写入内存指令
{
QRegExp rx("M([0-9a-f]+),[0-9a-f]+:([0-9a-f]+)");//正则截取地址和要写入的内容
rx.indexIn(input);
QString rawData=rx.cap(2);
QByteArray data;//将截取到的内容转为qbytearray
for(int i=0;i<rawData.length()/2;i++)
data.append(rawData.mid(i*2,2).toUInt(NULL,16));
sendSerialWriteMem(rx.cap(1).toUInt(NULL,16),data);//发送串口指令
sendToClient("OK");//向gdb回复OK
}
else if(input.startsWith("qRcmd,7265736574"))
{
sendSerialReset();//发送串口指令
sendToClient("OK");//向gdb回复OK
}
else//其他未知指令,回复空字符串
{
sendToClient("");
}
}
//向tcp客户端gdb发送字符串
void SerialOCD::sendToClient(const QString &data)
{
QByteArray array=data.toLatin1();
int sum=0;//进行校验和计算
for(int i=0;i<array.length();i++)
sum+=array.at(i);
sum%=0x100;
QString output=QString("+$%1#%2").arg(data).arg(sum,2,16,QLatin1Char('0'));//拼接指令字符串
socket->write(output.toStdString().c_str(),output.length());//用当前socket发送
}
//获取当前串口列表
QStringList SerialOCD::getSerialList()
{
QStringList res;
foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts()) //遍历并添加获取到的串口名称
res<<info.portName();
return res;
}
//启动串口连接,返回是否连接成功
bool SerialOCD::startSerial(const QString &name)
{
waitReadMemTimer=new QTimer();//创建读内存超时定时器
waitReadMemTimer->setSingleShot(true);//仅运行一次
waitReadMemTimer->setInterval(1000);//超时设定为1s
connect(waitReadMemTimer,&QTimer::timeout,[=]{
sendToClient("00000000");
emit onErrorOccur("内存数据读取超时");
});
port=new QSerialPort();//创建串口对象
port->setPortName(name);//设定端口号
if(port->open(QIODevice::ReadWrite))//打开串口
{
port->setBaudRate(QSerialPort::Baud115200,QSerialPort::AllDirections); //波特率115200
port->setDataBits(QSerialPort::Data8); //8数据位
port->setFlowControl(QSerialPort::NoFlowControl);
port->setParity(QSerialPort::NoParity); //无校验位
port->setStopBits(QSerialPort::OneStop); //1停止位
connect(port,SIGNAL(readyRead()),this,SLOT(slotSerialReadyRead()),Qt::DirectConnection);
//退出时断开串口
connect(this,&SerialOCD::onStopConnect,port,&QSerialPort::close,Qt::QueuedConnection);
connect(this,&SerialOCD::onStopConnect,port,&QSerialPort::deleteLater,Qt::QueuedConnection);
}
else
{
emit onErrorOccur("打开串口失败");
waitReadMemTimer->deleteLater();
port->deleteLater();
return false;
}
return true;
}
//断开串口
void SerialOCD::stopSerial()
{
waitReadMemTimer->deleteLater();
port->clear();//清空串口数据
port->close();//断开串口
port->deleteLater();//销毁串口对象
}
//串口接收槽函数
void SerialOCD::slotSerialReadyRead()
{
serialBuf.append(port->readAll());//读出数据追加到缓存区内
parseSerial();//解析缓存区数据
}
//解析串口缓存区数据
void SerialOCD::parseSerial()
{
if(serialBuf[0]==(char)DEBUG_FRAME_HEADER)//第一个字节是帧头,可以进入解析
{
if(serialBuf.size()>2 && serialBuf.size()>=serialBuf[1])//缓存区内数据长度足够
{
char cmd=serialBuf[2];//命令码
int frameLen=serialBuf[1];//帧长
if(cmd==SerialCMD_ReadMem)//返回的是下位机读取的内存数据
{
waitReadMemTimer->stop();
QString res="";
int dataLen=frameLen-3;
for(int i=0;i<dataLen;i++)//拼接结果字符串返回给gdb客户端
res+=QString("%1").arg((unsigned char)serialBuf[i+3],2,16,QLatin1Char('0'));
sendToClient(res);
}
serialBuf.remove(0,frameLen);//从缓存区中移除当前帧
if(serialBuf.size()>0)//若缓冲区仍有数据说明后面还有数据帧,递归解析
parseSerial();
}
}
else//帧错误或不完整
{
while(serialBuf[0]!=(char)DEBUG_FRAME_HEADER && serialBuf.size()>0)//去除错误数据
serialBuf.remove(0,1);
if(serialBuf.size()>0)//若缓冲区仍有数据说明后面还有数据帧,递归解析
parseSerial();
}
}
//发送串口指令,请求下位机发送指定地址处指定长度的内存数据
void SerialOCD::sendSerialReadMem(int addr, int len)
{
if(port->isOpen())
{
QByteArray arr;
arr.resize(8);
arr[0]=DEBUG_FRAME_HEADER;
arr[1]=8;
arr[2]=SerialCMD_ReadMem;
arr[3]=len;
for(int i=0;i<4;i++)//将地址拆分为四个字节
arr[i+4]=(addr>>(i*8))&0xFF;
port->write(arr);//串口发送
waitReadMemTimer->start();
}
}
//发送串口指令,向下位机指定地址处写入指定的内存数据
void SerialOCD::sendSerialWriteMem(int addr, const QByteArray &data)
{
if(port->isOpen())
{
QByteArray arr;
int frameLen=7+data.length();//计算总帧长
arr.resize(frameLen);
arr[0]=DEBUG_FRAME_HEADER;
arr[1]=frameLen;
arr[2]=SerialCMD_WriteMem;
for(int i=0;i<4;i++)//将地址分为四个字节
arr[i+3]=(addr>>(i*8))&0xFF;
for(int i=0;i<data.length();i++)//依次写入数据
arr[i+7]=data.at(i);
port->write(arr);
}
}
//发送串口复位指令
void SerialOCD::sendSerialReset()
{
if(port->isOpen())
{
QByteArray arr;
arr.resize(3);
arr[0]=DEBUG_FRAME_HEADER;
arr[1]=3;
arr[2]=SerialCMD_Reset;
port->write(arr);
}
}