打印
[C语言]

一种高效的串口数据包接收方式

[复制链接]
楼主: 科技猎人
手机看帖
扫描二维码
随时随地手机跟帖
21
科技猎人|  楼主 | 2014-1-15 13:25 | 只看该作者 回帖奖励 |倒序浏览
dirtwillfly 发表于 2014-1-14 21:50
谢谢楼主分享

谢谢斑竹支持。。。

使用特权

评论回复
22
chenbb8| | 2014-1-15 13:39 | 只看该作者
队列的方式,如果是一个一个字节的压入队列的话,处理起来比较麻烦,可以用一个状态机,取一个字节处理一次
如果是一个一个数据帧的压入队列的话,这就可以用普通的方式来处理了。

使用特权

评论回复
23
chenbb8| | 2014-1-15 13:41 | 只看该作者
有些人用硬件DMA实现了FIFO式的接收,没看代码 因为双缓冲+DMA已经满足我的要求了,我想他应该是一个一个字节的压入FIFO的吧~

使用特权

评论回复
24
科技猎人|  楼主 | 2014-1-15 14:09 | 只看该作者
chenbb8 发表于 2014-1-15 13:39
队列的方式,如果是一个一个字节的压入队列的话,处理起来比较麻烦,可以用一个状态机,取一个字节处理一次 ...

之前做一个音频编解码的,串口传输,用的就是循环队列缓存方式。因为是数据流,没有包得概念,也不校验,接收是不均匀的,每收一个字节就放到队尾;处理是均匀的,每隔固定时间从队首取一字节。这种搭配是很合理的。。。

使用特权

评论回复
25
科技猎人|  楼主 | 2014-1-15 14:19 | 只看该作者
如果是用“循环队列缓存法”处理数据包得话就麻烦了。中断接收也是每收一个字节就放到队尾。需要不断查询整个队列是否有一个完整的包收到,而判断一个数据包就需要有一个特定的包格式了,如固定的包首,报尾,长度,校验等。如果数据传输有错误,就更容易找错数据包了。找到数据包后一般还得再拷贝到另一个零时缓存区以便应用层处理。优点是不容易丢包,接收简单;缺点是需要一个专用的大的缓存区,数据包要有特定格式,取数据包繁琐易错,一包出错往往会连带后面好多包也出错。相比之下“时隙断句缓存法的”缺点是还需要一个定时器中断来配合接收处理。。。

使用特权

评论回复
26
科技猎人|  楼主 | 2014-1-15 14:24 | 只看该作者
chenbb8 发表于 2014-1-15 13:41
有些人用硬件DMA实现了FIFO式的接收,没看代码 因为双缓冲+DMA已经满足我的要求了,我想他应该是一个一个字 ...

硬件DMA的FIFO式接收 因为多用了特定硬件对CPU的释放肯定是最好的。如果在结合上硬件或软件实现的“时隙断句”那么“DMA下的时隙断句缓存接收法”应该是最高效的了。不过大部分单片机还没有DMA这东东。。。

使用特权

评论回复
27
huangxz| | 2014-1-15 14:45 | 只看该作者
“时隙断句”怎么理解?

使用特权

评论回复
28
金融小数| | 2014-1-15 14:47 | 只看该作者
简单的看了楼主的代码,感觉很乱,好像也没看到楼主所谓的串口机制。

使用特权

评论回复
29
科技猎人|  楼主 | 2014-1-15 15:12 | 只看该作者
huangxz 发表于 2014-1-15 14:45
“时隙断句”怎么理解?

“时隙断句”就是靠字节间的时间间隙来判断一个数据包是否已完全接收。就像是读一篇**靠句之间的自然停顿来把句子区分开。

使用特权

评论回复
30
科技猎人|  楼主 | 2014-1-15 15:21 | 只看该作者
金融小数 发表于 2014-1-15 14:47
简单的看了楼主的代码,感觉很乱,好像也没看到楼主所谓的串口机制。

好吧,确实有些乱,因为好多时候整理代码比写代码更无聊,更让人烦躁。我说的串口机制确实有,也确实不太容易找到。“MsgOS-51”是一个完整的单片机系统框架,而这篇贴子说的串口接收机制只是其中的一个点而已。个人觉得“MsgOS-51”对于大多数菜鸟来说都是一座学习的富矿,当然如果您是高手的话,也就意义不大了。

使用特权

评论回复
31
huangxz| | 2014-1-15 15:22 | 只看该作者
科技猎人 发表于 2014-1-15 15:12
“时隙断句”就是靠字节间的时间间隙来判断一个数据包是否已完全接收。就像是读一篇**靠句之间的自然停 ...

你如果把这个说成timeout大家容易理解一点,
这个其实就是大家经常使用的超时来判断接收是否完成的功能。
不过这种方式实际使用起来还是会影响通讯速度的,而且这种用超时判断的方式本身就跟异步通信的原理有相违背的地方。异步通信就是希望数据不受时间的影响,而只与协议关联。

使用特权

评论回复
评分
参与人数 1威望 +2 收起 理由
科技猎人 + 2 赞一个!
32
chenbb8| | 2014-1-15 15:32 | 只看该作者
huangxz 发表于 2014-1-15 15:22
你如果把这个说成timeout大家容易理解一点,
这个其实就是大家经常使用的超时来判断接收是否完成的功能。
...

如果数据连续发过来,包和包之间没有时间间隔的话,也没问题啊

超时自然就形成了一个新的接收帧,而如果数据超过 接收帧能容纳的大小的话,也生成一个接收帧。
只要后台能从两个甚至以上的接收帧中合成出一个通讯包就可以了。

不过要保证切换帧的动作足够快,不会丢失数据才行。

使用特权

评论回复
评分
参与人数 1威望 +2 收起 理由
科技猎人 + 2 赞一个!
33
科技猎人|  楼主 | 2014-1-15 15:35 | 只看该作者
huangxz 发表于 2014-1-15 15:22
你如果把这个说成timeout大家容易理解一点,
这个其实就是大家经常使用的超时来判断接收是否完成的功能。
...

好吧,不同的人理解不一样观点也不一样,和而不同嘛。一般发送数据包肯定是一次全部发出去吧,发送中有中断道是也有一定几率,但在绝大多数情况下,尤其对于应用层,通信单位是包或帧。这应该是最自然的分隔方法了。我在应用中遇到过特定的数据包格式会影响程序处理、数据编码或系统运行等其他方面,会遇到些麻烦。而我说的方法是不需要有特定格式的,但然有的话也不影响。

使用特权

评论回复
34
科技猎人|  楼主 | 2014-1-15 15:38 | 只看该作者
chenbb8 发表于 2014-1-15 15:32
如果数据连续发过来,包和包之间没有时间间隔的话,也没问题啊

超时自然就形成了一个新的接收帧,而如果 ...

先辈高人啊,“连包”和“换帧要快”确实是关键问题。。。

使用特权

评论回复
35
科技猎人|  楼主 | 2014-1-15 15:44 | 只看该作者
说成“超时断句”也可以,但要注意这里是字节间隙时间超时,不是整个数据包接收超时。在我的实际应用中数据包长度是不定的,肯能是10字节也可能是1000字节,那么数据包的发送总时间也就不定了。。。

使用特权

评论回复
36
mohanwei| | 2014-1-15 17:31 | 只看该作者
环形缓冲区才是效率最高的……

静态内存毫无疑问比动态的来得更快,而且取数据时不需要拷贝数据,直接根据读写位置指针访问缓冲区的buff就好了,处理完了直接修改一下读写为位置指针。

使用特权

评论回复
评分
参与人数 1威望 +2 收起 理由
科技猎人 + 2 赞一个!
37
chenbb8| | 2014-1-15 17:42 | 只看该作者
mohanwei 发表于 2014-1-15 17:31
环形缓冲区才是效率最高的……

静态内存毫无疑问比动态的来得更快,而且取数据时不需要拷贝数据,直接根据 ...

不需要拷贝数据,直接根据读写指针访问缓冲区?
你这种做法关闭中断的时间过长。

使用特权

评论回复
38
科技猎人|  楼主 | 2014-1-15 21:39 | 只看该作者
mohanwei 发表于 2014-1-15 17:31
环形缓冲区才是效率最高的……

静态内存毫无疑问比动态的来得更快,而且取数据时不需要拷贝数据,直接根据 ...

这样做的话数据不够安全灵活,如果缓存包数较多的话,需要一个较大的专用内存空间。在我的实际应用中有这样的情况,A口收到数据包,解析后发现要原样发送到B口(485总线),但B口首次发送失败要延时100毫秒后再次发送。这时我的程序是不释放次包数据的内存的,而且可以马上处理A口收到的下一包。什么时候B口的那包数据发送成功了才释放相应内存。如果用环形换存区的话好像不好处理吧,只能处理一包释放一包的数据空间,才好处理下一包。

使用特权

评论回复
39
mohanwei| | 2014-1-16 16:09 | 只看该作者
chenbb8 发表于 2014-1-15 17:42
不需要拷贝数据,直接根据读写指针访问缓冲区?
你这种做法关闭中断的时间过长。 ...

你研究一个具体的环形缓冲区API代码就知道了,一方只管写,另一方只管读,只要保证环形缓冲区对象变量是基本类型,并且不允许溢出(写入方判断缓冲区已满后直接丢弃待写入的数据),是不需要关中断的。

使用特权

评论回复
40
mohanwei| | 2014-1-16 16:14 | 只看该作者
科技猎人 发表于 2014-1-15 21:39
这样做的话数据不够安全灵活,如果缓存包数较多的话,需要一个较大的专用内存空间。在我的实际应用中有这 ...

一般收和发是各自使用一个单独的环形缓冲区的:
收环形缓冲区:中断只写,应用程序只读
发环形缓冲区:应用程序只写,发送中断里只读

环形缓冲区是确定性的,如果来不及处理,就是通信协议设计的不好,系统架构也烂,好处是不会破坏其他线程;
动态内存申请是不确定性的,最轻的就是申请不到,丢弃本帧数据,这点跟环形缓冲区被填满后果相当——但是这点破通信数据把动态内存耗光,很可能会导致其他工作线程、OS崩溃……

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则