vb串口 任意波特率_基于单片机串口数据帧解析的一种方法

前言
   大家好,我是川楠,前段时间在芯吧客的问答频道上回答了好几个关于串口数据帧解析的问题。想想当初我学习单片机的时候,也在这个地方折腾了不少时间,看过了很多的代码,也尝试了多种写法。
   实现串口数据帧断帧,有很多的方法,比如使用串口的IDLE中断进行断帧,使用定时器根据时间断帧、使用特殊标识符进行断帧等等,刚开始工作的时候,我自己就写好几个版本,基本上一个项目一个版本,但是现在我使用的基本上只有这一个版本。

方法一:
   使用串口的IDLE中断:比如STM32有串口的IDLE中断,在数据发送完成,串口进入空闲的时候会产生一个IDLE中断,在HAL中也使用到这种方式。但是,其他的单片机不一定有IDLE中断,比如51单片机、MSP430等等,所以这个方式并不是万能的,.
方法二:
使用特殊字符:给数据包的帧头和帧尾使用一个或者一组特定的字符,也就是我们说的帧头和帧尾。这个有一定的概率会造成数据包错位或者数据包内容和帧头尾内容一样。如果数据帧帧尾丢失,着会这造成单片机一直等待数据帧帧尾,进入死等情况。
方法三:
   使用串口+定时的方式进行断帧。定时器主要用来计时数据帧与帧之间的时间,这要这个时间大于一个设定的阈值,超出这个时间就表示当前数据帧发送完毕,下一次来的数据,一定是一个新的数据帧。
本次主要对方法三做一个说明。
这也是我在项目中常用一种方式,这种方式可以适用于串口、RS485、RS422等通讯接口,其带有循环缓存队列的思想用在CAN总线通讯中也十分合适(CAN通讯不需要自己去判断报文的帧头帧尾),这样的通讯设置方式能够极大的降低数据掉包率。

使用资源
   串口     1个
   定时器    1个

实现基本原理
采用循环队列,将串口的数据流进行接收,根据定时器计时单个BYTE之间的时间,只要这个时间超过一个阈值,则认为下一个数据为新的数据帧开头。(使用循环缓存队列进行数据接收、采用定时器进行数据断帧),
数据接收采用的是单字节中断接收方式。在查串口的中断函数中,只进行数据接收,不对数据进行处理。在主函数中,才对数据帧的合法性进行判断和数据帧内容进行处理和应答。

特点
采用循环缓存数据队列,有效的降低数据掉包风险
适用于串行通讯
要求发送数据必须连续,且帧与帧之间需要有一定的时间间隔。
数据帧可以实现任意长度
采用生产消费者模型的设计思路


下面我就举个自己设计的项目例子吧。本次使用STM32F103RCT6单片机,当然我也将这种方式用到过MSP430、8051、NRF24LE1等单片机中。

数据帧规定
一个完整的数据包由开始字符、设备类型码、设备地址码、命令字、数据长度、数据区、CRC校验及结束字符组成。如下所示:

8bfa435a118b7c1c89c7cc4e012f5890.png

各区域含义如下:
开始字符:1字节,表示一个数据包的开始。固定为0xAA。
设备类型:1字节,表示设备类型,固定为0x01。所有设备都能接收广播类型(0xFF)的命令请求
设备地址:1字节,表示设备在系统中的地址,由用户可以自行定义,默认值为0x01。所有设备都能接收广播地址(0xFF)的命令请求。
命令字:1字节,即功能命令。见下文,按照规定的格式访问设备。
数据长度:1字节,表示后面跟随的有效数据区的字节数,范围0  -  240。
数据区:N字节,为有效数据,长度为前面数据长度定义的字节数。数据长度为0时没有数据区。数据区的数值,参见命令描述。
CRC校验:2字节,从开始字符到数据区最后一个字节的所有字符的16位CRC校验值。校验值低位再前,高位在后。
结束字符:1字节,表示数据包结束。固定为0x0E。

数据帧断帧时间阈值
在发送数据包的时候,要求先准备好所有要发送的字符,连续发送出去,中间不得有较长时间停顿。
多个数据包间必须有3.5个BYTE传送时间的时间间隔。
例:RS232传输位如下:
1 起始位
8 数据位,首先发送最低有效位
0 位作为奇偶校验
1 停止位

计算时间:T=3.5*( 1+数据位+奇偶校验+停止位) / 波特率
如:使用9600,则间隔时间为4ms。
在波特率大于19200的时候,使用固定的时间1.75ms

3c1b3ab0dfdcb9977aa6c452392577e0.png

注意:既然定义了这样的时间间隔,那么就必须要要求发送端发过来的数据帧也要满足这个时间间隔的标准。

eg:约定总线的波特率为115200,如果发送端需要发送3个数据帧的时候,那么就必须要要求这个三个数据帧与帧的时间间隔必须要要大于1.75MS,否者接收端就会把这三个数据帧认为是一个数据帧。

程序代码实现
首先需要对串口初始化:将单片机的串口配制成实际要求的串口波特率,和通讯格式,我这里默认配置为115200,8,N,1的方式。串口要打开接收中断,每接收一个BYTE数据,就要进入中断一次。
定时器也需要进行初始化,将定时的时间间隔为50uS,并且启动定时器,让定时器间隔50uS进入中断函数一次。
串口与定时器初始化的代码我就不公布了,这是单片机开发人员的常规操作。而且每款单片机的初始化的方式不一样。

接下来就进入主题了,在进入主题之前,先介绍下我所定义的一些结构体类型,不然后面看起来会相当的吃力:
首先,定义了一个T_FramCtlType的数据结构体。这个结构体用来记录帧与帧之间的时间间隔。主要是用在定时器的中断函数中,这里大家有个相关的映象即可:

cd7eed89566b14cf48b7f595435eb5d9.png

然后我定义了一个消息和循环缓存的数据结构体:

351e58eb2e9079884490042edef3cbb1.png

ComMsgType单个数据帧的最大长度,我这里定义为为250个字节,大家在使用的时候,可以根据自己的协议设计要求,可以自行定义。
MsgFifoType是串口消息的循环缓存队列和帧时间的一个结构体,可以缓存10个数据帧,后面我们所有的操作都是围绕着这个结构体进行操作。
   接下来是协议相关的,我这里宏定义了数据帧的帧头,帧尾和数据帧类容的结构体。

b2bd2ba37271abdfd1432f203df7db40.png

下面开始看看代码的编写,首先是结构体数据的初始化。

ca8c0576a3783743406d9564e604459d.png

注意:COMMsgFifo[_COMx]这个是我为了适配多路串口同时使用,所以定义了多个MsgFifoType类型的缓存,一路串口一个COMMsgFifo缓存池。所以这里需要稍微把思路转换下......

点击下方“
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值