Qt 和 MCU 的串口通讯(完整帧)

8 篇文章 2 订阅
2 篇文章 1 订阅

前言:

        最近一段时间在做有关串口通讯的Qt项目,其中与MCU STM32那部分的通讯比较令人头疼,因为MCU处理的是HEX16进制数,而Qt应用更多的倾向于对字符串的处理,经过这段时间的痛苦而又煎熬的摸索,也算是有所新的认识。在这中秋佳节即将来临之际,也得空写点关于这方面的心得,算是一个记录笔记吧。

介绍:

项目情况

MCU STM32控制端:

几个控制按键挂在MCU上,随着按键的动作相应地发出指定数据帧,通过串口RX/TX传给ARM板端,Qt应用作出相应动作。

ARM Qt显示应用端:

需要Qt的应用能够接受到MCU 发出的数据帧,并且解析出有用的数据,进行相应动作。

MCU那边的串口通讯我就不说了,应该比较常见。我着重讲一下Qt这边怎么处理一个完整的数据帧。

相关知识点

一个完整的数据帧,包括帧头、标识符、数据长度、数据块、校验和帧尾等,比如:

帧头各种标识...数据长度数据块校验帧尾
FFEE...12数据长度个字节**AA

串口通讯中,以商议好的数据帧形式发送和接受数据,能够有效的避免数据接受不完整,接受错误。使用指定形式的数据帧是一种有效、准确的通讯方式。

校验

数据在传输过程中,可能会存在数据出错的情况。为了保证数据传输的正确性,因此会采取一些方法来判断数据是否正确,或者在数据出错的时候及时发现进行改正。常用的几种数据校验方式有奇偶校验、CRC校验、LRC校验、格雷码校验、和校验、异或校验等。

一般串口通讯中常用到的校验有CRC32、CRC16、和校验或是异或校验等。如果大家有兴趣可以多找一找这方面的资料看看,我在这里就不多做赘述。讲的比较详细:

https://blog.csdn.net/zhengqijun_/article/details/53150749

划重点————————————————————————————————————————————————————

Qt 解析完整数据帧

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中串口使用的大概了解,各位有补充的欢迎留言指导及斧正,谢谢大家,预祝大家中秋佳节快乐。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值