4、CAN错误处理机制:错误类型与状态机

CAN总线的可靠性,说白了就靠这套错误处理机制撑着。我刚开始接触CAN的时候,总觉得这玩意儿太复杂——不就是传个数据嘛,搞这么多错误类型干嘛?后来在产线上被坑过一次,才明白这些设计全是血泪教训换来的。

4.1 五种错误类型

CAN协议定义了五种错误。任何节点只要检测到其中一种,就会立刻发出错误帧。咱们一个一个说。

错误类型 检测时机 检测节点 触发条件
位错误 发送时 发送节点 发送位与总线电平不匹配
填充错误 接收时 所有节点 连续6个相同位
CRC错误 接收时 接收节点 CRC校验不通过
格式错误 接收时 所有节点 帧格式字段非法
ACK错误 发送后 发送节点 ACK槽未被置为显性

4.1.1 位错误

这个最直观。发送节点往总线上放了一个显性位(0),结果回读发现是隐性位(1)。说明总线被别的节点拉住了,或者物理层出了问题。

我的经验: 位错误频繁出现,十有八九是终端电阻没配好,或者线缆太长导致信号反射。别一上来就怀疑芯片坏了。

4.1.2 填充错误

CAN协议规定,连续5个相同位之后必须插入一个相反位。如果接收端发现连续6个相同位,那就是填充错误。

为什么会这样?说白了就是为了保证时钟同步。没有位填充,节点之间的时钟漂移会越积越大。

4.1.3 CRC错误

发送节点会计算一个15位CRC(标准帧)或17位CRC(扩展帧)附在帧尾。接收节点用自己的算法重新算一遍,对不上就是CRC错误。

注意: CRC错误不一定都是传输干扰。我曾经遇到过一例,是某个节点的晶振偏差太大,导致采样点偏移,CRC算出来总是不对。换了个晶振就好了。

4.1.4 格式错误

帧格式里有些字段是固定值。比如CRC界定符必须是隐性位,ACK界定符也必须是隐性位。如果检测到显性位,那就是格式错误。

这种错误通常意味着总线上的节点在发送非法帧,或者硬件逻辑出了bug。

4.1.5 ACK错误

发送节点发完数据后,会在ACK槽等待一个显性位。如果没人应答(还是隐性),它就认为ACK错误。

你想想看,这意味着什么?总线上没有其他节点在正常接收。我调试时遇到这种情况,第一反应就是查终端电阻和节点供电。

4.2 错误状态机

每个CAN节点内部都有两个计数器:发送错误计数器(TEC)和接收错误计数器(REC)。根据这两个值,节点会处于三种状态之一。

核心逻辑: 错误越多,计数器越大。计数器越大,节点越“被动”。最终可能被踢出总线。

4.2.1 主动错误状态

这是正常状态。TEC < 127 且 REC < 127。节点检测到错误时,发送主动错误帧——由6个显性位组成,能强制打断当前总线通信。

说白了,主动错误节点是“强势”的,它犯错时整个总线都得停下来听它的。

4.2.2 被动错误状态

当 TEC > 127 或 REC > 127 时,节点进入被动错误状态。这时候它检测到错误,只能发被动错误帧——6个隐性位。这玩意儿不会影响总线,其他节点可能根本不理它。

我记得有个项目,某个节点间歇性丢帧,查了半天发现它已经处于被动错误状态了。它发的错误帧太“温柔”,主节点根本没当回事。

4.2.3 总线关闭状态

当 TEC > 255 时,节点彻底关闭总线接口。它不再发送任何数据,也不再接收任何数据。相当于被踢出局了。

要恢复?只能靠硬件复位或者应用层手动请求恢复。而且恢复前还得等128个连续的隐性位(总线空闲)。

4.3 错误状态转换图

下面这张图把状态转换关系画清楚了。我建议你把它存下来,调试时对照着看。

CAN节点错误状态转换图 主动错误 TEC<127, REC<127 被动错误 TEC>127 或 REC>127 总线关闭 TEC>255 TEC>127 TEC<127 且 REC<127 TEC>255 128个隐性位 + 应用层请求 说明: 1. 主动错误节点发送主动错误帧(6个显性位),能打断总线 2. 被动错误节点发送被动错误帧(6个隐性位),不影响总线 3. 总线关闭节点不参与任何通信,需外部干预才能恢复

4.4 错误计数器的增减规则

计数器怎么加、怎么减,是有明确规则的。我整理了一张表,你调试时对着看就行。

事件 TEC变化 REC变化
发送节点检测到位错误 +8 不变
发送节点检测到ACK错误 +8 不变
接收节点检测到CRC错误 不变 +1
接收节点检测到填充错误 不变 +1
接收节点检测到格式错误 不变 +1
节点成功发送一帧 -1 不变
节点成功接收一帧 不变 -1(若REC>0)
被动错误节点发送成功 -1 不变
避坑指南: 注意看,接收错误每次只加1,发送错误一次加8。这意味着发送节点犯错代价更大。我曾经遇到过某个节点因为终端电阻松动,发送总出错,TEC很快冲到255,直接被总线关闭了。

4.5 错误帧结构

错误帧分两种:主动错误帧和被动错误帧。结构上就差在错误标志上。

  • 主动错误帧: 6个显性位(错误标志)+ 8个隐性位(错误界定符)
  • 被动错误帧: 6个隐性位(错误标志)+ 8个隐性位(错误界定符)

主动错误帧的6个显性位会破坏总线上的位填充规则,其他节点检测到连续6个显性位,就知道出错了。被动错误帧的6个隐性位则不会影响总线——说白了,它犯错但不敢大声嚷嚷。

4.6 实际调试中的经验

最后分享几个我踩过的坑,希望能帮你少走弯路。

案例1:总线关闭后怎么恢复?

有些芯片(比如SJA1000)在总线关闭后,需要软件手动复位才能恢复。我见过一个项目,节点总线关闭后一直不恢复,因为工程师没写复位逻辑。结果整个系统瘫痪了。

案例2:被动错误节点很难排查

被动错误节点不会打断总线,它只是默默地发隐性错误帧。如果你用普通示波器看,根本看不出异常。我建议用CAN分析仪抓一下错误帧计数,哪个节点的错误帧数量异常,基本就是它了。

嗯,错误机制这块就讲这么多。记住一句话:CAN的错误处理不是用来“避免”错误的,而是用来“容忍”错误的。只要错误率在可控范围内,总线就能正常工作。真正要警惕的,是那些让节点进入被动错误甚至总线关闭的持续性故障。