第4章:CAN错误处理机制——5种错误类型与错误状态机
大家好,我是老张。今天咱们聊聊CAN总线里一个特别重要的东西——错误处理机制。说实话,我刚开始接触CAN的时候,觉得这东西不就是个通信协议嘛,能有多复杂?直到有一次在实车上,总线莫名其妙就挂了,排查了整整两天……嗯,从那以后,我再也不敢小看CAN的错误处理了。
4.1 为什么需要错误处理?
你想想看,汽车上跑着几十个ECU,环境又吵又热,电磁干扰到处都是。CAN总线要是没有一套完善的错误处理机制,那车开着开着,刹车信号丢了、油门信号错了,后果不堪设想。
CAN协议的设计者早就想到了这一点。他们搞了一套非常严谨的错误检测和恢复机制。说白了,就是让每个节点都能自己发现问题、自己报告问题、自己处理问题。不需要一个中央监控器来盯着。
核心思想:分布式错误处理。每个CAN节点都是自己的“警察”,发现违规就报警。
4.2 五种错误类型详解
CAN协议定义了5种错误类型。我一个个讲,每个我都会结合项目经验来说。
4.2.1 位错误(Bit Error)
这个最好理解。发送节点往总线上发了一个“显性”位(逻辑0),结果自己回读的时候发现总线上是“隐性”位(逻辑1)。这就叫位错误。
为什么会这样?可能是总线短路了,也可能是另一个节点同时在发数据,产生了冲突。我个人习惯在调试时先查位错误计数器,如果这个值涨得很快,十有八九是物理层出了问题。
我的经验:曾经有个项目,位错误频繁出现,查了半天发现是CAN收发器的共模电感虚焊了。重新焊接后,问题消失。
4.2.2 填充错误(Stuff Error)
CAN协议规定:连续发送5个相同电平的位之后,必须插入一个相反电平的位。这叫“位填充”。如果接收节点发现连续6个相同电平的位,那就报填充错误。
你可能会问:为什么要搞这么麻烦?其实是为了保证时钟同步。CAN总线没有单独的时钟线,全靠电平跳变来同步。如果长时间没有跳变,时钟就漂了。
我记得有一次,一个同事写的CAN驱动,发送数据时忘了做位填充处理,结果接收端一直报填充错误。嗯,这种低级错误,犯过一次就记住了。
4.2.3 CRC错误
CRC(循环冗余校验)是通信协议的老朋友了。CAN帧里有一个15位的CRC字段,发送节点计算好填进去,接收节点重新计算一遍。对不上?那就是CRC错误。
CRC错误的常见原因:
- 总线干扰导致数据位翻转
- 波特率不匹配
- 时钟精度不够
我建议你在做系统设计时,给CRC错误计数器设一个阈值。如果短时间内连续出现多次CRC错误,大概率是硬件问题,不是偶发干扰。
4.2.4 格式错误(Form Error)
CAN帧的格式是固定的。比如CRC界定符必须是隐性位,ACK槽后面必须是隐性位。如果接收节点发现这些固定位置的位电平不对,就报格式错误。
说白了,这就是“格式不对,直接拒收”。我在项目中遇到过一种情况:某个ECU的晶振老化,导致位时序偏差,偶尔会踩到格式错误的边界。这种问题最难查,因为不是每次都出现。
4.2.5 应答错误(ACK Error)
发送节点发完一帧数据后,会在ACK槽等待接收节点的应答。如果没收到应答(即总线上还是隐性位),就报应答错误。
应答错误的典型场景:
- 总线上没有其他节点(就你一个在发)
- 接收节点处于被动错误状态,无法发送应答
- 接收节点的CAN控制器坏了
注意:应答错误不等于数据没收到。有时候接收节点收到了数据,但因为自身状态问题无法应答。发送节点会重发,直到成功或达到重发次数上限。
4.3 错误状态机
每个CAN节点内部都有两个计数器:发送错误计数器(TEC)和接收错误计数器(REC)。根据这两个计数器的值,节点会处于三种状态之一。
| 状态 | 条件 | 行为 |
|---|---|---|
| 主动错误 | TEC < 127 且 REC < 127 | 正常通信,发现错误时发送主动错误标志(6个显性位) |
| 被动错误 | TEC > 127 或 REC > 127 | 可以接收和发送,但发现错误时只能发送被动错误标志(6个隐性位),且发送前要等待8个隐性位 |
| 总线关闭 | TEC > 255 | 完全断开与总线的连接,不发送也不接收 |
4.3.1 主动错误状态
这是正常状态。节点发现错误后,会发送一个“主动错误标志”——6个显性位。这6个显性位会覆盖总线上的正常数据,强制所有节点知道“出错了”。
主动错误标志的威力很大,因为它会破坏当前正在传输的帧。所以设计上要求:只有主动错误状态的节点才能发主动错误标志。
4.3.2 被动错误状态
当TEC或REC超过127时,节点进入被动错误状态。这时候节点变得“低调”了:
- 发现错误后,只能发6个隐性位作为被动错误标志
- 隐性位不会覆盖显性位,所以其他节点可能根本不知道它报了错
- 发送数据前,必须等待8个连续的隐性位(这叫“挂起传输”)
我曾经在项目里遇到过:一个ECU频繁进入被动错误状态,导致它的报文总是发不出去。排查后发现是它的CAN控制器初始化参数配错了,位时序不对。调整后恢复正常。
4.3.3 总线关闭状态
这是最严重的情况。当TEC超过255时,节点认为自己“病得不轻”,干脆主动断开与总线的连接。不发送、不接收、不参与任何总线活动。
总线关闭后怎么恢复?有两种方式:
- 自动恢复:检测到128次11个连续的隐性位后,自动回到主动错误状态
- 手动恢复:通过软件复位CAN控制器
重要提醒:总线关闭的节点不会影响其他节点通信。这是CAN协议的一个优点——一个节点坏了,不会拖垮整个网络。
4.4 错误计数器的增减规则
错误计数器不是简单的一刀切。CAN协议规定了详细的增减规则,我挑几个重点说:
- 发送成功:TEC减1(前提是TEC > 0)
- 接收成功:REC减1(前提是REC > 0)
- 发送错误:TEC加8
- 接收错误:REC加1
- 主动错误标志发送后:TEC再加8
你看这个规则,发送错误的惩罚比接收错误重得多。为什么?因为发送错误会破坏总线上的数据帧,影响更大。接收错误只是自己没收到,不影响别人。
我个人习惯在调试时,把TEC和REC的值打印出来。如果发现某个节点的TEC一直在涨,那就要重点检查它的发送逻辑和物理层。
4.5 实战中的错误处理策略
说了这么多理论,咱们聊聊实际项目中怎么用。
策略一:监控错误计数器
在诊断协议里,可以定义一条读取TEC和REC的请求。这样在实车测试时,就能实时监控每个节点的健康状态。
策略二:设置错误阈值
不要等到总线关闭了才处理。我建议在TEC达到100时就报一个警告,让系统知道“这个节点快不行了”。
策略三:区分错误类型
位错误和CRC错误,大概率是物理层问题。格式错误和填充错误,更可能是软件配置问题。应答错误,则要检查网络拓扑。
避坑指南:我曾经在一个项目里,把CAN控制器的采样点位置设错了。结果在常温下一切正常,一到高温环境就频繁报CRC错误。后来调整了采样点位置,问题解决。所以,CAN控制器的时序参数一定要仔细算,别偷懒。
4.6 小结
CAN的错误处理机制,说白了就是一套“自我诊断+自我恢复”的系统。5种错误类型覆盖了通信中可能出现的各种问题,3种状态机则保证了节点不会无限恶化下去。
你想想看,一个没有中央控制器的网络,每个节点都能自己发现问题、自己处理问题、自己决定要不要退出——这种设计思路,在30年前是非常超前的。直到今天,很多工业总线还在借鉴CAN的错误处理思想。
嗯,这一章就到这里。下一章咱们聊聊CAN的位时序和采样点配置,这可是调试中的硬骨头。到时候我会分享几个实战中的调参技巧,敬请期待。
公众号:蓝海资料掘金营,微信deep3321