新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > 嵌入式硬件通信接口协议-UART(四)设计起止式的应用层协议

嵌入式硬件通信接口协议-UART(四)设计起止式的应用层协议

作者:时间:2019-03-20来源:网络收藏

  串口实现了两个终端设备之间进行可靠的通信,串口在这中间完成了传输层的作用。本次要讲的是关于数据的协议。

本文引用地址:http://www.eepw.com.cn/article/201903/398678.htm

  类似场景

  洞幺!洞幺!我是洞拐!收到请回答!收到请回答!over!

  在战争题材影视剧中经常能够看到这样的对白,在通过对讲机等相关无线设备呼叫队友时,先呼叫对方名称,然后告知自己身份,说完内容最后再说over,表示一次呼叫结束。

  是的,没错,这就是本节要讲的在串口通信中发挥重要作用的起止式协议!

  的时序本身就是起止式协议,具体可参考《嵌入式接口协议-(一)协议基础》这一篇内容的介绍。

  事实上串口实现了数据通信过程中的传输层,而应用层就系统功能的业务逻辑,应用层控制需要收发的各种数据内容。

  数据解析的前提是通信双方都是用统一的数据帧格式,因此在这里将设计一个简单的起止式的数据帧格式,保证设备之间进行可靠的通信。

  现在的很多无线模块,为了使用简单和易于集成,模块对外使用接口,并采用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校验值,随后就是“填充”的过程。

  为了防止应用层调用接口时,传进来的数据包的地址、组帧结果的首地址指向同一个内存地址,所以在组帧前需要将源数据内容单独缓存,再进行“填充”的操作。

  解析帧结构

  解析帧结构其实就是对一长串的数据流进行解析处理,从而提取出数据包。

  这里被解析的数据来源是一个循环缓冲区,对循环缓冲区内的可读数据进行解析。因此需要使用循环缓冲区配合。

  代码截图:


上一页 1 2 下一页

关键词: UART 硬件通信

评论


相关推荐

技术专区

关闭