赞
踩
最近一段时间在做有关串口通讯的Qt项目,其中与MCU STM32那部分的通讯比较令人头疼,因为MCU处理的是HEX16进制数,而Qt应用更多的倾向于对字符串的处理,经过这段时间的痛苦而又煎熬的摸索,也算是有所新的认识。在这中秋佳节即将来临之际,也得空写点关于这方面的心得,算是一个记录笔记吧。
MCU STM32控制端:
几个控制按键挂在MCU上,随着按键的动作相应地发出指定数据帧,通过串口RX/TX传给ARM板端,Qt应用作出相应动作。
ARM Qt显示应用端:
需要Qt的应用能够接受到MCU 发出的数据帧,并且解析出有用的数据,进行相应动作。
MCU那边的串口通讯我就不说了,应该比较常见。我着重讲一下Qt这边怎么处理一个完整的数据帧。
一个完整的数据帧,包括帧头、标识符、数据长度、数据块、校验和帧尾等,比如:
帧头 | 各种标识 | ... | 数据长度 | 数据块 | 校验 | 帧尾 |
FF | EE | ... | 12 | 数据长度个字节 | ** | AA |
串口通讯中,以商议好的数据帧形式发送和接受数据,能够有效的避免数据接受不完整,接受错误。使用指定形式的数据帧是一种有效、准确的通讯方式。
校验:
数据在传输过程中,可能会存在数据出错的情况。为了保证数据传输的正确性,因此会采取一些方法来判断数据是否正确,或者在数据出错的时候及时发现进行改正。常用的几种数据校验方式有奇偶校验、CRC校验、LRC校验、格雷码校验、和校验、异或校验等。
一般串口通讯中常用到的校验有CRC32、CRC16、和校验或是异或校验等。如果大家有兴趣可以多找一找这方面的资料看看,我在这里就不多做赘述。讲的比较详细:
https://blog.csdn.net/zhengqijun_/article/details/53150749
划重点————————————————————————————————————————————————————
MCU发出的是16进制数据帧,Qt善于处理QString的字符串。
我先说一下Qt 解析部分大概思路:1. 串口读取为二进制字节组---QByteArray;2. 将QByteArray 写入数据流QDataStream,而后从中按一个一个字节读取出,直到结束;3. 按数据帧协议处理一个字节一个字节,环环相扣,引入状态机机制,解析完整帧。
直接上代码。Talk is cheap, show me the code.
头文件声明:
- QString buffer;//串口接受数据缓冲区
- QString bufferdatalen;//数据长度两字节字符串缓存
- QString frameData;//
- uint8_t checkSum, SendBuf[200];//校验异或和
- ushort TxNum;
-
- int num;
- int state_machine;//协议解析状态机
- int lencnt, datalen;
- bool stopped;
- bool ok;
CPP文件解析函数一览:
- //读取串口数据帧并处理
- void SerialPortA::readMyComA(QByteArray temp)
- {
- QDataStream out(&temp, QIODevice::ReadWrite);
- // qDebug()<<"MyComA temp hex"<<temp.toHex();
-
- while (!out.atEnd()) {
- qint8 outChar = 0;
- out>>outChar;
- QString byte = QString("%1").arg(outChar&0xFF, 2, 16, QLatin1Char('0'));
- // qDebug()<<"byte"<<byte;
- byte = byte.toUpper();
-
- if(state_machine == 0)//协议解析状态机
- {
- if(byte == INC1)//起始符1
- state_machine = 1;
- else
- state_machine = 0;//状态机复位
- }
- else if(state_machine == 1)
- {
- if(byte == INC2)//起始符2
- state_machine = 2;
- else
- state_machine = 0;//状态机复位
- }
- else if(state_machine == 2)
- {
- if(byte == INC3)//起始符3
- state_machine = 3;
- else
- state_machine = 0;//状态机复位
- }
- else if(state_machine == 3)
- {
- if(byte == FRAMESTAR)//帧头
- state_machine = 4;//
- else
- state_machine = 0;
- }
- else if(state_machine == 4)
- {
- if(byte == FRAMETYPE)//帧类型
- {
- state_machine = 5;
- buffer = byte;
- }
- else
- state_machine = 0;
- }
-
- /×××××××××中间的部分按照各自的协议数据帧进行处理×××××××××××××/
-
- else if(state_machine == 12)//第一字节数据长度
- {
- buffer += byte;
- bufferdatalen = byte;
- state_machine = 13;
- }
- else if(state_machine == 13)//第二字节数据长度
- {
- buffer += byte;
- bufferdatalen += byte;
- lencnt = 0;//接收数据计数器
- datalen = bufferdatalen.toInt(&ok, 16);//接收数据长度
- // qDebug()<<"datalen is :"<<datalen<<"bufferdatalen"<<bufferdatalen;
- state_machine = 14;
- }
- else if((state_machine == 14) || (state_machine == 15))//接收一定长度的数据块
- {
- frameData += byte;//数据保存
- buffer += byte;
- if(lencnt == (datalen-1))
- state_machine = 16;
- else
- {
- state_machine = 15;
- ++lencnt;
- }
- }
- else if(state_machine == 16)//进行校验
- {
- TxNum = 0;
- if(buffer.isEmpty()) return;
- // buffer += "AA";//加上帧尾
- // qDebug()<<"用于计算校验位的字符串: "<<buffer;
- QByteArray temp;
- temp = QByteArray::fromHex(buffer.toLatin1().data());//获取buffer数据为字符串,需要转换成16进制
-
- QDataStream out(&temp, QIODevice::ReadWrite);//将字节组读入
- while(!out.atEnd())
- {
- qint8 outchar = 0;
- out >> outchar;//每字节填充一次,直到结束
- SendBuf[TxNum] = (uint8_t)(outchar & 0xff);
- TxNum++;
- }
- checkSum = (uint8_t)(myAlgorithm->Sum_Calculate(SendBuf, TxNum));//求帧头以外所有数据的校验和
- QString strSum = QString("%1").arg(checkSum&0xFF, 2, 16, QLatin1Char('0'));
- strSum = strSum.toUpper();
- byte = byte.toUpper();
-
- // qDebug()<<"Check SUM is :"<<strSum<<"Byte is:"<<byte;
- if(byte == strSum)//判断校验XOR是否相等
- {
- state_machine = 17;
- // qDebug()<<"!!!!!!!!!!!!!!!!!!!!!!!";
- sendOneFrame(CORRECT_RESPONSE);//校验无异常
- }
- else
- {
- state_machine = 0;
- sendOneFrame(ERROR_RESPONSE);//校验异常
- }
- }
- else if(state_machine == 17)
- {
- // qDebug()<<"*************"<<byte;
- if((byte == "AA") || (byte == "aa")) //判断是否接收到帧尾结束符
- {
- // qDebug()<<"frame data :"<<frameData<<"buffer"<<buffer;
- // qDebug()<<"MyComA Receive Frame End AA!!!";
- /****************数据块8字节分析*********************/
- byte8Analysis(frameData);
- /*****************数据块8字节分析********************/
- }
- frameData = "";
- buffer = "";
- state_machine = 0;//状态机复位
- }
- }
- }
校验计算函数:
- uint8_t Algorithm::Sum_Calculate(uint8_t *puchMsg, uint16_t usDataLen)
- {
- uint8_t CRCValue=0;
- uint16_t i=0;
- for ( i=0; i<usDataLen; i++ )
- {
- CRCValue = puchMsg[i] ^ CRCValue;//进行异或校验取值
- }
- return CRCValue;
- }
解析部分,按照自身协商的数据帧,实际情况进行处理分析。大概的思路已经给出。
在此,也感特别谢@yuxiangs的转发博客给予的思路:
https://blog.csdn.net/yilongdashi/article/details/80449315#comments
以上仅是个人对qt中串口使用的大概了解,各位有补充的欢迎留言指导及斧正,谢谢大家,预祝大家中秋佳节快乐。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。