link-scope3/serialocd.cpp

286 lines
9.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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"}
};
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())
{
if(len>8)//最多一次性读取8个字节
len=8;
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);
}
}