2.27 USART串口数据包

数据包定义

学习:

如何规定一个合理的数据包格式、以及如何收发数据包。

数据包的作用:把一个个单独的数据打包起来,方便我们多字节的数据通信。(我们之前学了串口的代码,发送1个字节和接收1个字节都没问题,但在实际应用中,我们需要把多个字节打包为一个整体进行发送,比如,有个陀螺仪传感器,他需要用串口发送数据到STM32,陀螺仪的数据,比如X轴一个字节、Y轴一个字节、Z轴一个字节,总共三个数据,需要连续不断的发送,当连续发送的时候,比如:XYZXYZXYZ这样,会出现一个问题,就是接受方不知道哪个对应X、Y、Z,因为接收方可能会从任意位置接收,会导致数据错位的现象。)

HEX数据包

研究几个问题:

1.包头包尾和数据载荷重复的问题。

如图所示,我们定义了FF为包头,FE为包尾,如果我传输的数据本身就是FF和FE怎么办呢?

这个问题确实存在,如果数据和包头包尾重复,可能会引起误判,对于这个问题,有下面几种解决方法:

(1)、限制载荷数据的范围,如果可以的话,我们在发送的时候,对数据进行限制。

(2)、如果无法避免载荷数据和包头包尾重复,那我们就尽量使用固定长度的数据包,这样由于载荷数据是固定的,只要包头包尾对齐对齐了数据,我们就可以知道哪个数据是包头包尾,哪个是载荷数据。在接收载荷数据的时候,我们并不会判断他是否是包头包尾;而在接收包头包尾的时候,我们会判断它是不是确实是包头包尾,用于数据对齐。

(3)、增加包头包尾的数量,并且尽量呈现出和载荷数据出现不了的状态。

2.包头包尾是否是全部需要的问题。

包头包尾并不是全部都需要的,比如我们可以只要一个包头,把包尾删掉,这样数据包的格式就是,一个包头FF,加4个数据。

3.固定包头和可变包头的选择问题。

对于HEX数据包来说,如果你的载荷出现和包头包尾重复的情况,那就最好选择固定包长,这样可以避免接收错误。

4.各种数据转换为字节流的问题。

数据包都是一个字节一个字节组成的,如果你想发送16位的整型数据、32位的整型数据、float、double,甚至是结构体,其实都没有问题,因为他们内部都是由一个字节一个字节组成的,还需要用一个uint8_t的指针指向他,把他们当做一个字节数组发送就行了。

文本数据包

由于数据译码变成的字符形式,这就存在了大量的字符可以作为包头包尾,可以有效避免载荷和包头包尾重复的问题。

比如这里,就是使用了@作为包头,/r/n作为包尾,在载荷数据中间,可以出现除了包头包尾的任意字符。

文本数据包常用的地方:蓝牙模块的AT指令、CNC和3D打印机常用的G代码,都是文本数据包的格式。缺点就是解析效率低。

数据包收发流程

HEX数据包

HEX数据包的发送,就是定一个数组,填充数据,然后用上一节写过的SendArray函数把这个数组发送出去。

接收一个HEX数据包,这里演示的是固定包长HEX数据包的接收方法:

通过之前的学习,我们知道,每收到一个字节,程序都会进一遍中断,在中断函数里,我们可以拿到这一个字节,但拿到之后,我们就得退出中断了。所以每拿到一个数据,都是一个独立的过程,所以在程序我们要设计一个能记住不同状态的机制(状态:包头、数据、包尾),在不同的状态执行不同的操作。同时还要对进行状态的合理转移。这种程序思维就叫做“状态机”。

在这里我们就使用状态机的方法来接收一个数据包。 每个状态需要用一个变量来标志一下,比如我这里用变量S来标志,三个状态依次为S=0、S=1、S=2,根据S的值,在中断程序中进行判断,分别进入不同状态的程序。

文本数据包

文本数据包的发送,就是定一个字符串,然后用上一节写过的SendString函数把这个字符串发送出去。

接收一个文本数据包,这里演示的是可变包长文本数据包的接收方法:

同样也是利用状态机,定义3个状态,在中断函数中进行判断,并且随着状态的不同,在程序中改变S的值。

但要注意,这个是可变包长,我们在接收数据的时候,要时刻监视是不是收到包尾了,所以S=1时,也就是接收数据时,也要判断数据是不是包尾,如果是包尾,则数据不接收,同时跳到下一个状态,等待包尾。