4、CAN协议基础:CAN总线仲裁机制、位填充机制、错误处理机制

好,咱们今天聊点硬核的。CAN总线这东西,搞汽车电子的没人能绕开。我当年刚入行时,总觉得CAN就是个串口升级版,结果第一次调试多节点通信就被教做人了——总线莫名其妙锁死,报文发不出去。后来才明白,仲裁、位填充、错误处理这三个机制,才是CAN协议的灵魂。

说白了,CAN总线能成为汽车行业的事实标准,靠的就是这三板斧。咱们一个一个拆开讲。

4.1 CAN总线仲裁机制:谁先说话谁有理

先问个问题:多个ECU同时往总线上发数据,会不会打架?

会,但CAN有办法解决。它用的不是“谁先抢到谁发”,而是“谁优先级高谁发”。这个机制叫非破坏性逐位仲裁

怎么实现的?靠的是显性位(0)和隐性位(1)。总线上,显性位能覆盖隐性位。你想想看,如果两个节点同时发,一个发0,一个发1,那总线上的电平就是0——发1的那个节点一看,咦,我发的和总线上的不一样,说明有人比我优先级高,那我主动退让。

仲裁发生在标识符(ID)段。ID越小,优先级越高。我见过不少新手问:“那ID能不能重复?” 答案是绝对不能。重复ID会导致仲裁失败,两个节点都以为自己赢了,最后数据全乱套。

关键点:
  • 仲裁是逐位进行的,从ID的最高位开始比
  • 显性位(逻辑0)优先级高于隐性位(逻辑1)
  • 仲裁失败的节点自动转为接收模式,不破坏总线
  • ID越小,优先级越高(0x000优先级最高)

我在项目中遇到过一个问题:某个ECU的ID设成了0x7FF,结果每次和别的节点抢总线都抢不过,导致关键报文总是延迟。后来我把ID改小了一档,问题就解决了。嗯,这里要注意,ID分配不是随便来的,得按报文紧急程度排。

4.2 位填充机制:保证时钟同步的“小聪明”

位填充,说白了就是防止总线“冷场”。

CAN总线用的是NRZ编码,不归零。如果连续发5个以上的相同电平,接收端的时钟就容易跑偏。你想想看,没有跳变沿,怎么同步?

所以协议规定:连续发送5个相同位后,必须插入一个相反位。这就是位填充。

举个例子:

原始数据:11111 00000 11111
填充后:  111110 000001 111110

看到没?每5个相同位后面,硬塞一个相反的。接收端收到后,会自动把这个填充位去掉,还原原始数据。

我的经验: 位填充会影响实际传输效率。理论上CAN 2.0A的帧最多128位,但填充后可能膨胀到150位左右。设计通信周期时,记得留余量。

位填充覆盖的范围是:从SOF(帧起始)到CRC(循环冗余校验)段。ACK段和EOF段不参与填充。为什么?因为ACK段只有2位,没必要;EOF段是7个隐性位,用来标识帧结束,填充了反而坏事。

我曾经踩过一个坑:自己写CAN驱动时,忘了在发送端做位填充,结果接收端CRC校验一直报错。查了两天才发现,原来是填充位没加,导致CRC计算对不上。从那以后,我再也不敢手撸位填充逻辑了,老老实实用硬件模块。

4.3 错误处理机制:CAN的“自我修复”能力

CAN总线的错误处理,是我见过最完善的之一。它不光是检测错误,还能自动重发、自动隔离故障节点。

错误类型分五种:

错误类型 检测位置 说明
位错误 发送节点 发送的位与总线电平不一致(仲裁时除外)
填充错误 所有节点 检测到连续6个相同位
CRC错误 接收节点 CRC校验和与本地计算不一致
格式错误 所有节点 帧格式不符合规范(如EOF段不是隐性位)
ACK错误 发送节点 发送后未收到ACK应答

检测到错误后,节点会发送错误帧。错误帧由6个显性位(错误标志)和8个隐性位(错误界定符)组成。这6个显性位会破坏当前帧,让所有节点都知道出错了。

然后呢?发送节点自动重发。重发次数不限,直到成功或被总线关闭。

注意: 每个CAN控制器都有两个计数器:发送错误计数器(TEC)和接收错误计数器(REC)。当TEC或REC超过一定阈值,节点会进入不同状态:
  • 错误主动(Error Active):TEC < 127,正常参与通信
  • 错误被动(Error Passive):128 ≤ TEC ≤ 255,只能发被动错误帧
  • 总线关闭(Bus Off):TEC > 255,完全断开总线

我遇到过最头疼的问题:某个ECU的CAN收发器坏了,导致它不停地发错误帧,把整个总线都拖垮了。其他节点全都在重发,总线利用率直接飙到100%。后来查出来是收发器的共模电压出了问题。嗯,这里要提醒一句:CAN总线出问题,别光看软件,硬件也得查。

4.4 三个机制的协同工作

仲裁、位填充、错误处理,这三个机制不是孤立的。它们共同保证了CAN总线的可靠性。

举个例子:假设节点A和节点B同时发数据,A的ID小,赢了仲裁。但A在发送过程中,因为干扰导致位错误。这时候:

  1. A检测到位错误,立即发错误帧
  2. 所有节点收到错误帧,丢弃当前数据
  3. A自动重发,再次参与仲裁
  4. 如果A的ID还是最小,它继续发;否则让给优先级更高的节点

整个过程对应用层是透明的。你想想看,如果没有仲裁机制,重发时可能又冲突;没有位填充,时钟可能失步;没有错误处理,一个坏节点就能搞死整个网络。这三个机制,缺一个都不行。

避坑指南: 我曾经在项目里把CAN控制器的自动重发功能关了,想自己实现重发逻辑。结果发现,手动重发根本赶不上硬件自动重发的速度,而且还要处理仲裁失败后的状态机。折腾了两周,最后还是老老实实开了硬件自动重发。所以我的建议是:除非你有非常特殊的理由,否则别关自动重发。

好了,CAN协议的三个核心机制就讲到这里。下节课咱们聊CAN帧结构,到时候你会看到,这些机制是怎么嵌入到每一帧里的。