为什么写这个博客呢,因为我发现在qt中不论用到什么通信方式,最后处理数据的方法都是一样的,蓝牙数据接收,串口数据接收,网口数据接收,can数据接收,wifi通信(即网口)数据接收,基本都是一个套路完成的接口函数,然后去处理数据,这次写个模板出来不用每次都去查找以前的程序了,也怪费劲的。
qt5.7 mingw windows8
各种模板如下,如后期有新型,也会更新出来
第一步 都是通过各种接口函数,把识别到的一包或者连续几包接收打包起来,到一个QByteArray中去,然后在开启一个函数,
void MainWindow::comBatAnalyze(QByteArray &allData)
去传递这个QByteArray(单次接收全部数据)
不带CRC16 MODBUS校验方式:
1.将全部数据转换为16进制字节类型:
优点:数据内容清晰明确,类似于stm32接收数据处理判断。
缺点:数据定长、也可以自行处理申请动态数组,事先判断数据长度
方法:
void MainWindow::comBatAnalyze(QByteArray &allData)
{
unsigned char rxdata[100];
int len;
bool ok;
memset(rxdata,0,100);//清空数组
QByteArray dataTemp=allData;
len=dataTemp.count();
for(int i=0;i
2.直接判断字符串形式:
优点:数据长度飘忽不定时,没有稳定数据格式,可采用此方式让数据一直累加判断
缺点:写起来麻烦一些,只能判断固定长度数据(因通讯原因而断开的)
方法:
void MainWindow::comBatAnalyze(QByteArray &allData)
{
QByteArray dataTemp;
int len;
int start;
int move;
bool ok;
dataTemp=allData;
while((len=dataTemp.count())>=37*2)
{
QCoreApplication::processEvents(QEventLoop::AllEvents, 200);
move=2;
start=dataTemp.indexOf("42",0);//搜索42字符,返回搜索到的起始位置
if(start>=0)
{
len=len-start;//将42做为开头,长度随之变化
dataTemp=dataTemp.right(len);//从右侧裁剪len长度数据保存
if(len>=37*2)
{
if(dataTemp.mid(0,2)=="42"&&dataTemp.mid(10,2)=="43"&&dataTemp.mid(22,2))//判断开头是否为effeef
{
if(dataTemp.mid(36,2)=="58"&&dataTemp.mid(46,2)=="2e"&&dataTemp.mid(52,2)=="5a"&&dataTemp.mid(60,2)=="2e"&&dataTemp.mid(72,2)=="25")//dian
{
//...
move=37*2;
dataTemp=dataTemp.right(len-move);
if((len-move)>37*2){
dataTemp.clear();
allData.clear();
}
}
else{
dataTemp.clear();
break;
}
}
else{
dataTemp.clear();
break;
}
}
else{
break;
}
}
else{
dataTemp.clear();
break;
}
}
allData=dataTemp;
}
带CRC校验方式:
带crc校验就不能用以上的方式了,因为需要数据的每个字节去分析,方式如下:
void MainWindow::comBatAnalyze(QByteArray &allData)
{
QByteArray dataTemp;
bool ok;
int len;
int start;
dataTemp=allData;
while((len=dataTemp.count())>=25*2)
{
QCoreApplication::processEvents(QEventLoop::AllEvents);
start=dataTemp.indexOf("fe0414",0);//返回该字节数组中字节数组fe第一次出现的索引位置,然后从索引位置向前搜索。
if(start>=0){
len=len-start;
dataTemp=dataTemp.right(len);
if(len>=25*2){
if(dataTemp.mid(0,6)=="fe0414"){
dataTemp=dataTemp.left(50);//只处理一组即可,取前25字节
QCoreApplication::processEvents(QEventLoop::AllEvents);
QByteArray ccc;//crc用
quint16 ddd;//crc用
for(int i=0;i<25;i++){
ccc[i]=dataTemp.mid(i*2,2).toInt(&ok,16);
if(i==22)ddd=crc16ForModbus(ccc);//获取到前23位crc校验码
}
if((ccc[23]==ddd)&&(ccc[24]==(ddd>>8))){//校验crc
//...校验通过,处理数据
}
}
dataTemp.clear();
}
else{
break;
}
}
else{
dataTemp.clear();
break;
}
}
allData=dataTemp;
}
CRC方式参考我这个帖子中的代码。
20221128增加串口接收数据断包补全
优点:可以补全因通讯原因造成的数据断包
缺点:大量数据时无法使用此方法
原理:在通讯接口接收中断有数据时,建立的定时器开启10ms中断,这时候10ms串口过来的数据会在全局QByteArray数组中补全,10ms之后将数组传入到处理函数中,基本就是全的了,如果不全就适当调增延时时间,大量数据时可能不适用,未测试大数据
方法:
头文件:
QSerialPort *serial1;//串口句柄
QByteArray allData1;
QTimer *timeserial1;
函数文件:
//初始化部分
timeserial1=new QTimer(this);
connect(timeserial1,&QTimer::timeout,[=](){
timeserial1->stop();
comBatAnalyze1(allData1);//处理串口得到的数据
});
//串口中断槽函数
void menu::serialRead1()
{
// static QByteArray allData;
timeserial1->start(10);//设置判断10ms内接收完毕数据,在处理
while (!serial1->atEnd()){
allData1 += serial1->readAll().toHex();
}
}
//串口处理函数
void menu::comBatAnalyze1(QByteArray &allData)
{
QByteArray dataTemp;
bool ok;
int len=0;
int start;
int move;
dataTemp=allData.toUpper();
len=dataTemp.size()/2;
qDebug()<
可以发现,接收全了,只要不是发送特别快连包,就能单次处理,如果连包,可以按照上面第二种方式将数据组合判断帧头处理
//后续更新会继续更新本篇博客
记录,分享,查询,让我们成为 搬运工而不只是获取者。