4. CAN错误处理:CAN总线错误类型与状态机

CAN总线在飞控系统里,说白了就是一条生命线。我见过不少飞控板,别的都好好的,就CAN通信老出幺蛾子。今天咱们就聊聊CAN总线上的那些错误类型,以及硬件是怎么通过错误计数器和状态机来自我保护的。

你想想看,飞控系统里电机、舵机、传感器都在高频工作,电磁环境相当恶劣。CAN总线虽然抗干扰能力强,但也不是金刚不坏之身。嗯,这里咱们得把五种错误类型掰开揉碎了讲清楚。

4.1 五种CAN总线错误类型

CAN协议定义了五种错误检测机制。我个人习惯把它们分成两类:一类是“传输过程中出的错”,另一类是“协议格式上的错”。咱们一个个来看。

4.1.1 位错误(Bit Error)

这是最直观的错误。发送节点往总线上发一个显性位(0),结果自己回读的时候读到了隐性位(1),那就说明总线被别的节点拉低了,或者受到了干扰。

核心要点:发送节点在发送位的同时,会监控总线电平。如果读到的和自己发的不一致,就报位错误。

我在项目中遇到过一件事:有个舵机控制节点,每次启动瞬间就会把总线拉成隐性,导致主控发出去的显性位全部变成位错误。后来查出来是舵机电源上电瞬间的浪涌干扰了CAN收发器的参考电平。

避坑指南:我曾经在调试时发现位错误频繁出现,最后定位到是CAN收发器的共模电压范围不够。飞控系统里不同节点地电位可能有差异,建议选用共模范围宽的收发器,比如TJA1050或SN65HVD230。

4.1.2 填充错误(Stuff Error)

CAN总线采用NRZ编码,为了防止时钟同步丢失,协议规定:连续发送5个相同电平的位后,必须插入一个相反电平的填充位。如果接收节点检测到连续6个相同电平,就报填充错误。

为什么会这样?说白了,没有电平跳变,节点间的时钟就会慢慢漂移。填充机制保证了总线上的电平至少每5位就翻转一次。

数据位序列 填充后序列 说明
11111 111110 5个1后插入0
00000 000001 5个0后插入1
111110 1111101 注意填充位后继续计数

注意:填充机制只适用于数据帧和远程帧的SOF到CRC段。ACK段、EOF段和帧间隔不进行填充。这个细节我当年在写CAN驱动时踩过坑,以为所有段都要填充,结果通信一直对不上。

4.1.3 CRC错误(CRC Error)

CRC校验是CAN帧的最后一道防线。发送节点根据数据段计算15位CRC,接收节点重新计算并比对。如果不一致,就报CRC错误。

我记得有一次飞控在高温箱里测试,CRC错误率突然飙升。查了半天发现是CAN控制器时钟在高温下漂移了,导致CRC计算时序出错。后来换了温漂更小的晶振才解决。

CRC生成多项式: x15 + x14 + x10 + x8 + x7 + x4 + x3 + 1

4.1.4 格式错误(Form Error)

格式错误检测的是CAN帧中固定格式的位段。比如CRC界定符必须是隐性位,ACK界定符必须是隐性位,EOF必须是7个隐性位。如果这些固定位段出现了显性位,就报格式错误。

你想想看,这些位段是协议硬性规定的,如果出现异常,说明总线上的节点可能没有正确理解帧格式,或者硬件逻辑出了故障。

4.1.5 应答错误(ACK Error)

发送节点在ACK槽位发送隐性位,然后等待至少一个接收节点拉成显性位来确认。如果发送节点读到的还是隐性位,就说明没有节点正确接收到这帧数据,报应答错误。

我在项目中遇到过:飞控主控发送数据后一直报应答错误,后来发现是总线终端电阻没接对。没有终端电阻,信号反射导致接收节点无法正确采样ACK槽位。

小技巧:调试时如果发现应答错误,先检查总线终端电阻(120Ω两端各一个),再检查节点是否处于只听模式(Listen-Only Mode)。

4.2 错误计数器与状态机

光检测到错误还不够,CAN控制器需要一套机制来管理错误状态。这就是错误计数器和状态机要做的事。

4.2.1 错误计数器

每个CAN节点都有两个错误计数器:

  • 发送错误计数器(TEC):记录发送错误次数
  • 接收错误计数器(REC):记录接收错误次数

计数器的增减规则很有意思:

  • 发送节点检测到错误:TEC + 8
  • 接收节点检测到错误:REC + 1
  • 节点成功发送一帧:TEC - 1(如果TEC > 0)
  • 节点成功接收一帧:REC - 1(如果REC > 0,且REC ≤ 127)
  • 如果REC > 127,成功接收一帧后REC减到119~127之间

为什么发送错误加8,接收错误只加1?我个人理解是:发送错误的影响更大,因为发送节点会主动破坏总线。加8是为了让发送节点更快进入错误被动状态,减少对总线的干扰。

4.2.2 错误状态机

根据TEC和REC的值,CAN节点处于三种状态之一:

状态 条件 行为
错误主动(Error Active) TEC ≤ 127 且 REC ≤ 127 正常通信,检测到错误时发送主动错误帧(6个显性位)
错误被动(Error Passive) TEC > 127 或 REC > 127 可以通信,但检测到错误时发送被动错误帧(6个隐性位),且发送前等待8个隐性位
总线关闭(Bus Off) TEC > 255 完全断开与总线的连接,不发送任何数据

关键点:错误被动状态下,节点发送的被动错误帧是隐性位,不会破坏总线上的其他通信。总线关闭状态下,节点需要等待128个11位隐性位(总线空闲)才能恢复。

4.2.3 状态转换流程

咱们用一张SVG图来展示这个状态机,这样更直观:

错误主动 TEC≤127 且 REC≤127 错误被动 TEC>127 或 REC>127 总线关闭 TEC > 255 TEC或REC超过127 TEC和REC都降到127以下 TEC超过255 128个总线空闲后恢复 图例说明: 错误恶化方向 错误恢复方向 总线关闭后自动恢复

重要提醒:总线关闭后,节点不会自动恢复通信,必须检测到128次11位连续隐性位(总线空闲)才能回到错误主动状态。在飞控系统中,如果某个节点进入总线关闭状态,意味着它完全失联了。我建议在应用层增加超时检测和节点健康监控机制。

4.3 实际调试中的经验

说了这么多理论,咱们聊聊实际调试中怎么用这些知识。

我在飞控调试时,习惯先看错误计数器的值。如果REC持续增长,说明总线上有干扰或者某个节点在发垃圾数据。如果TEC增长,说明本节点发送有问题,可能是硬件故障或者配置错误。

具体排查步骤:

  1. 读取CAN控制器的错误状态寄存器,看当前处于什么状态
  2. 如果处于错误被动,读取TEC和REC的具体值
  3. 根据错误类型寄存器判断是哪类错误最多
  4. 位错误多:检查硬件电平、终端电阻、共模电压
  5. 填充错误多:检查时钟精度,看是否有节点时钟漂移
  6. CRC错误多:检查数据链路层,看是否有数据被篡改
  7. 格式错误多:检查CAN控制器配置,波特率是否一致
  8. 应答错误多:检查节点是否在线,总线是否断开

调试小工具:我习惯在飞控固件里加一个CAN错误统计任务,每100ms打印一次TEC、REC和错误类型分布。这样在飞行日志里就能看到通信质量的变化趋势。嗯,这个习惯帮我解决了不少疑难杂症。

最后说一句:CAN总线的错误处理机制设计得非常巧妙,它让总线在恶劣环境下依然能保持通信。但咱们做飞控的,不能完全依赖硬件纠错,应用层的冗余设计和健康监控同样重要。


公众号:蓝海资料掘金营,微信deep3321