4. CAN总线错误处理:错误类型、错误计数器、错误状态机、总线关闭恢复
CAN总线在工业现场之所以这么能打,很大程度上归功于它那套健壮的错误处理机制。说白了,就是总线自己会看病、自己会吃药,实在不行还能自己躺下休息。我刚开始接触CAN时,觉得这部分理论性太强,直到有一次在电机驱动项目里,总线莫名其妙就挂了,查了三天才发现是错误状态机在作祟。嗯,从那以后,我再也不敢跳过这章了。
4.1 错误类型:总线上的五种“病”
CAN协议定义了五种错误类型。任何节点只要检测到其中一种,就会立刻发送错误帧。我个人习惯把这五种错误分成两类:位层面的错误和帧层面的错误。
| 错误类型 | 检测层面 | 触发条件 | 我见过的坑 |
|---|---|---|---|
| 位错误 (Bit Error) | 位 | 发送节点监控总线,发现发送的电平与回读的电平不一致 | 总线阻抗不匹配时,回读信号畸变,误报位错误 |
| 填充错误 (Stuff Error) | 位 | 连续6个相同电平出现,违反位填充规则 | 晶振偏差大的节点,采样点偏移,容易误判填充位 |
| CRC错误 (CRC Error) | 帧 | 接收节点计算的CRC与发送节点的CRC不匹配 | 我曾经遇到过,线束过长导致信号反射,CRC频繁出错 |
| 格式错误 (Form Error) | 帧 | 帧的固定格式位(如CRC界定符、ACK界定符)出现非法电平 | 干扰导致ACK槽被拉低,接收节点报格式错误 |
| 应答错误 (ACK Error) | 帧 | 发送节点在ACK槽没有检测到显性位(没有节点应答) | 终端电阻没接好,信号反射导致ACK丢失 |
核心要点:位错误和填充错误是“发送节点自己就能发现的”;CRC错误、格式错误、应答错误是“接收节点反馈给发送节点的”。理解这个区别,后面看错误计数器就简单了。
4.2 错误计数器:两个“记分牌”
每个CAN节点内部都有两个硬件计数器:TEC(发送错误计数器)和REC(接收错误计数器)。它们不是软件变量,是CAN控制器硬件的寄存器。你想想看,硬件自动累加,不需要CPU干预,这才是实时性的保障。
计数的规则很有意思,不是简单的“出错加1”。我总结了一个口诀:“发送成功减,接收成功减;出错加8,成功减1”。但要注意,发送节点和接收节点的加减规则不完全一样。
| 事件 | TEC变化 | REC变化 | 说明 |
|---|---|---|---|
| 发送节点检测到位错误 | +8 | 不变 | 发送节点自己背锅 |
| 发送节点检测到ACK错误 | +8 | 不变 | 没人应答,发送节点负责 |
| 接收节点检测到CRC错误 | 不变 | +8 | 接收节点发现数据不对 |
| 接收节点检测到填充错误 | 不变 | +8 | 同上,接收节点报错 |
| 发送成功(无错误) | -1 | 不变 | 发送节点减分 |
| 接收成功(无错误) | 不变 | -1 | 接收节点减分 |
| 发送节点作为接收者检测到错误 | 不变 | +1 | 发送节点也会监听总线,发现别人的错误 |
避坑指南:我曾经在调试时发现TEC涨得飞快,但REC几乎不动。查了半天,原来是发送节点的CAN_H和CAN_L接反了。发送节点自己发出去的信号,回读时全是错的,所以位错误疯狂累加。记住:TEC飙升,先查硬件连接。
4.3 错误状态机:三个“健康等级”
根据TEC和REC的值,节点会处于三种状态之一。这就像人的健康状态:正常、亚健康、病危。状态切换完全由硬件自动完成,不需要软件干预。
警告:错误被动状态下,节点发送的被动错误帧是隐性位。这意味着如果总线上只有一个节点处于错误被动,它发送的错误帧可能被其他节点忽略,导致错误帧无法被正确识别。我曾经在一个只有两个节点的系统里遇到过,一个节点进入错误被动后,另一个节点死活收不到它的错误帧,整个总线陷入死锁。
4.4 总线关闭恢复:让“装死”的节点活过来
当TEC超过255,节点就进入总线关闭状态。说白了,就是节点自己把自己踢出群聊,不再参与任何总线活动。为什么要这样设计?你想想看,如果一个节点疯狂出错还继续发数据,整个总线都会被它污染。所以CAN协议设计了一个“自我隔离”机制。
恢复过程是这样的:
- 检测到TEC > 255,节点立即进入总线关闭状态。
- 硬件自动停止所有发送/接收活动,总线接口进入高阻态。
- 启动恢复计时:节点需要检测到总线上连续出现128个隐性位(即总线空闲状态)。
- 恢复完成:TEC和REC清零,节点重新进入错误主动状态。
关键细节:这128个隐性位必须是连续的。如果中间出现一个显性位,计数器就重置,重新开始计数。这意味着如果总线一直很忙(一直有显性位),节点可能永远无法恢复。我建议在系统设计时,预留一个“静默窗口”,让总线空闲一段时间,给关闭的节点一个恢复机会。
4.5 实战中的错误处理策略
理论讲完了,说说实际项目中怎么用。我个人习惯在应用层做三件事:
- 定期读取TEC和REC:大部分CAN控制器都有寄存器可以读取这两个值。我一般用定时器每100ms读一次,记录到日志里。如果发现某个节点的TEC持续增长,说明它的硬件链路有问题。
- 监控错误状态中断:很多CAN控制器支持错误状态变化中断。当节点从错误主动进入错误被动,或者进入总线关闭时,会产生中断。我在中断服务程序里只做一件事:记录时间戳和状态,然后唤醒一个后台任务去处理。
- 总线关闭后的恢复策略:硬件自动恢复需要128个连续隐性位,但实际项目中我很少完全依赖硬件。我会在应用层加一个“恢复超时”机制。如果节点进入总线关闭后,超过一定时间(比如1秒)还没恢复,我就主动复位CAN控制器,强制重新初始化。
避坑指南:我曾经在一个项目里,节点进入总线关闭后,硬件自动恢复了,但应用层的发送缓冲区还残留着之前没发出去的数据。恢复后这些数据被重新发送,结果又导致错误,再次进入总线关闭。这就是所谓的“震荡恢复”。我的解决办法是:在恢复后,先清空发送缓冲区,再重新开始正常通信。
嗯,CAN的错误处理机制,说白了就是一套“犯错-记分-隔离-恢复”的闭环系统。理解了这个闭环,你就能在调试时快速定位问题,而不是对着示波器发呆。记住:错误计数器是诊断的第一手资料,错误状态机是系统的健康指示灯。