3. CAN总线错误处理:错误类型、错误状态与恢复机制
做CAN总线开发,最怕什么?
我个人最怕的是——设备莫名其妙“死掉”了,但物理层看起来一切正常。示波器一挂,波形也没问题。可就是通信断了。
后来我才明白,这多半是CAN的错误处理机制在“干活”。CAN总线之所以能在工业现场这么可靠,靠的就是这套错误检测与恢复体系。今天咱们就把它彻底讲透。
3.1 错误类型:CAN总线能识别哪5种错误?
CAN协议定义了5种错误类型。说白了,就是总线上的每个节点都在“互相监督”。一旦发现不对劲,立刻上报。
| 错误类型 | 检测层级 | 触发条件 |
|---|---|---|
| 位错误(Bit Error) | 发送节点 | 发送的位与总线上的位不一致 |
| 填充错误(Stuff Error) | 所有节点 | 连续6个相同电平出现 |
| CRC错误(CRC Error) | 接收节点 | CRC校验值与本地计算结果不符 |
| 格式错误(Form Error) | 所有节点 | 固定格式位出现非法电平 |
| 应答错误(ACK Error) | 发送节点 | 发送节点未收到ACK应答 |
3.1.1 位错误
这个最好理解。发送节点往总线上放了一个“显性位”(0),结果自己回读发现总线上是“隐性位”(1)。这就叫位错误。
我在项目中遇到过一种情况:两个节点的CAN控制器时钟偏差太大,导致采样点错位。发送的位被误读,然后发送节点自己检测到位错误,疯狂重发。嗯,那场面,总线利用率直接飙到90%以上。
3.1.2 填充错误
CAN协议有个“位填充”规则:连续发送5个相同位后,必须插入一个相反位。这是为了给接收节点提供时钟同步的跳变沿。
如果接收节点发现总线上出现了连续6个相同电平,那就说明——要么发送节点坏了,要么总线被干扰了。这就是填充错误。
3.1.3 CRC错误
CAN帧尾部有一个15位的CRC校验字段。接收节点收到数据后,会用自己的CRC算法重新算一遍。如果结果对不上,就报CRC错误。
说白了,这就是数据完整性的最后一道防线。CRC错误通常意味着数据在传输过程中被篡改了。
3.1.4 格式错误
CAN帧中有一些固定格式的位,比如CRC界定符、ACK界定符、EOF等。这些位在协议里规定死了必须是隐性位(1)。
如果某个节点在应该出现“1”的位置读到了“0”,那就报格式错误。这通常说明总线上的节点时序有问题,或者有节点在乱发数据。
3.1.5 应答错误
这个只发生在发送节点身上。发送节点在ACK Slot位会释放总线(输出隐性位),然后等待接收节点拉低总线(输出显性位)来确认收到。
如果发送节点没等到这个ACK,就报应答错误。原因很简单:总线上没有节点在听它说话。
3.2 错误状态:节点是怎么“变坏”的?
CAN节点不是一发现错误就立刻“罢工”的。它有一套分级机制,根据错误严重程度,把自己分成三种状态。
| 错误状态 | 发送错误计数(TEC) | 接收错误计数(REC) | 行为表现 |
|---|---|---|---|
| 主动错误(Error Active) | 0 ~ 127 | 0 ~ 127 | 正常通信,发现错误时发送主动错误帧 |
| 被动错误(Error Passive) | 128 ~ 255 | 128 ~ 255 | 只能发送被动错误帧,发送前需等待8个隐性位 |
| 总线关闭(Bus Off) | ≥ 256 | — | 完全脱离总线,不参与任何通信 |
3.2.1 主动错误状态
这是正常状态。节点发现错误时,会发送一个由6个显性位组成的“主动错误帧”。这个帧很“强势”,会覆盖总线上的其他数据,强制所有节点知道出错了。
你想想看,这其实是一种“宁可错杀一千,不可放过一个”的策略。虽然粗暴,但在工业现场很管用。
3.2.2 被动错误状态
当错误计数超过127,节点就进入被动错误状态。这时候它变得“胆小”了——发送的错误帧只有6个隐性位,不会干扰其他节点的正常通信。
而且,被动错误节点在发送数据帧之前,必须等待8个连续的隐性位(正常节点只需要等待3个)。说白了,就是给它降权了,让它少说话。
关键点: 被动错误节点仍然可以接收数据,但它的发送能力被严重限制了。我见过一个系统,某个节点因为线路接触不良,反复进入被动错误状态,导致它的数据更新频率从10ms降到了500ms。
3.2.3 总线关闭状态
这是最严重的状态。当发送错误计数达到256,节点会彻底关闭CAN控制器,不再参与任何总线活动。相当于“自我隔离”了。
为什么会这样?因为如果一个节点持续出错,它反复发送错误帧会严重干扰总线。干脆让它闭嘴,保护整个网络。
3.3 错误恢复机制:怎么让节点“复活”?
节点进入总线关闭后,怎么恢复?
CAN协议规定了一个恢复流程:节点需要连续监测到128次11个连续的隐性位(相当于128个总线空闲序列),然后自动从总线关闭状态恢复到主动错误状态。
这个机制的设计意图很明确:
- 等待总线安静: 128个空闲序列大约需要1.5ms左右(以500kbps计算),确保总线上的干扰已经消失
- 自动恢复: 不需要外部干预,节点自己就能“复活”
- 防止频繁抖动: 如果节点恢复后又立刻出错,它会再次进入总线关闭,不会反复“折腾”总线
3.4 错误计数器的更新规则
错误计数器怎么加减?这里有个细节很多人容易忽略。
| 事件 | 发送错误计数(TEC) | 接收错误计数(REC) |
|---|---|---|
| 发送节点检测到位错误 | +8 | — |
| 接收节点检测到CRC错误 | — | +1 |
| 发送节点成功发送一帧 | -1 | — |
| 接收节点成功接收一帧 | — | -1(若REC > 0) |
| 发送节点进入被动错误后成功发送 | -7 | — |
你看这个规则,很有意思:
- 加得多,减得少: 一次错误加8,一次成功才减1。这明显是“从严从重”的惩罚机制
- 被动错误节点有“减刑”机会: 进入被动错误后,成功发送一次可以减7,这是为了让节点有机会“改过自新”
- 接收错误计数不会无限增长: 接收错误计数超过127后,再增加时速度会变慢,这是为了防止节点因为接收错误而轻易进入总线关闭
总结一下: CAN的错误处理机制,说白了就是一套“三权分立”的治理体系。5种错误类型负责“立法”,3种错误状态负责“司法”,错误计数器负责“执法”。这套机制保证了CAN总线在恶劣工业环境下的可靠性。
你想想看,一个节点出错了,它不会立刻“死机”,而是先警告(主动错误),再降权(被动错误),最后隔离(总线关闭)。等环境好了,它还能自动恢复。这种设计思路,值得我们做嵌入式开发的每个人学习。
下一章,咱们聊聊CAN总线最让人头疼的问题——总线仲裁与实时性分析。到时候我会分享一个我在汽车电子项目中遇到的“优先级反转”案例,保证让你印象深刻。