【QT】QT串口接收一帧长字节数据不完整,需要接收两次

1.对于串口读取数据的传统方法readReady()

在使用Qt自带的串口QtSerialPort时。其发送过来的数据需要进行接收,则需要连接一个相应的槽函数:

connect(currentPort ,SIGNAL(readyRead()),this,SLOT( slots_serialRxCallback()));// 有数据就直接接收显示

其中只要是串口中有数据,便会执行slots_serialRxCallback()槽函数,并不是说一帧数据发送完了,才执行一次slots_serialRxCallback()函数。而是接收一部分数据进入一次回调处理函数。

2. 对于长数据帧无法完全接收的弊端

很显然,无法形成一个完整的数据帧就无法进行数据处理。

通过测试单片机利用串口发送数据,一次能够接收33个字节,要上报的数据是48个字节,因此接收数据就会先接收33个字节,再接收15个字节。这就导致了一个问题,两次接收的数据被后续的数据处理函数认为是两个字符串,无法进行通信协议判断。

因此如何将这两次接收的数据拼接为一个完整的数据帧是解决这个问题的要点,另外说明一下,虚拟串口调试,48个字节QT是可以一次性接收的,但是单片机发出来的就不行。有知道其中原因的大佬可以评论留言。

3. 给出的解决方案-拼接

如何拼接呢?经过实验测试,我找到了一个比较简单的方法,不用什么定时器延时接收。

第一步:接收数据触发数据处理槽函数slots_serialRxCallback()

第二步:在触发后断开槽函数数据接收,同时延时5毫秒。

disconnect(currentPort ,SIGNAL(readyRead()),this,SLOT( slots_serialRxCallback()));
while (currentPort->waitForReadyRead(5) == true);

上面这两步是必须的,如果不断开接收槽函数,就会一直进入接收数据处理,造成程序异常,引起死机。延时五毫秒之后。

第三步:在触发接收后,调用一次waitForReadyRead,等待5ms。再重新进入数据接收,将后半串数据进行接收。

connect(currentPort ,SIGNAL(readyRead()),this,SLOT( slots_serialRxCallback()));

我们在头文件中定义

QByteArray myData2; QByteArray rxbuf;
rxbuf = myData2.append(currentPort -> readAll());

rxbuf是拼接成的完整数据帧,myData2是用来存放第一次接收的数据,再readAll。

第四步:将第一次的缓存数据清空用于接收第二次数据,如果不清空这个数组,那么再下一次接收的数据到来时就会进行累加。会随着接收数据量的增加越加越长。

myData2.clear();

经过上述处理就能得到一个完整的数据帧了。

如果不能理解,我为大家列出其流程图,让你直观感受这么处理的妙处。

 回调函数如下:

/**
 * @brief PLCTempControl::slots_serialRxCallback 接收并先更新温度
 * 接收数据格式位34 D0 88 FF FF 3C 01 79 3C 01 7C 3C 01 77 3C 01 71 3C 01 C8 3C 01 A1 3C 01 A9 3C 01 82 3C 01 82 3C 01 70 3C 01 76 3C 01 71 3C 01 6B 01 00 05 5E
 */
void PLCTempControl::slots_serialRxCallback()
{
    disconnect(currentPort ,SIGNAL(readyRead()),this,SLOT( slots_serialRxCallback()));// 有数据就直接接收显示
    while (currentPort->waitForReadyRead(5) == true);
    connect(currentPort ,SIGNAL(readyRead()),this,SLOT( slots_serialRxCallback()));// 有数据就直接接收显示
    rxbuf = myData2.append(currentPort -> readAll());//读串口缓区QByteArray数据 关键是append
    myData2.clear();
    if (!rxbuf.isEmpty())
    {
    qDebug()<<"接收处理函数";
    qDebug()<<rxbuf.toHex(' ');
    }
    if(this->isDataEffective(rxbuf))//判断校验码是否正确,是位真,不是为假。
    {
       switch( (quint8)rxbuf.at(2) )
       {
          case 0x88:
           quint8 tmp1 = rxbuf.at(6); //tmp1高8位
           quint8 tmp2 = rxbuf.at(7); //tmp2低8位
           qint16 y1 = (qint16)((tmp1<<8)|(tmp2<<0));
            tmp1 = rxbuf.at(9); //tmp1低8位
            tmp2 = rxbuf.at(10); //tmp2高8位
           qint16 y2 = (qint16)((tmp1<<8)|(tmp2<<0));
            tmp1 = rxbuf.at(12); //tmp1低8位
            tmp2 = rxbuf.at(13); //tmp2高8位
           qint16 y3 = (qint16)((tmp1<<8)|(tmp2<<0));
            tmp1 = rxbuf.at(15); //tmp1低8位
            tmp2 = rxbuf.at(16); //tmp2高8位
           qint16 y4 = (qint16)((tmp1<<8)|(tmp2<<0));
            tmp1 = rxbuf.at(18); //tmp1低8位
            tmp2 = rxbuf.at(19); //tmp2高8位
           qint16 y5 = (qint16)((tmp1<<8)|(tmp2<<0));
            tmp1 = rxbuf.at(21); //tmp1低8位
            tmp2 = rxbuf.at(22); //tmp2高8位
           qint16 y6 = (qint16)((tmp1<<8)|(tmp2<<0));
            tmp1 = rxbuf.at(24); //tmp1低8位
            tmp2 = rxbuf.at(25); //tmp2高8位
           qint16 y7 = (qint16)((tmp1<<8)|(tmp2<<0));
            tmp1 = rxbuf.at(27); //tmp1低8位
            tmp2 = rxbuf.at(28); //tmp2高8位
           qint16 y8 = (qint16)((tmp1<<8)|(tmp2<<0));
            tmp1 = rxbuf.at(30); //tmp1低8位
            tmp2 = rxbuf.at(31); //tmp2高8位
           qint16 y9 = (qint16)((tmp1<<8)|(tmp2<<0));
            tmp1 = rxbuf.at(33); //tmp1低8位
            tmp2 = rxbuf.at(34); //tmp2高8位
           qint16 y10 = (qint16)((tmp1<<8)|(tmp2<<0));
            tmp1 = rxbuf.at(36); //tmp1低8位
            tmp2 = rxbuf.at(37); //tmp2高8位
           qint16 y11 = (qint16)((tmp1<<8)|(tmp2<<0));
            tmp1 = rxbuf.at(39); //tmp1低8位
            tmp2 = rxbuf.at(40); //tmp2高8位
           qint16 y12 = (qint16)((tmp1<<8)|(tmp2<<0));
            tmp1 = rxbuf.at(42); //tmp1低8位
            tmp2 = rxbuf.at(43); //tmp2高8位
           qint16 y13 = (qint16)((tmp1<<8)|(tmp2<<0));
            tmp1 = rxbuf.at(45); //tmp1低8位
            tmp2 = rxbuf.at(46); //tmp2高8位
           qint16 y14 = (qint16)((tmp1<<8)|(tmp2<<0));
           this -> tempDisplay(y1, y2, y3, y4, y5, y6, y7,
                               y8, y9, y10, y11, y12, y13, y14);//温度显示函数
           break;
       }
    }
}

4.校验位判断

/**
 * @brief PLCTempControl::isDataEffective 检查校验码,验证数据是否有效
 * @param rxbuf
 * @return
 */
bool PLCTempControl::isDataEffective(QByteArray &rxbuf)
{
    QByteArray rxbuffer;
    rxbuffer.resize(48);//申明一个数组存储接收数据
    rxbuf.resize(48);
    static quint8 count = 0;
    static quint8 state = 0;
    static quint8 checksum = 0;

    for(int8_t i = 0;i < 48; i++)
    {
        quint8 res = rxbuf.at(i);
        res = res;
        switch(state)
        {
            case 0:
                if(res == 0x44){
                    count = 0;
                    rxbuffer[count] = res;
                    count++;
                    state = 1;
                    qDebug()<<"首位校验";
                }else{
                    count = 0;
                    state = 0;
                }
            break;
            case 1:
                if(res ==0xD0){
                    rxbuffer[count] = res;
                    count++;
                    state = 2;
                    qDebug()<<"D0位校验";
                }else{
                    count = 0;
                    state = 0;
                }
            break;
            case 2:
                rxbuffer[count] = res;
                count++;
                if(count>=47)state = 3;
            break;
            case 3:
                rxbuffer[count] = res;
                checksum = 0;
                for(quint8 i = 0;i < count; i++)
                {
                    checksum+=rxbuffer.at(i);
                }
                if(res == checksum)
                {
                    return true;
                    qDebug()<<"校验和成功";
                }
                state=0;
                count=0;
                break;
            default:break;
        }
    }
    return false;
}

为界面添加图片的方法

利用QLabl控件,将文字改为图片,要注意载入图片的大小,不然显示不全。

  • 9
    点赞
  • 94
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: 使用QSerialPort接收一包完整数据,请按照以下步骤: 1. 配置串口参数:创建一个QSerialPort对象,并设置串口的相关参数,比如波特率、数据位、停止位等等。这样可以确保串口能正确传输数据。 2. 定义数据接收槽函数:创建一个槽函数来接收串口传来的数据。在这个函数中,可以使用QSerialPort的readAll()函数来获取完整的一包数据。可以通过一个字节数组来存储这些数据。 3. 连接信号和槽:使用QObject的connect()函数将串口的readyRead()信号连接到上述定义的槽函数,这样当串口有数据可读时,就会自动调用槽函数来接收数据。 4. 打开串口:在接收数据之前,需要调用QSerialPort的open()函数打开串口,以便开始接收数据。 5. 接收完整数据:当串口有数据可读时,readyRead()信号会被触发,对应的槽函数会被调用。在槽函数中,可以使用readAll()函数将所有可读数据读取到一个字节数组中。可以使用字节数组的size()函数来判断是否接收了一包完整数据。 6. 关闭串口:在接收完整数据后,调用QSerialPort的close()函数关闭串口,以节省系统资源。 上述是使用QSerialPort接收一包完整数据的简单流程。根据实际需求,你可能需要接收槽函数中加入更多的处理逻辑,比如对数据进行解析、校验等等。 ### 回答2: qserialport是Qt框架提供的一个串口通信的类,用于在应用程序中实现串口通信功能。要接收一包完成的数据,可以按照以下步骤进行: 1. 创建一个QSerialPort对象,并设置串口的参数,例如串口号、波特率、数据位、校验位等。可以使用setPortName()、setBaudRate()、setDataBits()等方法来设置参数。 2. 打开串口连接,使用open()方法打开串口连接。 3. 建立一个槽函数,用于接收串口数据。可以使用readyRead()信号和相关的槽函数进行数据接收。当串口有数据到达时,readyRead()信号会被触发,并调用相应的槽函数。 4. 在槽函数中,使用readAll()方法来读取串口的数据。readAll()方法会读取串口缓冲区中的所有数据,并将其返回。 5. 对接收数据进行处理,可以将其存储到缓冲区中,或者进行解析和处理,根据实际需求进行操作。 6. 如果是一包完整数据,可以根据特定的数据格式或者数据包头部来判断数据是否完整。例如,可以使用特定的标志位或者固定度来判断是否收到一整包数据。 7. 当判断到数据完整时,关闭串口连接,使用close()方法关闭串口连接。 需要注意的是,数据接收过程依赖于串口发送端的数据发送频率、数据格式等因素。可以根据实际情况灵活调整接收数据的方式和顺序,以确保数据完整性和正确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

米杰的声音

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值