一种基于SLIP 协议的串行数据解析方法
2019-07-22李逸楠孙丽君所玉君
李逸楠,孙丽君,王 欣,所玉君
(1.天津津航计算技术研究所,天津 300308;2.天津市航空电子综合显示控制重点实验室,天津 300308;3.天津航海技术研究所,天津 300131)
1 背景
在嵌入式系统中,分系统间最常用的数据传输方式是采用各种串口进行通讯,比如RS232/RS422/RS485等[1-3]。抛开底层硬件设计的不同,通常在链路层通过一些协议来保证发送方与接收方数据的一致性。
SLIP协议是一种常用的链路层通讯协议,规定了帧头、帧尾、在帧头帧尾之间如果出现帧头或帧尾字符,则按照一定的规则进行替换,保证发送方在帧头帧尾间不会出现帧头帧尾对应的字符[4];接收方收到数据后,也需要按照帧头帧尾把数据解析出来,再按照替换规则把有效数据还原。这时得到的数据就可以按照上层协议进一步解析,得到有意义的数据。不同的项目有具体的上层协议,解析的方法不尽相同,但基本思路是一致的。本文涉及到的协议及硬件可返回的数据如下。
1.1 SLIP协议
以0xc0为帧头,0xc0为帧尾,帧头帧尾间如果出现0xc0,则以连续两个字符0xdb和0xdc取代;如果帧头帧尾间出现0xdb,那么就用连续两个字符0xdb和0xdd替代;接收方也按照这个规则还原接收到的数据。
1.2 上层协议
经SLIP协议解析后的数据为一个完整的数据帧,格式如表1所示。
表1 消息帧格式
1.3 可从硬件获取的信息
UINT16 ReciveCοunt:自硬件上电以来收取到的所有字符数,数据类型为UINT16,无符号的16位整型。
UINT8 DataRev[2048]:数据接收缓存,硬件接收到数据之后会按顺序记录在缓存中,最多可存放2 048个字节,当超过2 048个字节,新数据从头开始存放。
2 需求分析
根据背景说明,可以分析出数据解析具体需求有以下几点:①需要根据硬件提供的信息计算出每周期接收到的新数据个数,并从数据缓存中取出新数据;②需要按照SLIP协议解析出原始数据,并将接收方回复的数据按照SLIP协议处理后发送;③如果一个周期内未接收到一个完整的数据帧,需要将不完整的数据帧存储起来,待下一个周期接收到数据之后,进行数据拼接,保证数据的完整性;④需要将解析出的原始数据整理为上述数据帧格式。一个周期内如果有多条数据帧,则都需要保存起来。
3 模块设计
按照上述需求分析,采用模块化的设计思想设计出以下几个软件模块。数据解析过程的总流程如图1所示。
3.1 不完整数据保存模块Save422Data()
该模块用来判断一个周期内收取到的数据中是否存在不完整的数据帧,如果存在,则给出需要保存的数据个数,并将需保存的数据存储在全局数组中。算法的关键是如何判断是否存在不完整的数据帧。本文采用的方法如下:判断接收到的数据中最后一个c0是否就是本次接收到的最后一个数据,如果是,说明本次接收的是一帧完整的数据;如果最后一个c0不是最后一个数据,说明本次接收到了不完整的数据,需要将最后一个c0之后的数据保存起来。
需要特别注意的是,主要有以下两种情况需要单独处理:①本周期内就只收到了一个数据,而这个数据恰好就是c0。如果按照之前的条件判断,不会把这单独的c0认为是一个不完整的数据帧,就不会保存这个c0,导致下一周期收取的数据没有帧头或帧尾,解析数据就会出错。正确的做法是遇到仅有一个c0的情况,要把这个c0保存起来,在下一周期拼接在收到数据之前。②收到数据不止一个,且最后一个c0确实也是本周期收到数据的最后一个,但这个c0之前还是个c0。如果按照最开始的条件判断,会误认为这是一帧完整的数据,而事实上,连着的两个c0前者是上一帧数据的结尾,后者是下一帧数据的开头。正确的做法是当遇到最后一个c0就是最后一个数据的情况,还需要额外判断这个c0之前是否是c0。如果不是,则说明确实是完整的数据,无需保存数据;如果是,则说明恰好收到了下一帧数据的开头,需要把这个c0保存起来,在下一周期拼接在收到数据之前。经过以上分情况处理,就可以保证不会漏掉一帧不完整的数据。程序控制流如图2所示。
图1 数据解析过程
3.2 底层收数模块px_422recive()
该模块负责判断何时有新数据传来,并从硬件缓存中取出新数据。基本思路是设计两个全局变量,一个用来存放当前周期的ReciveCοunt值(num),一个用来存放上一个周期的ReciveCοunt值(numοld)。如果当前值等于上周期值,则表明无新数据传来,如果当前值大于上周期值,则表明有新数据,需要从硬件内存中取出数据。新数据为从DataRev[numοld]开始到 DataRev[numοld+(num-numοld-1)]的数据。上述规律在ReciveCοunt小于2 048个数据时成立;当ReciveCοunt大于2 048个数据时,新数据将保存在缓存区的开头,这时情况稍显复杂。因为物理内存只有2 048 Byte,首先应将ReciveCοunt值对2 048取余,这样就将ReciveCοunt换为2 048以内的数值。则一旦数据超过2 048从头开始时,前述取数规律又可以使用。需要分情况讨论的就只剩上一周期数据(numοld)小于2 048,而当前周期数据大于2 048的情况了。由于取余处理,当前周期的ReciveCοunt实际值为余数(num),如果一个周期数据量不超过2 048个,则一定有num小于numοld。此条件可作为特殊情况的判断条件,这时新数据应该是内存尾部DataRev[numοld]开始到DataRev[numοld+(2048-numοld-1)]的数据与内存头部DataRev[0]开始到DataRev[num-1]的数据拼接而成。程序控制流程如图3所示。
图2 不完整数据保存模块控制流程图
3.3 链表数据插入模块ListInsert()
在SLIP协议解析过程中,需要根据规则,在数据中指定位置插入特定的数据。例如“如果帧头帧尾间出现0xdb,那么就用连续两个字符0xdb和0xdd替代”这一条规则其实就是在0xdb之后插入一个0xdd。为满足这个需求,设计了一种链表类型结构体,结构体设计如下:
typedef struct
{
UINT8RevBuf[MAXSIZE];
int charnum;
}SqList;
该结构体由一个字符数组和一个整型变量组成,字符数组用来存放消息数据,整型变量用来存放消息包含的字符个数,MAXSIZE为数据最大长度,由实际传输数据最大长度确定。
链表数据插入模块的操作对象就是这种链表数据,输入一个待操作的链表,待插入的数据,以及插入的位置,链表数据插入模块输出插入数据之后的链表,并自动更新数据长度。程序控制流如图4所示。
图3 底层收数模块程序流程
3.4 链表数据删除模块ListDelete()
在SLIP协议解析过程中,需要根据规则,删除数据中指定位置的数据。例如“帧头帧尾间如果出现0xc0,则以连续两个字符0xdb和0xdc取代”这条规则需要删掉不是帧头帧尾的0xc0,然后利用上一小节设计的链表数据插入模块在删掉的位置依次插入0xdc,0xdb。删除指定位置数据之后,其后数据依次向前移动一位,并让数据长度减1。程序控制流如图5所示。
3.5 协议解码模块Decode422data()
协议解码模块的输入为两个,一是待解码的数据长度,二是待解码的数据缓存区;输出为消息帧结构体数组,返回值为解析出的消息个数。简单介绍输出的消息帧结构体为:
typedef struct
{
UINT16 DataID;
UINT16 DataLen;
UINT8 DataItem[80];
UINT16 CheckSum;
}MessageRev;
该结构体与表1所述数据帧结构一致,DataID代表数据ID,DataLen代表数据长度,DataItem[80]代表有物理意义的数据内容(假设数据内容不会超过80个字符),CheckSum表示校验和。假设一个周期内传来的消息不会超过10帧,则需创建一个可存储10帧消息的结构体数组MessageRev Message[10]。
图4 链表数据插入模块
图5 链表数据删除模块
解码主要分为三个步骤:①从待解码的数据缓存区中把两个c0之间的数据取出,存放在上述链表结构中,同时记录好数据长度,并记录下解析出的消息帧条数;②如果解析出消息帧条数大于0,就按照SLIP协议的规则,把每一条消息帧中该删除的删掉,该插入的插入,得到解析后的数据帧;③从解析好的数据中提取出MessageRev结构体所需的各变量,按顺序存放在Message[10]结构体数组中,有几帧消息就占用数组中的前几个;返回消息帧条数,这样就把原始数据解析为可以直接用来判断处理的上层协议帧格式。
程序流程如图6所示。
图6 协议解码模块
3.6 协议编码模块Incode422data()
待发送数据需要根据SLIP协议要求进行编码,调用ListDelete模块将需要替换的字符删除,调用ListInsert模块插入规定的字符。全部替换完毕后,调用ListInsert模块在待发送数据开始和结尾分别插入0xc0,完成数据的SLIP协议编码。具体流程如图7所示。
图7 协议编码模块
4 实验及结果
首先测试底层接收数据功能是否正确,测试设备通过232串口向本设备发送数据,波特率460.8 Kbps,测试设备向本设备发送1 247 030个字节,在本设备读取记录的接收字节数也是1 247 030,说明基本的接收数据功能正常。
测试设备接着向本设备发送不同类型的数据,包括周期性数据与事件性数据,不同消息帧发送与接收到的帧计数如表2所示。
表2 消息帧解析实验结果
根据实验结果可以看到,周期性消息不同ID的数据帧接收到的个数是一致的,且发送与接收到的帧计数相同,说明周期性数据帧接收与解析功能正确。在周期性数据发送过程中中随机的发送事件性消息,可以看到发送帧计数与接收帧计数相同,说明随机的事件性数据帧也可以完整接收并解析出来。具体的帧内容,如果消息ID解析正确,则容易根据项目协议解析出来,不再赘述。
5 结论
本文给出了一种基于SLIP协议的串行数据解析方法,对解析过程中的各个模块进行了详细描述。特别描述了数据帧不完整情况下数据的存储与拼接方法。将各个模块集成为完整的数据解析软件并经过大量数据测试,结果表明数据接收无丢数现象,数据帧解析完整,不论周期性数据和事件性数据均无丢帧现象。本方法可适用于大多数串行通讯过程,具有适用面广、解析方法简单可靠之优点。