CAN 数据丢帧?别只加 FIFO,看看接收过载与错误处理
摘要:CAN 总线波形正常,但上位机偶尔收不到某一帧数据?或者节点突然进入“被动错误”状态?不是波特率问题,而是接收 FIFO 溢出或错误计数器触发的自我保护。本文解析 CAN 的“隐身杀手”——错误帧。
一、问题描述(现象)
**CAN 总线负载率只有 30%,理论上不会丢包;
但监控软件偶尔抓不到报文;
或者某个节点突然“失联”,示波器看总线还在跳,但它就是不回 ACK。**
很多工程师的排查方向是:
FIFO 深度不够?
中断里处理太慢?
换个更强的 MCU?
二、原理分析
1. 物理模型
CAN 控制器有一套自治的错误处理机制。
接收报文 -> [Acceptance Filter] -> [FIFO] -> [CPU Interrupt]2. 核心参数
FIFO 深度:通常只有 3 级(STM32)。
Error Counter(错误计数器):TEC / REC。
State(节点状态):Active -> Passive -> Bus Off。
3. 反直觉真相
CAN 丢帧通常不是“没收到”,而是“被硬件丢弃了”。
FIFO 溢出:中断处理慢了一点点,第 4 帧进来,硬件直接丢弃,且不通知你。
错误被动(Error Passive):节点发送错误过多,被禁止主动发送,只能被动接收。
Bus Off:节点自我隔离,彻底沉默。
三、工程级解决方案
方案 1:必须处理 FIFO 溢出(软件解法)
不要指望 FIFO 无限缓存。
// 在 CAN 中断里 if (__HAL_CAN_GET_FLAG(&hcan, CAN_FLAG_FOV0)) { __HAL_CAN_CLEAR_FLAG(&hcan, CAN_FLAG_FOV0); // 记录一次溢出错误 can_fifo_overflow_cnt++; }最佳实践:
中断里只做标记,不做复杂处理。
在任务线程中处理 CAN 数据。
方案 2:监控错误计数器(救命稻草)
在调试阶段,把错误计数器读出来。
uint8_t rec = (hcan.Instance->ESR >> 24) & 0xFF; // REC uint8_t tec = (hcan.Instance->ESR >> 16) & 0xFF; // TEC判读标准:
REC/TEC > 96 → 进入 Error Passive。
TEC > 255 → Bus Off。
方案 3:Bus Off 自动恢复(量产必加)
默认情况下,Bus Off 后需要手动复位。
自动恢复流程:
检测到 Bus Off 标志。
等待 128 次连续 11 个隐性位。
重新初始化 CAN 外设(HAL_CAN_Init)。
四、选型避坑建议
不要屏蔽错误中断:ERROR 中断是 CAN 的“黑匣子”,必须开启。
验收滤波器(Filter):
列表模式(List)适合节点少、ID 固定的场景。
掩码模式(Mask)适合批量 ID 过滤。
不要过度降噪:有些工程师为了“稳”把所有错误帧都屏蔽,结果总线真正出问题时你完全不知道。
五、总结 Checklist
[ ] 是否开启了 CAN ERROR 中断?
[ ] FIFO 溢出时是否有计数或日志?
[ ] Bus Off 后是否有自动恢复机制?
[ ] 验收滤波器是否配置正确(没把 ID 过滤掉)?
六、写在最后(关注我,少走弯路)
我是 gqqsherry,一个拒绝调包、专注底层逻辑的嵌入式工程师。
CAN 的错误处理机制是“最不像单片机外设”的部分,它更像一个微型操作系统。
关注我的专栏《嵌入式底层避坑指南》,我会持续更新 CAN、UART、SPI 等外设的真实调试案例和量产级解决方案。
👉下一篇预告:《CAN FD 跑不起来?别只怪线缆,看看采样点与位时序》
References
ISO 11898-1 – CAN error handling and fault confinement
STM32 Reference Manual – CAN error status register
如果你在项目中遇到过 CAN 节点“假死”或 Bus Off 问题,欢迎在评论区分享你的排查经验。
原创文章,转载请注明出处。
