2022-02-03 22:14:14 +08:00
|
|
|
|
#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"},
|
2022-03-30 17:11:59 +08:00
|
|
|
|
{"m0,1","00"},
|
|
|
|
|
{"qRcmd,726573756d65","OK"}
|
2022-02-03 22:14:14 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|