在嵌入式产品开发中,我们经常会遇到两个设备之间的通信、设备与服务器的通信、设备和上位机的通信等,很多时候通信协议都是自定义的,所以这就涉及到自定义协议的解析和组包问题。
比如针对下面的这样一个协议:
帧头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
下面来看看如何解析:
若干年前,在第一次面对这种问题时,用的如下傻瓜式的代码方式实现:
#includeintmain() { unsigned char Rxbuf[9]={0x55,0xAA,0x01,0x00,0x33,0x33,0x53,0x40,0xED}; short DeviceId; floatVoltage; 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; floatvoltage; unsigned char check; }; intmain() { 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其中convert.h如下:#include"convert.h" intmain() { unsigned char Rxbuf[9]={0x55,0xAA,0x01,0x00,0x33,0x33,0x53,0x40,0xED}; short DeviceId; floatVoltage; 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; }
#ifndef CONVERT_H #define CONVERT_H voidShortToByte(unsigned char*dest,int*index,short value); voidFloatToByte(char*dest,int*index,floatvalue); #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_H voidByteToShort(const unsigned char*source,int*index,short*result); voidByteToInt(unsigned char*source,int*index,int*result); voidByteToLong(char*source,int*index,long long*result); voidByteToFloat(unsigned char*source,int*index,float*result); voidByteToDouble(unsigned char*source,int*index,double*result); voidByteToString(unsigned char*source,int*index,char*result,int length); voidShortToByte(unsigned char*dest,int*index,short value); voidIntToByte(char*dest,int*index,int value); voidLongToByte(char*dest,int*index,long long value); voidFloatToByte(char*dest,int*index,floatvalue); voidDoubleToByte(unsigned char*dest,int*index,double value); voidStringToByte(char*dest,int*index,int length,char*value); #endif//CONVERT_H
组包的过程和解析的过程正好相反,这里不再赘述。你在开发中遇到这种问题时,又是如何处理的呢?欢迎留言讨论!
责任编辑:彭菁
-
嵌入式
+关注
关注
5091文章
19176浏览量
307259 -
通信协议
+关注
关注
28文章
911浏览量
40412 -
数据
+关注
关注
8文章
7134浏览量
89478 -
服务器
+关注
关注
12文章
9295浏览量
85962 -
嵌入式开发
+关注
关注
18文章
1035浏览量
47677
原文标题:嵌入式开发中,自定义协议的解析与组包
文章出处:【微信号:玩点嵌入式,微信公众号:玩点嵌入式】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论