在嵌入式产品开发中,我们经常会遇到两个设备之间的通信、设备与服务器的通信、设备和上位机的通信等,很多时候通信协议都是自定义的,所以这就涉及到自定义协议的解析和组包问题。
比如针对下面的这样一个协议:
帧头1 帧头2 字段1 字段2 校验
固定值:0x55 固定值:0xaa 设备id 电压值 前面所有数据异或值
char char short float char
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
下面来看看如何解析:
若干年前,在第一次面对这种问题时,用的如下傻瓜式的代码方式实现:
#include int main(){ unsigned char rxbuf[9] = {0x55,0xaa,0x01,0x00,0x33,0x33,0x53,0x40,0xed}; short deviceid; float voltage; unsigned char check = 0; int i; 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,deviceid); printf(voltage:%f,voltage); } return 0;}
简单来说就是硬来,按照数组的先后顺序逐个重组解析,如果协议比较长,代码里会充斥着很多的数组下标,一不小心就数错了。而且如果更改协议的话,代码要改动很多地方。
后来有人告诉我可以定义个结构体,然后使用memcpy函数直接复制过去就完事了。
#include #include #pragma pack(1)struct rxframe{ unsigned char header1; unsigned char header2; short deviceid; float voltage; unsigned char check;};int main(){ unsigned char rxbuf[9] = {0x55,0xaa,0x01,0x00,0x33,0x33,0x53,0x40,0xed}; struct rxframe rxdata; unsigned char check = 0; int i; 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,rxdata.deviceid); printf(voltage:%f,rxdata.voltage); } return 0;}嗯,的确是方便了很多。不过,该方式仅适合小端传输方式。
再后来,又见到有人用如下代码实现:
#include #include convert.hint main(){ unsigned char rxbuf[9] = {0x55,0xaa,0x01,0x00,0x33,0x33,0x53,0x40,0xed}; short deviceid; float voltage; unsigned char check = 0; int i; int index = 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,deviceid); printf(voltage:%f,voltage); } return 0;}其中convert.h如下:
#ifndef convert_h#define convert_hvoid shorttobyte(unsigned char* dest, int* index, short value);void floattobyte(char* dest, int* index, float value);#endif // convert_hconvert.c如下:#include convert.h#include #include static bool endianflag = 0;void bytetoshort(const unsigned char* source, int* index, short* result){ int i, len = sizeof(short); char p[len]; memset(p, 0, len); if(endianflag == 1 ) { for( i = 0; i < len; i++ ) *(p+i) = *(source + *index + len - i - 1); } else { for( i = 0; i < len; i++ ) *(p+i) = *(source + *index + i); } *result = *((short*)p); *index += len;}void bytetofloat(unsigned char* source, int* index, float* result){ int i, len = sizeof(float); char p[len]; memset(p, 0, len); if(endianflag == 1 ) { for( i = 0; i < len; i++ ) *(p+i) = *(source + *index + len - i - 1); } else { for( i = 0; i < len; i++ ) *(p+i) = *(source + *index + i); } *result = *((float*)p); *index += len;}该方法既可以支持小端模式,也可以支持大端模式,使用起来也是比较方便。
除了上述2个函数,完整的转换包含以下函数,就是将bytes转换为不同的数据类型,以及将不同的数据类型转换为bytes。
#ifndef convert_h#define convert_hvoid bytetoshort(const unsigned char* source, int* index, short* result);void bytetoint(unsigned char* source, int* index, int* result);void bytetolong(char* source, int* index, long long* result);void bytetofloat(unsigned char* source, int* index, float* result);void bytetodouble(unsigned char* source, int* index, double* result);void bytetostring(unsigned char* source, int* index, char* result, int length);void shorttobyte(unsigned char* dest, int* index, short value);void inttobyte(char* dest, int* index, int value);void longtobyte(char* dest, int* index, long long value);void floattobyte(char* dest, int* index, float value);void doubletobyte(unsigned char* dest, int* index, double value);void stringtobyte(char* dest, int* index, int length, char* value);#endif // convert_h
组包的过程和解析的过程正好相反,这里不再赘述。你在开发中遇到这种问题时,又是如何处理的呢?欢迎留言讨论!
第三代半导体SiC模块厂商中科意创完成数千万元A+轮融资
设备维修的检查方法和操作实践
无人驾驶汽车不再遥远?控制权之争愈发激烈
智融22.5W移动电源双向快充芯片:SW6201
感应电机是什么意思啊_感应电机的工作原理
嵌入式开发中组包的过程和解析的过程一样吗
Moxa推出全新的双RF无线技术,为铁路带来更顺畅的通讯
单片机C51编程规范
我国重复使用火箭研制目标确定
电源的可靠性怎样提高
认识发烧音响器材
SIP网络音频模块SIP2101V/SIP2103V系列说明
俞敏洪:人工智能与教育结结合是未来一大趋势
大众福特联盟谈判终局 共创自动驾驶合资企业
铝合金压铸工艺简介 压铸过程主要工艺参数
智能环境监测系统的结构组成及场景应用案例
关于存储器的工作原理以及市场趋势分析
小米6价格曝光,将再一次带给我们惊喜
智能机器人会改变人类的生产和生活方式吗
2999元!OPPO R11拆解:居然具备生活防水?(组图)