4. CAN错误处理机制:错误类型、状态机与计数规则

CAN总线为什么能在工业、汽车领域这么稳?说白了,它的错误处理机制功不可没。我刚开始接触CAN协议栈时,觉得这部分就是些状态切换和计数器,没什么大不了的。直到有一次,我在一个电机控制项目里,总线莫名其妙地频繁掉线,排查了整整两天……最后发现是位错误导致的。从那以后,我再也不敢小看这套机制了。

今天咱们就把CAN的错误处理掰开揉碎,讲清楚三件事:五种错误类型、错误状态机、计数规则。你理解透了,以后调试CAN总线至少能少走一半弯路。

4.1 五种错误类型:CAN节点如何发现“出事了”

CAN控制器在发送或接收每一帧时,都会实时检查5种错误。只要发现一种,节点就会主动发出错误帧,通知总线上所有节点。

错误类型 检测时机 触发条件 我踩过的坑
位错误 发送节点 发送的位电平与总线回读不一致 终端电阻没焊好,回读电平飘忽不定
填充错误 所有节点 连续6个相同位(违反位填充规则) 干扰导致填充位被翻转,误判为填充错误
CRC错误 接收节点 本地计算的CRC与发送端CRC不匹配 波特率偏差导致采样点偏移,CRC算出来总不对
格式错误 所有节点 帧格式固定位(如CRC分隔符)电平错误 某款MCU的CAN控制器有硬件bug,固定位输出异常
应答错误 发送节点 ACK槽位没有收到显性位(无人应答) 总线上只有一个节点时,自己发自己收,应答错误是常态

核心要点:位错误和应答错误只有发送节点能检测;填充错误、CRC错误、格式错误是所有节点都能检测。这个区别在调试时很重要——你看到错误帧,得先判断是谁报的。

4.2 错误状态机:三个状态,一条命

每个CAN节点内部都有一个错误状态机,根据错误计数器的值在三个状态间切换。我习惯把它理解成“交通灯”:绿灯随便跑,黄灯小心点,红灯靠边停。

我的经验:很多初学者以为“总线关闭”就是节点坏了。其实这是CAN协议的保护机制——它主动切断故障节点,不让它污染总线。你想想看,如果一个节点疯狂发错误帧,整个总线都会被它拖垮。

CAN节点错误状态机 错误主动 TEC<128 且 REC<128 错误被动 TEC≥128 或 REC≥128 总线关闭 TEC > 255 TEC/REC超限 TEC/REC恢复 TEC>255 128个隐性位恢复(硬件复位) TEC:发送错误计数器 | REC:接收错误计数器 错误主动节点可发送主动错误帧(6个显性位) | 错误被动节点只能发送被动错误帧(6个隐性位)

注意:从“总线关闭”恢复到“错误主动”,必须检测到128次连续的11个隐性位(即总线空闲)。这不是软件能干预的,完全由硬件自动完成。我曾经在项目里等这个恢复等了快1秒,差点以为芯片挂了。

4.3 错误计数规则:谁犯错,谁记分

CAN协议用两个计数器来记录节点的“犯错记录”:发送错误计数器(TEC)和接收错误计数器(REC)。规则其实很简单——成功减分,失败加分。

4.3.1 发送错误计数规则

  • 发送成功:TEC减1(前提是TEC > 0)
  • 发送错误:TEC加8
  • 特殊情况:如果发送时检测到位错误,且错误发生在ACK槽之后,TEC只加1——因为这时候帧基本发完了,问题不大

4.3.2 接收错误计数规则

  • 接收成功:REC减1(前提是REC > 0)
  • 接收错误:REC加1
  • 特殊情况:如果接收节点检测到CRC错误,且自己是错误主动状态,REC加8——主动节点要承担更多责任

关键规律:发送错误每次加8,接收错误每次加1。这意味着发送节点犯错后“扣分”更狠,更容易进入总线关闭。为什么这么设计?因为发送节点犯错会直接污染总线,必须严惩。

4.4 错误帧结构:节点如何“喊停”

当节点检测到错误时,它会立即发送错误帧。错误帧分两种:

错误帧类型 发送者 错误标志 后续行为
主动错误帧 错误主动节点 6个显性位 强制覆盖总线,所有节点都能感知
被动错误帧 错误被动节点 6个隐性位 不覆盖总线,其他节点可能忽略

错误帧之后,总线会进入错误分隔符(8个隐性位),然后重新开始仲裁。整个流程完全由硬件自动完成,软件不需要干预。

调试技巧:用示波器抓CAN总线波形时,如果你看到连续6个显性位,那就是主动错误帧。我曾经靠这个特征,在5分钟内定位到一个节点因为电源纹波过大导致频繁位错误的问题。

4.5 实战避坑:错误处理中的常见陷阱

  1. 总线关闭后不会自动恢复通信——硬件恢复需要128个空闲位,但如果你软件没有重新初始化CAN控制器,节点可能一直处于关闭状态。我建议在应用层加一个超时检测,主动复位CAN外设。
  2. 错误被动节点发送的数据可能被忽略——因为被动错误帧是隐性位,其他节点可能检测不到。如果你的节点频繁进入错误被动,赶紧查硬件,别指望软件能兜底。
  3. 不要随意清零错误计数器——有些开发者觉得计数器碍事,在中断里手动清零。这是大忌!CAN的错误机制是闭环的,你清零了计数器,等于把故障节点重新放回总线,它会继续污染通信。
  4. 波特率偏差会导致CRC错误激增——我遇到过最诡异的一次,总线波特率标称500k,实际测量只有498k。接收节点采样点偏移,CRC错误率飙升到30%。最后换了精度更高的晶振才解决。

重要提醒:CAN错误处理机制是协议栈的“免疫系统”。调试时不要只盯着数据帧,多看看错误计数器的值。很多总线问题,在错误计数器里早就有了预兆。