串口完结了两个终端设备之间进行牢靠的通讯,串口在这中心完结了传输层的效果。本次要讲的是关于数据的协议。
相似场景
洞幺!洞幺!我是洞拐!收到请答复!收到请答复!over!
在战役体裁影视剧中常常可以看到这样的对白,在经过对讲机等相关无线设备呼叫队友时,先呼叫对方称号,然后奉告自己身份,说完内容终究再说over,表明一次呼叫完毕。
是的,没错,这便是本节要讲的在串口通讯中发挥重要效果的起止式协议!
UART的时序自身便是起止式协议,详细可参阅《嵌入式硬件通讯接口协议-UART(一)协议根底》这一篇内容的介绍。
事实上串口完结了数据通讯进程中的传输层,而应用层就体系功用的事务逻辑,应用层操控需求收发的各种数据内容。
数据解析的条件是通讯两边都是用一致的数据帧格局,因而在这儿将规划一个简略的起止式的数据帧格局,确保设备之间进行牢靠的通讯。
现在的许多无线模块,为了运用简略和易于集成,模块对外运用UART接口,并选用AT指令来完结装备和运用,常见的有ESP8266的WiFi模块、HC-05蓝牙串口模块。
AT指令的特色是易于人机交互,运用者对其发AT指令时,都是用ASCII字符发送,关于模块的处理,也是以字符来处理。这样的AT指令,它的起止式特色是以“AT”两个字符最初,并以回车换行“”字符完毕。

HC-05蓝牙模块指令示例
可是项目工程中,数据在嵌入式设备是以HEX数据(16进制)运算和处理,假如参阅AT指令去规划帧结构,那么在收发处理时分,必定要将收到的纯数据(16进制)依照字符处理。
比方一个终端设备,其功用便是环境检测,或许包括温湿度、光照强度、二氧化碳浓度、PM2.5浓度等等,假如要宣布一个温度收集成果24℃数据,收集设备将数据24分红2个字节发送,由于ASCII字符’2’对应的16进制是0x32、ASCII字符’4’对应的16进制是0x34,这样的一个温度数据就需求2个字节来发送。接纳端接纳到的是0x32、0x34后,再以查表办法逆向换算出原温度数据’24’,这个进程便是选用字符处理的费事之一。
因而不考虑运用ASCII字符来组帧结构。
精简起止式结构
最简略的帧,便是有最初+结束做起止标志。
比方0x55 + 数据包 + 0xAA。
在一长串的数据流中,接纳端逐字节接纳,并判别是否存在0x55,假如存在则开端存入数据包缓冲器,直到接纳了0xAA数据,以为完结一帧数据的接纳。
这个办法的确适当简略,不必太多的处理,只需求判别最初和结束即可。
而这样存在很大的问题,假如传输的内容也有0xAA这样的数据,这个0xAA并非结束标志,而程序接纳进程就提前完毕,这样就不能确保完好接纳一帧数据包了。
添加长度束缚
在精简起止式结构根底上,添加一数据来标志数据包长度。
比方0x55 + 长度 + 数据包 + 0xAA。
这样一来,接纳端判别接纳到了0x55的最初标志,紧接着再接纳一个“长度”的字节,依据这个长度来持续接纳后续剩下的数据。
可见假如有了长度的束缚,那么终究都不需求0xAA作为结束标志了。
这样的接口,即便有最初、长度、结束,还存在危险。比方传输数据时,物理线路遭到不知道搅扰,导致数据内容呈现了反常,那么接纳端即便完好接纳一切数量的数据下来,也是过错的内容。
添加校验查看
处理在发送进程中呈现的不知道过错问题,必定需求对数据进行校验。再添加一字段来标志数据内容的校验核算成果。
比方0x55 + 长度 + 校验值 + 数据内容 + 0xAA。
校验值是对数据包选用算法核算而得,接纳方完好收下一切数量的数据,再对数据包选用相同的算法核算出校验值,然后比照校验值来确认数据包的准确性。
关于校验值的运算,选用CRC-16运算的办法,检错能力强,开支小。
规划协议帧结构
综上所述,依据起止式的帧结构可以规划成:0x55 + 长度 + CRC校验 + 数据包。
在这儿,帧头标志选用0x55一个字节。
0x55二进制是01010101,这样在UART物理线路上输出的信号将会是占空比50%的方波,方波是最简单进行丈量和确诊的,在实践波形观测时可以确认稳定性、噪声毛刺等。
要说0xAA(二进制10101010)也是可以,可是UART发送时分是有一个开端位0,而且是以LSB办法先发送bit0的最低位,0xAA的bit0已经是0,而0x55的bit0是1,因而想得到方波当然优先考虑用0x55。
长度选用一个字节表明,则后续的CRC校验 + 数据包的总数量最多能放255个字节。
CRC校验选用CRC-16算法,占2个字节,此刻后续的数据包最多能放253个字节。
终上所述,得出终究的起止式帧结构:


接下来开端规划处理程序。
依据帧结构,可以界说如下的结构体:
typedef struct{
uint8_t head;
uint8_t len;
uint8_t crc16L;
uint8_t crc16H;
uint8_t packet[253];
}sst_frame_t;
其间要特别阐明的:
packet数据包最大长度设为253,是由于len是uint8_t类型,len最大255,而CRC校验值占了2个字节,因而packet数据包最多可253个字节。
CRC校验值选用的是CRC-16规范,校验值是个uint16_t类型的数据,传输时选用的是LSB形式,因而将CRC校验值设为两个uint8_t类型的数据,这样做便于在源码移植进程中,不同渠道的巨细端差异可以得到正确处理。
简述嵌入式设备内存巨细端差异在结构体界说以及运用时存在的问题:
假如对帧结构界说了如下的结构体:
typedef struct{
uint8_t head;
uint8_t len;
uint16_t crc16;
uint8_t packet[253];
}sst_frame_t;
核算后得到某一次的校验值成果是 0xDC66,这是一个uint16_t类型的数据,假如直接运用这个结构体来处理数据发送,那么:
在LSB的小端形式渠道下,数据的发送次序是
head、len、0x66、0xDC、packet[0]、packet[1]、…
反之在MSB大端形式的渠道里,数据的发送次序是
head、len、0xDC、0x66、packet[0]、packet[1]、…
因而选用2个字节uint8_t数据类型替代uint16_t来界说结构体中的CRC校验值,使得在跨渠道收发数据时无需做差异化处理。

构建帧结构
运用起止式进行数据传输时,把应用层的数据包进行组帧,这样可结构一个完好的数据帧,便于在应用层将完好的一帧数据传递给传输层宣布。
这儿的结构进程,事实上是对帧结构的“填充”进程。
首先是核算数据包的CRC校验值,随后便是“填充”的进程。
为了避免应用层调用接口时,传进来的数据包的地址、组帧成果的首地址指向同一个内存地址,所以在组帧前需求将源数据内容独自缓存,再进行“填充”的操作。

解析帧结构
解析帧结构其实便是对一长串的数据流进行解析处理,然后提取出数据包。
这儿被解析的数据来历是一个循环缓冲区,对循环缓冲区内的可读数据进行解析。因而需求运用循环缓冲区合作。
代码截图:
