4. CAN错误帧与错误处理:位错误、填充错误、CRC错误、格式错误、应答错误、错误状态机(主动/被动/总线关闭)

大家好,我是老周。今天我们来聊聊CAN总线里一个特别“硬核”的话题——错误处理。

说实话,很多工程师做CAN开发,能收发报文就觉得万事大吉了。但一旦上了车,跑起来,各种稀奇古怪的问题就来了。报文丢了、总线挂了、节点莫名其妙“失联”了……

这些问题的根源,十有八九都出在错误处理机制上。CAN协议之所以能在工业、汽车领域“横行霸道”这么多年,靠的就是这套极其健壮的错误检测与恢复机制。今天,我就把这套机制掰开了、揉碎了,讲给你听。

4.1 错误检测的五种“武器”

CAN协议定义了五种错误检测方式。它们就像五个哨兵,时刻盯着总线上的每一个比特。任何一个哨兵发现问题,就会立刻触发错误帧。

错误类型 检测层级 检测原理 个人经验
位错误 发送器 发送器监控总线电平,若与自己发送的不一致,则报错 最常见,硬件驱动能力不足或总线阻抗不匹配时频发
填充错误 接收器 检测到连续6个相同电平的位,违反位填充规则 干扰导致同步丢失时容易出现
CRC错误 接收器 接收方计算的CRC校验和与发送方的不一致 数据完整性被破坏,硬件或线缆问题居多
格式错误 接收器 检测到固定格式位(如CRC界定符、ACK界定符)为显性 协议栈实现bug或节点时序偏差
应答错误 发送器 发送器在ACK槽未检测到显性位 总线上没有其他节点接收,或接收节点全部故障

4.1.1 位错误

这个我太熟了。发送器在发送每个比特的同时,会去读总线上的电平。如果读回来的和自己发出去的不一样,那就说明出问题了。比如你发了个显性位(0),但总线电平却是隐性的(1),那肯定是别的节点在跟你“抢”总线,或者你的驱动器根本没把总线拉下去。

避坑指南: 我曾经在一个项目中,发现某个节点频繁报位错误。查了半天,发现是CAN收发器的Rs引脚(斜率控制)对地电阻焊错了,导致驱动能力下降。换了个电阻,问题解决。

4.1.2 填充错误

CAN协议规定,连续发送5个相同电平后,必须插入一个相反电平的位。这叫“位填充”。接收器如果检测到连续6个相同电平,就知道填充规则被破坏了。

为什么会这样?说白了,是为了保证总线有足够的跳变沿,方便所有节点同步时钟。如果长时间没有跳变,节点的时钟漂移就会累积,最终导致采样错误。

4.1.3 CRC错误

CRC(循环冗余校验)是数据完整性的最后一道防线。发送器会为数据场计算一个15位的CRC校验和,放在CRC场里。接收器收到后,用同样的算法重新算一遍。如果对不上,那就是CRC错误。

嗯,这里要注意:CRC错误只由接收器报告。发送器不会知道自己发的CRC错了。

4.1.4 格式错误

CAN帧里有些位是固定格式的,比如CRC界定符、ACK界定符、EOF(帧结束)等。这些位必须是隐性电平(1)。如果接收器在这些位置检测到显性电平(0),就会报格式错误。

我个人习惯把格式错误看作是“协议层面的语法错误”。出现这种错误,多半是节点时序有问题,或者协议栈实现有bug。

4.1.5 应答错误

这个最好理解。发送器在ACK槽发送一个隐性位,然后等待总线上至少有一个接收节点把它拉成显性。如果没人拉,那就是应答错误。

说白了,就是你的报文发出去了,但没人听。我遇到过一种情况:某个节点因为软件bug,把自己设置成了只听模式(Listen-Only),结果它只收不发,导致所有发给它的报文都得不到ACK,发送器一直报应答错误。

4.2 错误帧的结构

任何一个节点检测到错误,就会立刻发送错误帧。错误帧由两部分组成:错误标志和错误界定符。

  • 错误标志: 主动错误节点发送6个连续的显性位,被动错误节点发送6个连续的隐性位。
  • 错误界定符: 8个连续的隐性位,用于恢复总线空闲状态。

这里有个关键点:主动错误标志的6个显性位,会破坏当前正在传输的帧的位填充规则。其他节点收到这6个显性位后,也会检测到填充错误,进而发送自己的错误标志。这样一来,错误标志就会被“叠加”,总线上可能出现6到12个显性位。

核心要点: 错误帧的发送,会强制终止当前正在传输的报文。所有节点都会丢弃这个损坏的报文,然后等待总线空闲后重新发送。

4.3 错误状态机:从“好人”到“黑户”

CAN节点不是一报错就“死”的。它有一套非常精妙的状态机,根据错误严重程度,逐步限制自己的行为。这套状态机有三个状态:主动错误、被动错误和总线关闭。

CAN节点错误状态机 主动错误 发送主动错误标志 被动错误 发送被动错误标志 总线关闭 不参与总线通信 TEC > 127 TEC < 127 TEC > 255 128个总线空闲信号后自动恢复 状态转换关键:TEC(发送错误计数器) • 每次发送成功:TEC减1 • 每次发送错误:TEC加8 • 接收错误计数器(REC)也有类似机制,但只影响被动/主动转换 • 总线关闭后,必须等待128个总线空闲信号,TEC才能清零

4.3.1 主动错误状态

这是节点的“正常”状态。在这个状态下,节点检测到错误后,会发送主动错误标志(6个显性位)。这个标志足够强势,可以打断任何正在进行的通信。

说白了,主动错误节点是个“暴脾气”,发现问题就立刻拉总线,让所有人都知道。

4.3.2 被动错误状态

当节点的发送错误计数器(TEC)超过127时,它会进入被动错误状态。在这个状态下,节点检测到错误后,只能发送被动错误标志(6个隐性位)。

你想想看,6个隐性位能干什么?它不会影响总线电平,其他节点根本感觉不到。所以,被动错误节点虽然知道自己错了,但它没有能力去打断别人。它只能默默等待当前帧结束,然后重试。

注意: 被动错误节点在发送完错误帧后,还有一个“挂起传输”状态。它必须等待8个隐性位(错误界定符)之后,再额外等待8个隐性位,才能开始下一次发送。这是为了防止一个频繁出错的节点“霸占”总线。

4.3.3 总线关闭状态

这是最严重的状态。当TEC超过255时,节点会进入总线关闭状态。在这个状态下,节点完全与总线断开,既不发送也不接收任何报文。

我曾经在一个项目中,遇到一个ECU(电子控制单元)频繁进入总线关闭状态。查到最后,发现是它的CAN收发器电源纹波太大,导致发送数据时经常出错。TEC一路飙升,直到超过255,节点就“自闭”了。

要恢复,只能等硬件复位,或者等待协议规定的128个总线空闲信号出现。嗯,说白了,就是让节点“冷静”一段时间。

4.4 错误处理的实际影响

这套错误处理机制,保证了CAN总线在恶劣环境下的可靠性。但如果你不了解它,它也会给你带来很多麻烦。

  • 总线利用率下降: 频繁的错误帧和重传,会占用大量总线带宽。
  • 节点“失联”: 总线关闭的节点,就像人间蒸发一样,你发什么它都不回。
  • 故障扩散: 一个节点的错误,可能导致其他节点也检测到错误,形成“雪崩效应”。
调试建议: 我建议你在开发阶段,一定要把CAN控制器的错误状态寄存器读出来。通过分析TEC和REC的变化趋势,可以快速定位问题节点。很多CAN分析工具都支持实时显示这些计数器,用起来很方便。

好了,关于CAN的错误帧与错误处理,我就讲到这里。这套机制是CAN协议的“灵魂”所在。理解了它,你才算真正入门了CAN总线开发。下次遇到总线问题,别慌,先看看错误计数器,再分析错误类型,问题往往就能迎刃而解。