打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

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

2022.07.10 北京

关注

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();

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

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

 回调函数如下:

  1. /**
  2. * @brief PLCTempControl::slots_serialRxCallback 接收并先更新温度
  3. * 接收数据格式位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
  4. */
  5. void PLCTempControl::slots_serialRxCallback()
  6. {
  7. disconnect(currentPort ,SIGNAL(readyRead()),this,SLOT( slots_serialRxCallback()));// 有数据就直接接收显示
  8. while (currentPort->waitForReadyRead(5) == true);
  9. connect(currentPort ,SIGNAL(readyRead()),this,SLOT( slots_serialRxCallback()));// 有数据就直接接收显示
  10. rxbuf = myData2.append(currentPort -> readAll());//读串口缓区QByteArray数据 关键是append
  11. myData2.clear();
  12. if (!rxbuf.isEmpty())
  13. {
  14. qDebug()<<"接收处理函数";
  15. qDebug()<<rxbuf.toHex(' ');
  16. }
  17. if(this->isDataEffective(rxbuf))//判断校验码是否正确,是位真,不是为假。
  18. {
  19. switch( (quint8)rxbuf.at(2) )
  20. {
  21. case 0x88:
  22. quint8 tmp1 = rxbuf.at(6); //tmp1高8位
  23. quint8 tmp2 = rxbuf.at(7); //tmp2低8位
  24. qint16 y1 = (qint16)((tmp1<<8)|(tmp2<<0));
  25. tmp1 = rxbuf.at(9); //tmp1低8位
  26. tmp2 = rxbuf.at(10); //tmp2高8位
  27. qint16 y2 = (qint16)((tmp1<<8)|(tmp2<<0));
  28. tmp1 = rxbuf.at(12); //tmp1低8位
  29. tmp2 = rxbuf.at(13); //tmp2高8位
  30. qint16 y3 = (qint16)((tmp1<<8)|(tmp2<<0));
  31. tmp1 = rxbuf.at(15); //tmp1低8位
  32. tmp2 = rxbuf.at(16); //tmp2高8位
  33. qint16 y4 = (qint16)((tmp1<<8)|(tmp2<<0));
  34. tmp1 = rxbuf.at(18); //tmp1低8位
  35. tmp2 = rxbuf.at(19); //tmp2高8位
  36. qint16 y5 = (qint16)((tmp1<<8)|(tmp2<<0));
  37. tmp1 = rxbuf.at(21); //tmp1低8位
  38. tmp2 = rxbuf.at(22); //tmp2高8位
  39. qint16 y6 = (qint16)((tmp1<<8)|(tmp2<<0));
  40. tmp1 = rxbuf.at(24); //tmp1低8位
  41. tmp2 = rxbuf.at(25); //tmp2高8位
  42. qint16 y7 = (qint16)((tmp1<<8)|(tmp2<<0));
  43. tmp1 = rxbuf.at(27); //tmp1低8位
  44. tmp2 = rxbuf.at(28); //tmp2高8位
  45. qint16 y8 = (qint16)((tmp1<<8)|(tmp2<<0));
  46. tmp1 = rxbuf.at(30); //tmp1低8位
  47. tmp2 = rxbuf.at(31); //tmp2高8位
  48. qint16 y9 = (qint16)((tmp1<<8)|(tmp2<<0));
  49. tmp1 = rxbuf.at(33); //tmp1低8位
  50. tmp2 = rxbuf.at(34); //tmp2高8位
  51. qint16 y10 = (qint16)((tmp1<<8)|(tmp2<<0));
  52. tmp1 = rxbuf.at(36); //tmp1低8位
  53. tmp2 = rxbuf.at(37); //tmp2高8位
  54. qint16 y11 = (qint16)((tmp1<<8)|(tmp2<<0));
  55. tmp1 = rxbuf.at(39); //tmp1低8位
  56. tmp2 = rxbuf.at(40); //tmp2高8位
  57. qint16 y12 = (qint16)((tmp1<<8)|(tmp2<<0));
  58. tmp1 = rxbuf.at(42); //tmp1低8位
  59. tmp2 = rxbuf.at(43); //tmp2高8位
  60. qint16 y13 = (qint16)((tmp1<<8)|(tmp2<<0));
  61. tmp1 = rxbuf.at(45); //tmp1低8位
  62. tmp2 = rxbuf.at(46); //tmp2高8位
  63. qint16 y14 = (qint16)((tmp1<<8)|(tmp2<<0));
  64. this -> tempDisplay(y1, y2, y3, y4, y5, y6, y7,
  65. y8, y9, y10, y11, y12, y13, y14);//温度显示函数
  66. break;
  67. }
  68. }
  69. }

4.校验位判断

  1. /**
  2. * @brief PLCTempControl::isDataEffective 检查校验码,验证数据是否有效
  3. * @param rxbuf
  4. * @return
  5. */
  6. bool PLCTempControl::isDataEffective(QByteArray &rxbuf)
  7. {
  8. QByteArray rxbuffer;
  9. rxbuffer.resize(48);//申明一个数组存储接收数据
  10. rxbuf.resize(48);
  11. static quint8 count = 0;
  12. static quint8 state = 0;
  13. static quint8 checksum = 0;
  14. for(int8_t i = 0;i < 48; i++)
  15. {
  16. quint8 res = rxbuf.at(i);
  17. res = res;
  18. switch(state)
  19. {
  20. case 0:
  21. if(res == 0x44){
  22. count = 0;
  23. rxbuffer[count] = res;
  24. count++;
  25. state = 1;
  26. qDebug()<<"首位校验";
  27. }else{
  28. count = 0;
  29. state = 0;
  30. }
  31. break;
  32. case 1:
  33. if(res ==0xD0){
  34. rxbuffer[count] = res;
  35. count++;
  36. state = 2;
  37. qDebug()<<"D0位校验";
  38. }else{
  39. count = 0;
  40. state = 0;
  41. }
  42. break;
  43. case 2:
  44. rxbuffer[count] = res;
  45. count++;
  46. if(count>=47)state = 3;
  47. break;
  48. case 3:
  49. rxbuffer[count] = res;
  50. checksum = 0;
  51. for(quint8 i = 0;i < count; i++)
  52. {
  53. checksum+=rxbuffer.at(i);
  54. }
  55. if(res == checksum)
  56. {
  57. return true;
  58. qDebug()<<"校验和成功";
  59. }
  60. state=0;
  61. count=0;
  62. break;
  63. default:break;
  64. }
  65. }
  66. return false;
  67. }

为界面添加图片的方法

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

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
QTcpSocket 发送和接收数据的几种方法
认清基本数据类型和Qt串口通信数据类型转换
Qt学习 之 Socket通信(世界上最简单的例子了)
(转)4.3加载和保存(LoadingandSaving)
Qt6编程之QtEndian大小端字节序转换类使用案例
Qt模块化笔记之network——套接字Socket网络编程起步
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服