想不到第一次接触串口就要去写一个自定义的灵活性串口通信协议,所以在这里记录一下自己的心得,有什么不合理的地方还请大佬们指出。
先说明这里提到的灵活性其实也是相对来说的,并不是说明数据包都可以定义。
一、用户层串口通信协议
简单的来说用户层的串口通信协议就是如何定义一个数据包格式,发送端按照规定的数据包格式发送出去,接收端按照规定的数据包格式解析出正确的数据。
那为什么又要在用户层定义呢,其一是因为底层的通信协议不是随随便便就可以写出来的,二是因为我们在市面上买到的串口模块本身就已经有了底层通信协议,我们又何必去再自己写呢。
二、定义数据包格式
因为是第一次接触串口,所以肯定先上网搜了搜常见的数据包模板,发现基本上数据包格式有以下元素:
帧头 包序号 数据长度 数据 校验 帧尾
领导并不需要数据包定义的复杂,所以决定就从以上的元素来构思我自己的数据包格式。
首先,既然提到了灵活性,那么上面的部分元素可以存在或者不存在,存在的话又可以更换位置,之所以说部分元素是因为考虑以下几点:
1.帧头位置固定存在,这个不需要解释;
2.数据长度存在则必须放到数据的前面,这样更方便我们解析不定长数据;
3.校验必须存在且放在数据后面,因为校验本身就是为了检查数据的正确性,放到前面也不便于我们解析;
通过以上几点对各种形式的数据包排除后还剩18种,也就是说我定义的灵活性串口通信协议一共支持18种数据包(有无帧尾对半分)。
帧头 | 序号 | 数据长度 | 数据 | 校验 | 帧尾 |
帧头 | 数据长度 | 序号 | 数据 | 校验 | 帧尾 |
帧头 | 数据长度 | 数据 | 序号 | 校验 | 帧尾 |
帧头 | 数据长度 | 数据 | 校验 | 序号 | 帧尾 |
帧头 | 序号 | 数据 | 校验 | 帧尾 | |
帧头 | 数据 | 序号 | 校验 | 帧尾 | |
帧头 | 数据 | 校验 | 序号 | 帧尾 | |
帧头 | 数据长度 | 数据 | 校验 | 帧尾 | |
帧头 | 数据 | 校验 | 帧尾 |
帧头:必须存在,长度不限,但不建议太长;
序号:可定义1-4字节,若定义1字节,序号从1-255循环;
数据长度:可定义1-4字节;
数据:不定长,和数据长度匹配;
校验:1字节,暂时只考虑累加计数,用unsigned char保存;
帧尾:可有可无,长度不限;
三、注意事项:
1.定义的数据包发送的数据只能是字符'0'-'9','a'-'z','A'-'Z';而帧头和帧尾的字符肯定和数据字符不同,所以不考虑数据中出现帧头或帧尾的情况;
2.因为是系统与系统之间串口通信,所以需要考虑大小端问题,我自己定义发送为大端模式,接收端需要判断自身系统的模式来获取数据;
3.发送和接收的频率,因为工作要求,我自考虑了发送频率比接收频率快的情况(范围有限),并且当发送和接收的频率不同时,数据包的序号不可以让程序来自动改变;
4.还有一些代码上的特殊注意,不要用strlen来获取数据包的长度,因为会存在' '字符,比如序号大小为3字节,但序号为12,这其余两个字节为0,0就是' ';存放数据的变量类型一定要认真选择,如序号大小是0-4字节不固定,所以我们需要一个字节一个字节的进行存放和解析,那么会有情况是字节大小为3,序号为1023,如果用char一个一个去获取的话,你懂得;还有一些其他细节问题就不一一说明了。
四、API
建议将代码封装成库,然后提供几个接口供用户使用即可,这样也方便用户切换其他串口通信协议。
我自己是提供了三个接口:
int GetPacketSize(stPacketFormat stPacket,int bufLen);
参数 : stPacket 是从xml解析出来的数据包格式,bufLen为要发送的数据长度。
返回值: 组装成的数据包大小;
int CreatePacket(stPacketFormat stPacket,char **dataBuf,int bufLen);
参数 : stPacket 是从xml解析出来的数据包格式,*dataBuf为根据GetPacketSize的返回值创建的数组,用来存放组装成的数据包,bufLen为发送的数据长度。
返回值: 成功为0,非0失败;
int ParsePacket(stPacketFormat stPacket,char **dataBuf,int *pDataLen);
参数 : stPacket 是从xml解析出来的数据包格式,*dataBuf传进去从串口获取到的数据,函数调用成功后,解析出来的数据也从*dataBuf获取,*pDataLen传进去*dataBuf的长度,函数调用成功后,返回实际数据*dataBuf的长度。
四、收发数据包
1.先说配置数据包格式,可以采用xml文件来进行配置,程序通过解析xml来设置数据包格式;
2.发送端调用GetPacketSize获取数据包大小,创建数组将发送的数据传给CreatePacket封装成数据包;
3.发送端将数据包通过串口发送出去;
4.接收端从串口接收数据,然后将数据传给ParsePacket进行解析,获取到实际需要的数据。
思路就记录到这,具体实现的代码其实不重要。