嵌入式开发中,自定义协议的解析与组包

2023-02-07 13:25:26    来源 : 面包芯语
在嵌入式产品开发中,我们经常会遇到两个设备之间的通信、设备与服务器的通信、设备和上位机的通信等,很多时候通信协议都是自定义的,所以这就涉及到自定义协议的解析和组包问题。

比如针对下面的这样一个协议:


(资料图片)

帧头1帧头2字段1字段2校验
固定值:0x55固定值:0xAA设备ID电压值前面所有数据异或值
charcharshortfloatchar
1字节1字节2字节4字节1字节

数据在发送时涉及到一个大小端的概念,大小端是针对多字节数据的传输,比如上述协议中字段1,假设两字节内容为0x0001,先发送0x01后发送0x00,称为小端模式;先发送0x00后发送0x01,称为大端模式。

假设字段1内容为0x001,字段2内容为0x40533333(对应为3.3)

假设按照小端方式发送,下面是帧数据:

55 AA 01 00 33 33 53 40 ED

下面来看看如何解析:

若干年前,在第一次面对这种问题时,用的如下傻瓜式的代码方式实现:
#includeintmain(){unsignedcharRxbuf[9]={0x55,0xAA,0x01,0x00,0x33,0x33,0x53,0x40,0xED};shortDeviceId;floatVoltage;unsignedcharcheck=0;inti;for(i=0;i<8;i++){check^=Rxbuf[i];}if(Rxbuf[0]==0x55&&Rxbuf[1]==0xAA&&Rxbuf[8]==check){DeviceId=(Rxbuf[3]<<8)|Rxbuf[2];Voltage=*((float*)&Rxbuf[4]);printf("DeviceId:%d\n",DeviceId);printf("Voltage:%f\n",Voltage);}return0;}

简单来说就是硬来,按照数组的先后顺序逐个重组解析,如果协议比较长,代码里会充斥着很多的数组下标,一不小心就数错了。而且如果更改协议的话,代码要改动很多地方。

后来有人告诉我可以定义个结构体,然后使用memcpy函数直接复制过去就完事了。

#include#include#pragmapack(1)structRxFrame{unsignedcharheader1;unsignedcharheader2;shortdeviceId;floatvoltage;unsignedcharcheck;};intmain(){unsignedcharRxbuf[9]={0x55,0xAA,0x01,0x00,0x33,0x33,0x53,0x40,0xED};structRxFrameRxData;unsignedcharcheck=0;inti;for(i=0;i<8;i++){check^=Rxbuf[i];}memcpy(&RxData,Rxbuf,sizeof(Rxbuf));if(Rxbuf[0]==0x55&&Rxbuf[1]==0xAA&&RxData.check==check){printf("DeviceId:%d\n",RxData.deviceId);printf("Voltage:%f\n",RxData.voltage);}return0;}
嗯,的确是方便了很多。不过,该方式仅适合小端传输方式。

再后来,又见到有人用如下代码实现:

#include#include"convert.h"intmain(){unsignedcharRxbuf[9]={0x55,0xAA,0x01,0x00,0x33,0x33,0x53,0x40,0xED};shortDeviceId;floatVoltage;unsignedcharcheck=0;inti;intindex=0;for(i=0;i<8;i++){check^=Rxbuf[i];}if(Rxbuf[0]==0x55&&Rxbuf[1]==0xAA&&Rxbuf[8]==check){index+=2;ByteToShort(Rxbuf,&index,&DeviceId);ByteToFloat(Rxbuf,&index,&Voltage);printf("DeviceId:%d\n",DeviceId);printf("Voltage:%f\n",Voltage);}return0;}
其中convert.h如下:
#ifndefCONVERT_H#defineCONVERT_HvoidShortToByte(unsignedchar*dest,int*index,shortvalue);voidFloatToByte(char*dest,int*index,floatvalue);#endif//CONVERT_H
convert.c如下:
#include"convert.h"#include#includestaticboolEndianflag=0;voidByteToShort(constunsignedchar*source,int*index,short*result){inti,len=sizeof(short);charp[len];memset(p,0,len);if(Endianflag==1){for(i=0;i
该方法既可以支持小端模式,也可以支持大端模式,使用起来也是比较方便。

除了上述2个函数,完整的转换包含以下函数,就是将Bytes转换为不同的数据类型,以及将不同的数据类型转换为Bytes。

#ifndefCONVERT_H#defineCONVERT_HvoidByteToShort(constunsignedchar*source,int*index,short*result);voidByteToInt(unsignedchar*source,int*index,int*result);voidByteToLong(char*source,int*index,longlong*result);voidByteToFloat(unsignedchar*source,int*index,float*result);voidByteToDouble(unsignedchar*source,int*index,double*result);voidByteToString(unsignedchar*source,int*index,char*result,intlength);voidShortToByte(unsignedchar*dest,int*index,shortvalue);voidIntToByte(char*dest,int*index,intvalue);voidLongToByte(char*dest,int*index,longlongvalue);voidFloatToByte(char*dest,int*index,floatvalue);voidDoubleToByte(unsignedchar*dest,int*index,doublevalue);voidStringToByte(char*dest,int*index,intlength,char*value);#endif//CONVERT_H
组包的过程和解析的过程正好相反,这里不再赘述。你在开发中遇到这种问题时,又是如何处理的呢?欢迎留言讨论!

END

来源:TopSemic嵌入式

版权归原作者所有,如有侵权,请联系删除。▍

标签: 数据类型 比较方便 服务器的

相关推荐

x 广告

如有意见请与我们联系 邮箱:8 97 180 9 @qq.com

豫ICP备2021032478号-31

Copyright ©  2015-2022 元宇宙版权所有