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错误。
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 错误状态转换图
下面这张图把状态转换关系画清楚了。我建议你把它存下来,调试时对照着看。
4.4 错误计数器的增减规则
计数器怎么加、怎么减,是有明确规则的。我整理了一张表,你调试时对着看就行。
| 事件 | TEC变化 | REC变化 |
|---|---|---|
| 发送节点检测到位错误 | +8 | 不变 |
| 发送节点检测到ACK错误 | +8 | 不变 |
| 接收节点检测到CRC错误 | 不变 | +1 |
| 接收节点检测到填充错误 | 不变 | +1 |
| 接收节点检测到格式错误 | 不变 | +1 |
| 节点成功发送一帧 | -1 | 不变 |
| 节点成功接收一帧 | 不变 | -1(若REC>0) |
| 被动错误节点发送成功 | -1 | 不变 |
4.5 错误帧结构
错误帧分两种:主动错误帧和被动错误帧。结构上就差在错误标志上。
- 主动错误帧: 6个显性位(错误标志)+ 8个隐性位(错误界定符)
- 被动错误帧: 6个隐性位(错误标志)+ 8个隐性位(错误界定符)
主动错误帧的6个显性位会破坏总线上的位填充规则,其他节点检测到连续6个显性位,就知道出错了。被动错误帧的6个隐性位则不会影响总线——说白了,它犯错但不敢大声嚷嚷。
4.6 实际调试中的经验
最后分享几个我踩过的坑,希望能帮你少走弯路。
案例1:总线关闭后怎么恢复?
有些芯片(比如SJA1000)在总线关闭后,需要软件手动复位才能恢复。我见过一个项目,节点总线关闭后一直不恢复,因为工程师没写复位逻辑。结果整个系统瘫痪了。
案例2:被动错误节点很难排查
被动错误节点不会打断总线,它只是默默地发隐性错误帧。如果你用普通示波器看,根本看不出异常。我建议用CAN分析仪抓一下错误帧计数,哪个节点的错误帧数量异常,基本就是它了。
嗯,错误机制这块就讲这么多。记住一句话:CAN的错误处理不是用来“避免”错误的,而是用来“容忍”错误的。只要错误率在可控范围内,总线就能正常工作。真正要警惕的,是那些让节点进入被动错误甚至总线关闭的持续性故障。