如何使用Qt编程实现串口接收不定长数据帧

limilie 2014-09-04 11:27:59
要接收像这样的不定长数据帧,该怎么实现,使用的是第三方Qextserialport类。
帧结构:
帧头1 (1byte)+ 帧头2(1byte) + 数据长度(1byte) + 数据(n byte) + 校验(1byte) + 帧尾1(1byte) + 帧尾2(1byte)

学习了下Qextserialport在Windows的例子,使用事件触发的方式,串口接收到数据就发出readyRead()信号(QIODevice类),触发与之连接的槽函数执行读取数据操作readAll(),保存到自定义的缓存中处理。但是我发现readyRead信号不是每接收到一个字节就发出的,而是连续的一串数据或是几个数据之后。如果单片机向上位机发送数据的过程中,出现停顿,也就是一帧数据分几次传输这种情况,那readyread信号发出几次就无法知道。我是用单片机编程的思路来想这个问题的,单片机串口接收只要有一字节的数据接收到,就会产生中断,在中断里就对数据进行判断,放入缓冲中相应的位置。说得好乱-_-||

在网上查资料和论坛,大概是一下的思路:
1.创建循环缓冲区;
2.串口readyRead信号发出后,就把数据读取到循环缓冲区,修改指针;
3.开一个线程,读循环缓冲区,查找帧头,若找到,则把一帧数据读出,修改指针,判断数据帧是否有效,并解析;
不知道行不行,求指点
...全文
3855 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
hanrp0702 2016-08-13
  • 打赏
  • 举报
回复
引用 5 楼 caoshangpa 的回复:
http://blog.csdn.net/caoshangpa/article/details/50915005 这里有详细解决方案
我也遇到了这个问题,我想有没有什么函数,可以设置串口帧数据间隔,比如设置10ms以内的数据就是一帧数据,10ms以外的数据是另一帧。不然数据处理的时候判断帧头和帧尾挺麻烦的。
草上爬 2016-04-18
  • 打赏
  • 举报
回复
http://blog.csdn.net/caoshangpa/article/details/50915005 这里有详细解决方案
vectorLayer 2014-10-09
  • 打赏
  • 举报
回复
遇到同样的问题。 思路: 1.你的数据帧由于采用数据判断数据帧是否完整,所以即使有断点也可以用帧头帧尾定界。那么就只剩下上位机如何接收完整数据帧的问题。 2.readAll方法读一次数据就清一次缓存区,如果数据帧发送不能一次完成 解包过程就无法进行 ,所以没法界定数据帧传输是否完成。既然不定长搞不定 那就定义一个定长帧 ,我只需要将要发送的数据拷到自定义定长帧里面,多的字节用无意义字节补齐。控制读取字节,当缓存区数据达到帧长后再读取。在解包函数中将数据解出来就OK。 if(myCom->bytesAvailable() >=MSG_SIZE ) { QByteArray temp = myCom->readAll(); }
limilie 2014-09-06
  • 打赏
  • 举报
回复
引用 1 楼 zmlovelx 的回复:
不定长,先读头,读到头后,判断长度,然后再一次性读取此长 度的包。
嗯,我知道这样处理,但是串口接收函数readAll(),不是一次 就能把一帧数据读出来的,会出现断帧和粘包。我现在这样处理 ,关闭的时候程序会死机 //读取数据 SerialBuffer_t<unsigned char,100> gSerbufferIn;//缓冲 void MainWindow::serialRead() { int datalen = Serial->bytesAvailable(); QByteArray temp; temp = Serial->readAll(); for(int i=0;i<temp.count();i++){ unsigned char fs = temp.at(i); //相当于强制类型转 换static_cast<unsigned char>,直接用static_cast<unsigned char>不行。。。 gSerbufferIn.Put(fs); //放入循环缓冲 } } 线程中: static enum { RX_START = 0, RX_HEADA, RX_HEADB, RX_LEN, RX_DATA, RX_TAILA, RX_TAILB }chRxState; #define RX_TASK_FSM_RESET() do{chRxState = RX_START;} while(0) void Ser_test(void) { chRxState = RX_START; unsigned char chTempData; unsigned char chRxLength = 0; unsigned char *pRxData; bool isBufEmpty = true; while(2) { if(!gSerbufferIn.IsEmpty()){ //判断缓冲区是否为空 switch(chRxState) { case RX_START: chRxState = RX_HEADA; //break; case RX_HEADA: isBufEmpty = gSerbufferIn.Get(chTempData); //qDebug() << "data:" << chTempData; if(0xaa == chTempData){ chRxState = RX_HEADB; } break; case RX_HEADB: isBufEmpty = gSerbufferIn.Get(chTempData); if(0xaa == chTempData){ chRxState = RX_LEN; }else { chRxState = RX_HEADA; } break; case RX_LEN: QThread::msleep(1); isBufEmpty = gSerbufferIn.Get(chTempData); chRxLength = chTempData; //qDebug() << "mythread:" << chTempData; chRxState = RX_DATA; break; case RX_DATA: if(chRxLength < gSerbufferIn.InUse()){ for(int chIndex = 0 ;chIndex < chRxLength;chIndex++){ gSerbufferIn.Get(pRxData[chIndex]); } chRxState = RX_TAILA; //qDebug() << "length:" << chRxLength; } break; case RX_TAILA: isBufEmpty = gSerbufferIn.Get(chTempData); if(0x55 == chTempData){ chRxState = RX_TAILB; }else { chRxState = RX_HEADA; } break; case RX_TAILB: isBufEmpty = gSerbufferIn.Get(chTempData); if(0x55 == chTempData) { RX_TASK_FSM_RESET(); //qDebug() << "RX frame:" << pRxData; }else { chRxState = RX_HEADA; } break; } QThread::msleep(1);//to do another work }else { QThread::msleep(10); // } } } 这样可以接收到了数据帧,但是退出的时候,程序死机,网上查了说事指针使用不当,自己查来查去也没找到问题,自己基础太差了
帅得不敢出门 2014-09-04
  • 打赏
  • 举报
回复
不定长,先读头,读到头后,判断长度,然后再一次性读取此长度的包。
jiuchang 2014-09-04
  • 打赏
  • 举报
回复
应该是先读3个字节,包含头和长度n 然后再读n+3字节 当然后面应该加上校验

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧