|
这段代码实现了一个基于状态机的串口数据包解析器,结构基本正确,但存在几个严重且典型的问题,在实际项目中可能导致数据错乱、死循环甚至程序崩溃。下面逐一分析。 1. 代码功能简述协议格式:帧头 0x5A + 长度(len) + 数据(len字节) + 校验和(累加和) 校验方式:累加和(包含帧头、长度、全部数据字节) 状态机:WAIT_HEADER → WAIT_LEN → WAIT_DATA → WAIT_CHECKSUM → 回到 WAIT_HEADER 成功解析后:发送 buf_data[0] 和 buf_data[1](假定长度至少为2)
2. 存在的关键问题① 主循环与中断完全无同步 —— 数据竞争 / 重复解析 / 丢字节if(RI){ RI=0; tempsbuf=SBUF;}// main loopif(parser_input_byte(&p,tempsbuf)){ ...}
tempsbuf 在中断中被写入,主循环随时读取,没有任何互斥或标志位。 主循环运行极快,会在同一个字节上反复调用 parser_input_byte,导致状态机被多次喂入相同数据,完全打乱解析逻辑。 同时,当新字节到来时,tempsbuf 可能正在被主循环读取,造成数据竞争。
正确做法:使用 volatile 标志位,中断置位,主循环检测后清除并处理。 volatile bit hasNewByte = 0;// 中断中:tempsbuf = SBUF;hasNewByte = 1;// 主循环:if(hasNewByte) { hasNewByte = 0; if(parser_input_byte(&p, tempsbuf)) { ... }}
② 数据缓冲区 buf_data[3] 没有长度保护,极易溢出unsigned char buf_data[3;...p->buf_data[p->index++ = byte;if(p->index >= p->len) { p->state = WAIT_CHECKSUM; }
case WAIT_LEN: p->len = byte; if(p->len > sizeof(p->buf_data)) { p->state = WAIT_HEADER; // 长度非法,复位 break; } ...
③ 同步恢复能力极差,错误字节极易造成永久失步④ parser_init(&p) 位置错误 —— C 语言不允许在文件作用域执行函数Parser p;parser_init(&p); // ← 这行不能在全局作用域调用void main() { ... }
这种写法在某些编译器下可能被当作声明甚至报错。必须移到 main 内部: void main() { parser_init(&p); while(1) { ... }}
⑤ 校验和在失败后未清除,且校验包含帧头本身p->checksum 初始为 0,在收到 0x5A 时置为 0x5A,然后累加长度和数据。最后与收到的校验字节比较。 这要求发送方的校验和也必须包含帧头,否则永远校验失败。这一点不常见(通常校验只覆盖长度+数据),需确认协议双方一致。 校验失败后,p->checksum 未清零,下次 WAIT_HEADER 会重新赋值为 0x5A,所以无累积问题,但逻辑上最好在校验分支内做明确清理以增强可读性。
⑥ main 函数缺少串口初始化⑦ 发送函数 SendByte 未定义,且 while(1) 内无其他保护 3. 改进后的代码框架(关键部分)volatile bit hasNewByte = 0;unsigned char tempsbuf;// ... Parser 定义、parser_init、parser_input_byte(加入长度检查) ...void Uart() interrupt 4 { if(TI) { TI = 0; } if(RI) { RI = 0; tempsbuf = SBUF; hasNewByte = 1; }}void main() { Parser p; parser_init(&p); Uart_Init(); // 假设已实现 while(1) { if(hasNewByte) { hasNewByte = 0; if(parser_input_byte(&p, tempsbuf)) { SendByte(p.buf_data[0); SendByte(p.buf_data[1); } } }}
并且在 WAIT_LEN 中添加: if(byte > sizeof(p->buf_data)) { p->state = WAIT_HEADER; break;}
总结代码展现了状态机解析思想,但缺少实时系统中的同步机制、无边界检查、初始化位置错误,是典型的“能跑但一有干扰就崩”的嵌入式代码。上述几点修正后,才能在实际串口通信中稳定工作。
|