4. CAN错误处理:错误类型与错误状态机
大家好,我是老李。做嵌入式医疗设备这么多年,CAN总线上的错误处理,是我觉得最值得花时间讲透的一块。你想想看,医疗设备要是因为通信错误出了故障,那可不是闹着玩的。今天我们就来聊聊CAN的错误处理机制,我会结合我踩过的坑,把五种错误类型和状态机讲明白。
4.1 五种错误类型
CAN协议定义了五种错误类型。说实话,我刚接触CAN时觉得这五种错误有点多,但后来发现,正是这种精细的分类,让CAN在工业现场和医疗设备中如此可靠。
4.1.1 位错误(Bit Error)
这是最基础的一种错误。发送节点在发送位的同时,会监控总线上的实际电平。如果发送的电平和监控到的电平不一致,就产生位错误。
关键点:仲裁阶段不会触发位错误。因为仲裁时多个节点可能同时发送显性位和隐性位,显性位会覆盖隐性位,这是正常现象。
我在项目中遇到过一件事:有个呼吸机设备,偶尔会出现通信中断。排查了很久,最后发现是CAN收发器的共模电压范围不够,导致在强干扰下发送的位被误判。嗯,这里要注意,位错误不一定都是软件问题,硬件选型也很关键。
4.1.2 填充错误(Stuff Error)
CAN协议规定,连续发送5个相同电平的位后,必须插入一个相反电平的填充位。如果接收节点检测到连续6个相同电平的位,就判定为填充错误。
为什么要这么做?说白了,就是为了保证时钟同步。没有填充位,接收节点可能丢失同步信号。我刚开始做CAN驱动时,自己写了个位流编码器,结果填充逻辑写错了,导致接收端一直报填充错误。后来老老实实用了硬件自带的填充功能。
// 填充规则示例
// 发送数据: 11111 00000 11111
// 填充后: 111110 000001 111110
// 注意:填充位与前面的位相反
4.1.3 CRC错误(CRC Error)
CRC校验是保证数据完整性的最后一道防线。发送节点计算15位CRC校验码,接收节点重新计算并比对。如果不一致,就报CRC错误。
我个人习惯在医疗设备中,对关键数据(比如心率、血氧值)做双重校验。虽然CAN本身有CRC,但我在应用层再加一层校验,心里更踏实。毕竟人命关天的事,多一层保护总没错。
避坑指南:我曾经遇到过CRC计算多项式搞错的情况。不同CAN控制器可能使用不同的CRC实现,移植代码时一定要确认硬件支持的CRC算法。
4.1.4 格式错误(Form Error)
格式错误是指帧格式不符合CAN协议规范。比如,CRC界定符必须是隐性位,ACK槽之后必须有隐性位作为界定符。如果检测到这些固定格式的位置出现错误电平,就报格式错误。
这种错误通常由硬件自动检测,软件很少需要直接处理。但我在调试时发现,如果总线终端电阻匹配不好,信号反射可能导致格式错误。所以,布线时一定要保证终端电阻正确。
4.1.5 应答错误(ACK Error)
发送节点在ACK槽发送隐性位,然后监听总线。如果有接收节点正确收到了报文,会在ACK槽发送显性位进行应答。如果发送节点没有检测到显性位,就报应答错误。
这意味着什么?意味着你的报文发出去了,但没人接收。我在调试多节点系统时,经常用这个错误来检查节点是否在线。如果某个节点一直报应答错误,八成是它的CAN控制器没配置好,或者总线断开了。
| 错误类型 | 检测节点 | 常见原因 |
|---|---|---|
| 位错误 | 发送节点 | 总线干扰、收发器故障 |
| 填充错误 | 接收节点 | 位流编码错误、干扰 |
| CRC错误 | 接收节点 | 数据传输错误、CRC算法不匹配 |
| 格式错误 | 接收节点 | 帧格式异常、信号反射 |
| 应答错误 | 发送节点 | 无接收节点、节点离线 |
4.2 错误状态机
CAN节点根据错误计数器的值,在三种状态之间切换:错误主动、错误被动、总线关闭。这个状态机设计得非常巧妙,既能保证通信的可靠性,又不会因为单个节点的故障拖垮整个总线。
4.2.1 错误计数器
每个CAN节点都有两个计数器:发送错误计数器(TEC)和接收错误计数器(REC)。
- TEC:每次发送错误加8,每次成功发送减1
- REC:每次接收错误加1,每次成功接收减1
注意这个加减规则。发送错误一次加8,成功一次才减1。这意味着如果总线有问题,错误计数器会快速上升。我见过一个案例,总线终端电阻虚焊,导致信号反射严重,TEC在几秒内就飙到了200多。
4.2.2 三种状态
错误主动(Error Active): 这是正常状态。TEC和REC都小于128。节点可以正常发送和接收,检测到错误时发送主动错误标志(6个显性位)。
错误被动(Error Passive): TEC或REC大于等于128,但小于256。节点可以发送和接收,但检测到错误时只能发送被动错误标志(6个隐性位)。说白了,就是节点知道自己可能有问题,不再干扰其他节点。
总线关闭(Bus Off): TEC大于等于256。节点完全断开与总线的连接,不再参与任何通信。这是最严重的状态,需要软件干预才能恢复。
重要提醒:在医疗设备中,总线关闭状态必须被监控。我曾经遇到过一台输液泵,因为CAN控制器进入总线关闭状态,导致与主控的通信中断了整整30秒。虽然硬件会自动恢复,但这30秒的数据丢失在医疗场景下是不可接受的。
4.2.3 状态转换
状态转换完全由硬件自动完成,不需要软件干预。但作为开发者,我们需要理解这个机制,以便在软件层面做好监控和恢复。
// 状态转换伪代码
if (TEC >= 256) {
state = BUS_OFF;
// 断开总线连接
} else if (TEC >= 128 || REC >= 128) {
state = ERROR_PASSIVE;
// 发送被动错误标志
} else {
state = ERROR_ACTIVE;
// 正常通信
}
4.2.4 恢复机制
从总线关闭状态恢复,需要软件主动触发。通常的做法是:
- 检测到总线关闭事件
- 等待128个总线空闲位(11个隐性位为一个空闲位)
- 重新初始化CAN控制器
- 将TEC和REC清零
我个人习惯在恢复后先发送一个测试报文,确认总线正常后再恢复业务通信。这样可以避免刚恢复又立刻进入总线关闭的恶性循环。
实战建议:在医疗设备中,建议在应用层实现一个错误监控任务。每隔100ms读取一次错误计数器,如果发现TEC或REC持续增长,及时报警。我曾经在一个项目中,通过监控错误计数器的变化趋势,提前发现了总线连接器松动的问题。
4.3 总结
CAN的错误处理机制,说白了就是一套自我诊断和恢复的系统。五种错误类型覆盖了通信中可能出现的各种异常,状态机则保证了单个节点的故障不会拖垮整个总线。
在实际项目中,我建议大家:
- 重视硬件设计,特别是终端电阻和共模抑制
- 在软件层面监控错误计数器
- 对总线关闭状态做好恢复和报警处理
- 关键数据在应用层做双重校验
好了,这一章就讲到这里。下一章我们会聊CAN的位定时与同步,这是理解CAN通信速率和可靠性的关键。到时候见。