打印

请教一下 串口协议数据的几种处理方法

[复制链接]
16752|36
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
sfesdm|  楼主 | 2012-5-12 13:17 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
我们知道,串口的协议数据一般包括 帧头+地址+功能码+校验码+数据+帧尾 等组成。
请教一下,大家平时是怎么处理串口协议的呢,我用过的方法有两种,第三种方法听别人说过,但不知道具体是怎么样的实现思路。

第一种:超时判断,思路就是每在接收到串口数据,进入接收中断后,保存数据,并赋值一个变量,然后该变量的值在定时器里面自减,若自减到零说明对方数据传送完成,由此可判断接收完成,下一步就是在主程序里处理串口数据了。
这种办法现在基本不使用。

第二种:在接收中断里逐级判断,只有一级一级的数据判断正确,才进行接收,最后再判断校验码和帧尾,只有整个过程的数据都正确,才能确定数据接收完成,此时置位一个标志位,在主程序里处理通过此标志位执行相应程序。
现在我一直用的就是这种方法,但这种方法很容易有一种限制,就是只能一帧一帧的处理数据,就是说对方发了一帧数据后,下一帧数据要间隔一段时间(如10MS)再发送,如果两帧或以上的数据一起发送,第二种处理方法就会产生错误。

第三种:我也是听人说过,就是可以解决第二种只能一帧一帧接收数据的问题,具体的实现思路我也不清楚,只能分别简单描述一下,第三种方法也是我上来发帖请教的目的。
这种处理方法就是开辟一个相对较大的数组用于存放接收到的数据,接收程序只管接收数据,若接收到的数据到达数组尾端,下一个接收到的数据又会从数组头开始存放,即会把之前存放的数据覆盖掉。
在主程序里,不断扫描数组里面存放的数据,搜索里面的功能协议,每一条协议都要从数组里搜索一遍,每次循环都要这样做。我的疑问是怀疑自己理解错了,要是这样的话,单单搜索协议都要耗费大量的CPU时间,貌似不太现实。

请教一下各位大侠,是怎么处理多帧数据同时传输的问题的呢?

相关帖子

沙发
林粼粼| | 2012-5-12 15:32 | 只看该作者
中断接收只往缓冲内写数据,主程序从缓冲取数据,按照协议合并成完整的帧。基本思路就是这样

使用特权

评论回复
板凳
林粼粼| | 2012-5-12 15:36 | 只看该作者
把你的 第二种 接收中断里逐级判断,只有一级一级的数据判断正确 放在主程序里取缓冲数据之后。就是常用的做法

使用特权

评论回复
地板
sfesdm|  楼主 | 2012-5-12 15:56 | 只看该作者
3# 林粼粼
你好,如果像你说的那样,在主程序里搜索接收缓冲区,这样我就有几个疑问:
1、这样做的方法,接收中断只管接收,并不会置什么接收完成的标志位,这样就会导致:在主程序里的搜索程序是每次循环必然会执行到的。
举个例子,如果接收缓冲区比较大(对于51单片机来说),有几K,然后有几十条协议,什么设置时间、设置状态、返回时间、返回计数结果等等。那搜索程序是不是要从第一条协议开始,从接收缓冲区的头开始搜索,如果从头到尾都没搜索到第一条协议,那又从第二条协议,从接收缓冲区的头开始一直搜索……一直到最后一条协议搜索完……从下一次循环,又开始反复上面的过程呢?
那这样是不是像我上面说的,耗费了大量的CPU时间呢……

使用特权

评论回复
5
受不了了| | 2012-5-12 16:11 | 只看该作者
用MODBUS,只需要用到位读写,字节读写,就四五条协议,位和字节都有对应的地址,你把“什么设置时间、设置状态、返回时间、返回计数结果等等”和地址对应起来,再多功能都够了

使用特权

评论回复
6
林粼粼| | 2012-5-12 16:21 | 只看该作者
缓冲区不需要很大。保证2次处理期间的数据不漏就行了

你有必要先了解一下协议分层。

使用特权

评论回复
7
草根白骨精| | 2012-5-12 16:59 | 只看该作者
我就用的第一种方法,个人看法如下:
1:第一种方法主要适用于变字长帧接收,缺点是要占用一个定时器(两字节间隔超过4个字节时间即认为一帧传输完毕);
2:如果数据帧是定字长,那么你最好使用你的第二种方法
3:第三种实际就是以上两种的延伸,主要适用于通信速率繁忙的状况下,你可以开辟多个变量记录每帧的字长,循环处理存放数据即可,内部RAM够大不怕浪费你可以使用多缓存数组+状态机处理

使用特权

评论回复
8
ahgao| | 2012-5-12 19:10 | 只看该作者
对于一般单片机主要是用于控制用途,第二种是最实用的,它一般都采用半双工,请求/应答式通讯,对应的单片机以单任务的方式在运行(至少有通讯需求的部分是单任务性质)。第三种采用通讯缓冲区,对于需要大数据量交换,多任务或多线程应用意义重大,同时,程序的复杂性也增加很多。但是如果你的单片机应用仍然是单任务请求/应答式,这种方式意义不大,仅仅只是节约了数据本身的传输时间,而在控制用途的场合,尤其是链路不稳定情况下,如简单的无线通讯,如何保证通讯的正确性远比数据传输速度重要的多。

第三种方式的缓冲区一般需要FIFO,既环形缓冲器,分别有读写指针。希望这个可以回答你四楼的问题。

为什么说第三种程序要复杂很多呢,举一个例子,你考虑一个通讯出错以后重传的问题就明白了。可不是重新发一次就行的哦。

最后要说的是,通讯协议够用就好,不是貌似功能越强大越好。即使优秀如USB,也把传输模式分成了四种以应对不同情况。

使用特权

评论回复
9
dairen1990| | 2012-5-12 21:16 | 只看该作者
第三种方法就是用环形缓冲+状态机+时间轮询来解析传输协议的

使用特权

评论回复
10
dairen1990| | 2012-5-12 21:16 | 只看该作者
第三种方法就是用环形缓冲+状态机+时间轮询来解析传输协议的

使用特权

评论回复
11
sfesdm|  楼主 | 2012-5-12 21:17 | 只看该作者
8# ahgao
谢谢指教!如你所说,目前我确实只用到了单任务的通讯协议,但随着知识点的积累,我觉得有必要进行进一步的学习和了解,虽然目前也许用不上第三种方法,但我感觉,只要是做这方面技术的,以后会有用上的一天的。。。

使用特权

评论回复
12
ningling_21| | 2012-5-13 16:06 | 只看该作者
下一个接收到的数据又会从数组头开始存放,即会把之前存放的数据覆盖掉。
这说明缓冲区太小了,改大就可以了...
接收完再一帧一帧处理...

使用特权

评论回复
13
dqyubsh| | 2012-5-13 19:24 | 只看该作者
去查一下freemodbus

使用特权

评论回复
14
红金龙吸味| | 2012-5-14 10:25 | 只看该作者
一般用第三种,上面已经有朋友说了,fifo+fsm主循环解析

使用特权

评论回复
15
汽车电子| | 2012-5-14 10:58 | 只看该作者
中断只做两件事:接收数据进缓冲,清零超时计数器
void    UartInterrupt(void)
{
     ......
     Rec.OverTime=0;
     if(Rec.Counter<REC_BUFFER_LENGTH)
        Rec.Buffer[Rec.Counter++] = Data;
}

后台程序,10ms执行1次。注:Rec.OverTime为4字节整形,永远不会溢出
void    TaskUartRec(void)
{
       if(++Rec.OverTime != 2)    return;     // 超时20ms
       ......  //  接收处理程序,扫描帧头,可做贴包处理
}

帧头 + 校验 + 命令 + 方向 + 流水号 + 保留 + 长度 + 数据
帧头为2字节或4字节
其中方向、流水号、保留等可选,也可为其它内容。长度为数据段长度

很多人喜欢把校验放最后面,对前面所有字节进行校验。其实这样不好,校验的位置不定,增加程序的复杂性。  
而校验放在帧头后面,固定位置,程序更简单,使用结构就简单多了。只对校验字后面的数据进行校验就可以了,帧头为固定内容,没必要校验。

使用特权

评论回复
16
lxc806705| | 2012-5-14 15:49 | 只看该作者
中断只做两件事:接收数据进缓冲,清零超时计数器
void    UartInterrupt(void)
{
     ......
     Rec.OverTime=0;
     if(Rec.Counter
汽车电子 发表于 2012-5-14 10:58

这样对单任务是没有问题,比如说从GPS接收时间信息

假设下这种情况:在一个系统里有大量的广播信息(发的比较快)
那么这时候缓冲内的数据就会被冲掉,怎么办?
这时候我就在中断内进行帧鉴别然后导出有效内容到全局变量传递给主程序
但是这样会让中断程序显得比较大,而且用了全局变量很多个,看着很不爽
看了上面的帖子还是没太搞明白,有没有好的方法同时解决这2个问题。
我知道FIFO应该是可以的,但是那个东西我觉得又太复杂了些
因为我在FPGA里自己写异步FIFO的时候对写和读指针的判断就感觉很麻烦

真心请教

使用特权

评论回复
17
bhsdlmj| | 2012-5-14 17:00 | 只看该作者
我用第一种  我觉得简单好用的。

使用特权

评论回复
18
sfesdm|  楼主 | 2012-5-14 20:14 | 只看该作者
16# lxc806705
对于15楼汽车电子的方法,我感觉跟我说的第一种方法如出一辙,因为只有接收到数据才会把Rec.OverTime清零,主程序才会执行到接收处理程序,否则,只要Rec.OverTime不翻转,接收处理程序永远不会执行到的。

使用特权

评论回复
19
汽车电子| | 2012-5-14 20:28 | 只看该作者
开个几KB的DMA缓冲,是最爽爽的了。
DMA超时中断,就取全部数据
电脑串口通常开32KB缓冲

使用特权

评论回复
20
lxc806705| | 2012-5-15 08:54 | 只看该作者
16# lxc806705  
对于15楼汽车电子的方法,我感觉跟我说的第一种方法如出一辙,因为只有接收到数据才会把Rec.OverTime清零,主程序才会执行到接收处理程序,否则,只要Rec.OverTime不翻转,接收处理程序永远不会执 ...
sfesdm 发表于 2012-5-14 20:14
恩这种方法就是循环缓冲我理解的对吧?但这样无法解决想要得到的报文被其他报文
冲掉的问题;如果用第二种方法在中断中对帧进行判别处理,又会写出较长的程序,占用
较长的中断时间,这样会不会对主程序造成影响?

使用特权

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

本版积分规则

49

主题

346

帖子

3

粉丝