串口接收卡死只能重启?协议状态机三个隐藏坑
一句话: 帧长度没校验→越界写坏相邻变量长度下溢→状态机永久卡死帧尾字节落进else分支→抹掉接收完成标志。三个坑都在边界条件上坏帧不常来一来就难查。适合谁读写串口协议状态机、遇到过设备突然不理人只能重启的嵌入式工程师。协议背景典型的串口协议帧帧头 [长度2B] [命令] [读写标志] [数据...] [校验]接收端用状态机逐字节解析一个字节一个字节地喂。框架本身很标准但三个边界条件没堵住各自埋了一个雷。坑一长度没校验越界写坏内存接收状态机进入数据段状态后直接写缓冲Rx_Buffer[Cnt] byte; // Cnt 是无符号16位 Cnt;缓冲只有 256 字节。如果上位机或干扰线发了一帧长度字段是 300 的帧Cnt会一路加下去越界写——直接覆盖缓冲后面的全局变量。后果看运气轻则某个功能错乱重则写进函数指针区域直接 HardFault 死机。坑二长度下溢状态机永久卡死再看长度校验的另一面。长度字段如果小于 3连帧头命令都不够而代码里处理方式是remaining Length - 3; // Length 是无符号16位Length 2时2 - 3在无符号运算里下溢成65533——数据段要收 65533 个字节才完成。状态机永远收不满永久卡死。更狠的是状态机卡死期间后面所有正常帧包括帧头字节全被当数据吞掉。设备对外表现为收到什么都不回只能断电重启。这两个坑其实是同一个修复进入数据段前校验长度范围if (Length 259 || Length 3) { Discard_Frame(); // 长度非法整帧丢弃 return; }长度合法才收数据非法直接丢。越界和下溢一起解决。坑三帧尾抹掉接收完成标志前两个坑修完后还有个更隐蔽的校验通过后置完成标志主循环处理/* 校验通过 */ Frame_Ready 1; // 通知主循环处理但帧尾还有两个字节比如结束符它们随后到达落入状态机的 else 分支——else 分支做的事是清空整个接收结构else { /* 不是帧头也不是数据 */ memset(rx_struct, 0, sizeof(rx_struct)); // 刚置位的标志也被清掉! }平时主循环反应快能抢在帧尾之前把标志拿走。但遇到长命令比如擦 Flash关中断执行好几秒帧尾必然先到标志被抹——长命令的应答静默丢失上位机以为没收到重发、超时、连锁乱套。修复else 分支只在状态机处于等待帧头时才清空else if (state WAIT_HEAD) { memset(rx_struct, 0, sizeof(rx_struct)); }帧尾字节到来时状态机已经在别的状态不碰已完成帧的标志。修复清单坑根因修复越界写长度无上限校验收数据前校验 Length 最大缓冲永久卡死无符号下溢同上Length 3 直接丢弃标志被抹else 分支无条件清空只在等待帧头状态清空经验状态机每个入口都要校验——长度范围、状态合法性一个都不能省校验通过即完成的假设是错的——协议解析要把帧尾字节纳入状态机流程收尾也是帧的一部分这类 bug 平时不触发一触发就难查——坏帧不常来但干扰环境里迟早来。真到了设备偶尔不理人、重启就好的阶段先查这三个位置实测对比无校验坏帧能越界写内存、卡死状态机 | 加长度校验坏帧直接丢弃永不卡死有用的话点个收藏下次调试直接用。有问题欢迎评论区交流看到了都会回。下一篇脚本明明写对了为什么就是匹配不到\r\n 和 \n 的坑——跨平台脚本的行尾陷阱

相关新闻

最新新闻

日新闻

周新闻

月新闻