stm32HAL库串⼝DMA接收不定长度数据及粘包处理
串⼝接收不定长度数据及数据粘包解析的实现
1、如何让串⼝接收不定长度数据
想让Stm32 串⼝接收不定长度数据,这就需要我们开启串⼝空闲中断(IDLE)⽅式,所谓串⼝空闲中断指的是stm32的数据总线在接收数据的过程中,如果总线在接收⼀个字节所需要的时间内没有再接收到数据,单⽚机就会判定此时数据已经接收完成了,这时单⽚机会⾃动触发空闲中断IDLE标志位,引发空闲中断,我们只需要进⼊中断取数据就可以了。使⽤IDLE空闲中断我们就可以⽤串⼝接收任意长度的数据了。
2、串⼝接收不定长度数据的实现思路
我们实现串⼝接收不定长度数据的思路是:⾸先我们要定义⼀个接收数据的缓冲区,⼀般⽤数组接收数据,在串⼝初始化时要开启串⼝的空闲中断和接收中断。然后在有中断产⽣时,我们需要在串⼝中断函数⾥判断是空闲中断还是正常接收⼀个字节数据引起的接收中断,如果是正常接收字节的中断,那么我们需要把接收到的这个字节数据存放到缓冲数组中,如果是IDLE空闲中断,表⽰串⼝数据已经接收完成了,我们需要在IDEL中断处理函数中设置⼀个数据接收完成标志位表⽰已经完整的接收到⼀帧数据了,如:RecFlag=1;
3、数据粘包解析的实现思路
数据粘包是多个数据包发送时由于线路延时,或者发送端发送多个数据包的时间延时很⼩,导致⼏个数据包⼏乎同时到达接收端(数据包到达接收端的时间间隔⼩于⼀个字节时间),这样单⽚机接收数据时就会将这⼏个数据包当做⼀帧数据来接收存放。那么我们如何将这⼏个数据包合成的⼀帧数据拆解成⼏个数据包呢?其实,实现的⽅法也很简单,这就需要我们在发送端和接收端的数据格式上做⼀个统⼀的约定,约定了统⼀的数据发送格式在发送数据时就严格按照这个格式来发送。⼀般来说约定的格式我们要明确规定数据头和数据长度。然后我们再根据定义的数据头是什么数据,在这⼀帧数据中逐个去判断当前数据是不是数据头,如果是就说明这个是⼀个⼩数据包的开始位置,在根据数据长度就可以解析出⼀个数据包了。例如,我们约定发送的数据格式为:数据头(2byte)+发送者ID(1byte)+接收者ID(1byte)+ 命令码(1byte)+ 数据长度(1byte)+CRC
校验(2byte)。这样我们定义了标准的数据格式就容易拆包了。
4、下⾯我们就根据约定的数据发送格式来定义具体的数据头,例如我们定义⼀个数据包命令来查询接收命令的单⽚机PH值传感器1的数据是多少。
数据包定义如下:
数据头:0xAA55
发送者ID:0x01
接收者ID:0x02
命令码:0x01 //查询PH值传感器1命令随意约定命令代码
0x02 //响应PH查询命令
0x03 //设置传感器PH值上限命令
0x10 //设置成功
数据长度:xx
CRC校验:0xB1B5 // CRC 16 Modbus
那么发送端发送的查询数据为:aa 55 01 02 01 00 b5 b1
接收查询命令响应的数据发送格式也要按照约定的数据格式发送出去:
响应数据为:aa 55 02 01 02 02 00 07 12 91
如此约定了数据格式,如果真的发⽣粘包的情况,解析数据也很⽅便了,我们只需要到数据头的标志0xAA55,然后读取该数据包代表数据长度的字节存放的存储位置,就可以得到数据长度,⽐如数据包:aa 55 02 01 02 02 00 07 12 91 数据长度的位置就在AA这个字节之后的第五个字节,假设此时AA字节在缓冲数组的位置为RecBuf[i],那么长度字节存放的位置就是RecBuf[i+5],取出RecBuf[i+5]中的数据为2,说明该字节之后有2个字节的数据,再加上CRC的2个字节,我们就需要在RecBuf[i+5]之后还要取出4个字节的数据,才能完整的取出这个⼩数据包:aa 5502 0102 02 00 0712 91
需要注意的是我们再拆包的过程中要重复考虑其中存在的问题,⽐如:数据长度错误时的数量,不到数据头时循环变量的修
正等。
5、基于cubemx HAL库的实现⽅法
(1)新建cubemx⼯程选择stm32f103ze芯⽚(2)开启外部⾼速时钟HSE,配置好系统时钟树
(3)配置串⼝1 ,启⽤DMA传输,使能中断
DMA 接收配置成循环模式,数据位宽默认为8位
模拟串口使用printf函数
DMA 发送配置成正常模式,数据位宽默认为8位
(4)设置好⼯程名称和保存位置,选择⾃⼰⽤的开发⼯具和版本,然后⽣成⼯程代码就可以开始编写我们⾃⼰的代码了。
6、我们先看结果
⾸先,我们先测试单个命令发送(没有粘包)
命令1:查询PH指令aa 55 01 02 01 00 b5 b1
收到查询指令后,进⾏解析然后执⾏查询命令,向主机返回PH数据【返回数据】:aa 55 02 01 0202 00 07 12 91 // 返回PH值等于7
命令2:设置PH值上限指令aa 55 01 02 03 02 00 0b 57 5b //设置PH上限为11 【返回数据】:aa 55 02 01 10 00 49 a5 // 返回设置成功指令0x10
接下来,我们测试⼀下数据粘包的情况
我们把之前的2个命令合在⼀起发送出去,红⾊数据⽤来模拟有⼲扰的情况下,数据出错了。a2 55aa 55 01 02 01 00 b5 b132 15 8e 20 aa 55 01 02 03 02 00 0b 57 5b68 21
结果如下:
在串⼝收到这⼀帧数据后,调⽤拆包函数进⾏数据解析,按照数据头逐个进⾏⽐较只要到正确的数据头,就按照约定的数据长度去取数据,取到⼀个⼩包数据后进⾏CRC校验,校验正确后才执⾏命令。第⼀个⼩包执⾏完成后接着去取下⼀个包,然后执⾏,直到把接收到的数据解析并执⾏完成。