介绍
串口通信是单片机中最常见的通信方式之一,最近有个需求需要解析上位机发来的数据,简单记录一下解析的思路跟过程。
串口协议如下
通讯协议中各参数说明:
1. Head(包头):
固定占一个字节、固定为 0X1E(回传的 Head 与发送的 Head 相同);
2. PktLenH(包长度高 8 位):
固定占一个字节、包长度高 8 位,本文所述的包长度指整 个包的所有字节数(从第一位包头到最后一位校验位的所有字节个数);(若 DATA 为 空、那么包长度值为 8,若 DATA 为 10个数据、那么包长度值为 18) ;
3. PktLenL(包长度低 8 位):
固定占一个字节、包长度低 8 位;
4.Mod1:
固定占一个字节(回传的 Mod1 与发送的 Mod1 相同)、Mod1 的定义如下: 暂时固定为0X0B: 系统及功能
5. Mod2:
固定占一个字节(回传的 Mod2 与发送的 Mod2 相同)、固定为 0X00;
6. FLAG:
固定占一个字节、包状态主动被动标志位、FLAG 的定义如下: 0XFF: 主动标志,表示这条指令是上位机往模块发数据 (如果是上位机往模块发数据,这个 FLAG 值必须固定为 0XFF) 0X01: 被动标志,代表模块往上位机回传正确 0X05:
被动标志,代表上位机下发的包校验位计算出错或上位机发送了本模块不 支持的指令或其它非法错误 (如输入的数据格式不对等等)
(如果是模块往上位机回传数据、被动标志 FLAG 值可能为 0X01 或 0X05,0X01 代表 正确,0X05提示上位机下发的数据非法)
7. CMD:
命令字(回传的 CMD 与发送的 CMD 相同)、占一个字节; 详见本文件后续具体描述 (不同的命令有不同的 CMD)
8. DATA:
数据域,字节数不固定、可以是发送数据域或者回传数据域
a. 数据域中可以有数据也可以没有数据(具体会在每条 CMD 中详细说明)
b. 计算包的数据域的个数可通过“ (PktLenH<<8 + PktLenL) – 8 ”来计算
9. Chksum:
占一个字节、校验位为包的所有数据校验和(高八位溢出、取低八位即可 得到校验和),校验算法参考附录 1;
校验算法(累加和校验算法)
检验和算法参考程序
unsigned char GetCheckSum(unsigned char *pbuffer, unsigned short len)
{
unsigned char tmp = pbuffer;
unsigned char ucSum = 0;
while (len–)
{
ucSum += ((tmp++));
}
return (ucSum);
}
备注说明:
本通讯协议包中固定不变的参数为:
包头(1 个字节) + 包长度高 8 位(1 个字节) + 包长度低 8 位(1 个字节) + Mod1(1 个字节) + Mod2(1 个字节) + Flag(1 个字节) + Cmd(1 个字节) + Chksum (1 个字节) = 8 个字节;
解析思路
- 先判断帧头是否符合
- 再判断字节数目是否合理
- 判断校验
3.1 当校验通过了再判断固定字节跟标志位
3.2. 解析指令
3.3. 解析数据
代码记录
int8_t Parse_info(uint8_t RevBuf[])
{
uint16_t Buff_Size = 0;
uint8_t Temp[100] = {0};
uint8_t Check = 0;
uint16_t Data_Len = 0;
uint16_t i = 0;
if(RevBuf[0] == 0x1E)//帧头
{
Buff_Size = RevBuf[1]<<8 | RevBuf[2];
printf("收到的字节数是:%02X\r\n",Buff_Size);
if(Buff_Size>=8)
{
Data_Len = Buff_Size - 8;
printf("收到数据字节个数是:%d \r\n",Data_Len);
for(i=0;i<Buff_Size;i++)
{
Temp[i] = RevBuf[i];
printf("Temp[%02d] = %02X\r\n",i,Temp[i]);
}
printf("收到校验结果: %02X\r\n",Temp[Buff_Size-1]);
Check = GetCheckSum(Temp,Buff_Size-1);
printf("计算校验结果: %02X\r\n",Check);
if(Temp[Buff_Size-1] == Check )
{
printf("计算校验结果:通过\r\n");
if(Temp[3] == 0x0B)
{
printf("Mod1标志校验通过: %02X\r\n",Temp[3]);
}
else
{
return 0x05;//上位机数据非法
}
if(Temp[4] == 0x00)
{
printf("Mod2标志校验通过: %02X\r\n",Temp[4]);
}
else return 0x05;//上位机数据非法
if(Temp[5] == 0xFF)
{
printf("主动标志校验通过: %02X\r\n",Temp[5]);
}
else return 0x05;//上位机数据非法
switch(RevBuf[6])
{
case 0x01:
printf("上位机发的指令是: %02X\r\n",RevBuf[6]);
printf("执行打开某个GPIO功能\r\n");
break;
case 0x02:
printf("上位机发的指令是: %02X\r\n",RevBuf[6]);
printf("执行关闭某个GPIO功能\r\n");
break;
case 0x11:
printf("上位机发的指令是: %02X\r\n",RevBuf[6]);
printf("解析时间,回应心跳功能\r\n");
printf("上位机发的时间是:%02X%02X-%02X-%02X %02X:%02X:%02X",
Temp[7],Temp[8],Temp[9],Temp[10],Temp[11],Temp[12],Temp[13]);
break;
}
}
else
{
printf("校验和不通过\r\n");
return 0x05;//上位机数据非法
}
}
else
{
printf("字节数检查不通过\r\n");
return 0x05;//上位机数据非法
}
}
else
{
printf("帧头校验不通过\r\n");
return 0x05;//上位机数据非法
}
}