2. CAN 总线协议精讲:CAN 2.0 帧结构、仲裁机制、位填充与错误帧详解

好,咱们进入正题。CAN 2.0 协议,说白了就是 CAN 总线世界的「交通规则」。你只有搞懂了它,才能明白为什么 CAN 总线这么可靠,为什么它能扛住汽车上那么恶劣的电磁环境。我这些年调试过的 CAN 节点没有一千也有八百,很多疑难杂症,追根溯源都是对这几个基础概念理解不透彻。

2.1 帧结构:数据是怎么装进「信封」里的?

CAN 2.0 协议定义了两种帧格式:标准帧(11 位 ID)和扩展帧(29 位 ID)。别被数字吓到,其实结构非常清晰。

咱们先看标准帧。一个完整的数据帧,由以下几个部分组成:

  • SOF(Start of Frame):1 个显性位。告诉所有节点:「我要开始说话了!」
  • 仲裁场:11 位 ID + RTR 位。这是决定谁能抢到总线控制权的关键。
  • 控制场:IDE + r0 + DLC(4 位)。DLC 告诉别人我这包里装了多少数据(0-8 字节)。
  • 数据场:0-8 字节。真正要传的信息。
  • CRC 场:15 位 CRC + 1 位 CRC 界定符。用来检查数据有没有传错。
  • ACK 场:1 位 ACK Slot + 1 位 ACK 界定符。接收节点在这里告诉发送方:「我收到了!」
  • EOF(End of Frame):7 个隐性位。表示帧结束。

扩展帧呢?其实就是把 ID 从 11 位扩展到了 29 位。仲裁场变成了 11 位基础 ID + SRR + IDE + 18 位扩展 ID。其他部分基本一样。

重点提醒:标准帧和扩展帧可以共存于同一总线。但它们的仲裁优先级不同——标准帧的优先级永远高于扩展帧。为什么?因为标准帧的 IDE 位是显性(0),而扩展帧的 IDE 位是隐性(1)。显性位会「覆盖」隐性位,所以标准帧总能赢。

我记得有一次,客户抱怨说他们的扩展帧报文总是发不出去。我一查,总线上有个标准帧以 1ms 的周期在狂发。标准帧优先级高,扩展帧根本抢不过。这就是典型的「总线霸权」问题。

2.2 仲裁机制:谁的声音大,谁先说话

CAN 总线的仲裁机制,是我觉得最巧妙的设计之一。它不像以太网那样用 CSMA/CD(冲突检测),而是用 CSMA/CA(冲突避免)。说白了,就是「边发边听,谁先发 0 谁赢」。

具体怎么工作的?

  1. 所有节点同时开始发送 SOF 位。
  2. 然后逐位发送 ID。每个节点在发送每一位的同时,监听总线电平。
  3. 如果自己发的是隐性位(1),但总线上是显性位(0),说明有更高优先级的节点在发数据。自己立刻停止发送,转为接收模式。
  4. 仲裁失败的节点,会在总线空闲后自动重发。

你想想看,这个过程有多优雅。没有碰撞,没有重传风暴,优先级高的节点永远能第一时间拿到总线。这也是为什么 CAN 总线特别适合实时控制——关键信号的延迟是确定性的。

实战技巧:在 Vector CANalyzer 里,你可以用「Trace」窗口的「Arbitration」列来观察仲裁过程。我曾经用它抓到一个问题:某个节点的 ID 是 0x001,另一个是 0x002。按理说 0x001 优先级更高,但 0x002 的报文却总是先发出去。后来发现,0x001 节点的发送缓冲区被一个高频率的报文占满了,导致它根本没机会参与仲裁。嗯,这个坑我踩过。

2.3 位填充:为什么 CAN 总线不怕长串的 0 或 1?

位填充(Bit Stuffing)是 CAN 协议里一个容易被忽视,但极其重要的机制。它的作用很简单:防止总线长时间处于同一电平

规则是这样的:发送节点在连续发送 5 个相同电平的位之后,必须插入一个相反电平的位。接收节点在收到 5 个相同电平的位之后,会自动丢弃紧随其后的那个相反位。

为什么要这么做?两个原因:

  • 时钟同步:CAN 总线没有单独的时钟线。所有节点靠检测总线上的电平跳变来同步时钟。如果长时间没有跳变,节点的时钟就会漂移,导致采样错误。
  • 错误检测:如果接收节点检测到连续 6 个相同电平的位,就说明发生了位填充错误。这可以用来检测总线短路等硬件故障。

我见过一个案例:某款车在高温测试时,CAN 总线频繁报错。排查到最后,发现是某个节点的晶振在高温下频率漂移严重。正常情况下,位填充机制能容忍一定的时钟偏差。但晶振漂移太大,导致接收节点在采样时总是采到错误电平。这就是位填充机制在「救场」——它把错误暴露了出来,而不是让错误数据悄悄传过去。

注意:位填充机制只适用于 SOF 到 CRC 场之间的数据。EOF 和帧间空间不进行位填充。另外,CRC 界定符、ACK 界定符这些也是固定的隐性位,不参与填充。

2.4 错误帧:CAN 总线的「自我修复」能力

CAN 总线最让我佩服的一点,就是它的错误处理机制。它不需要一个中央控制器来监控总线健康,每个节点自己就能检测错误,并且主动报告。

CAN 2.0 定义了五种错误类型:

错误类型 检测条件 谁检测
位错误 发送的位与总线电平不一致 发送节点
填充错误 连续 6 个相同电平 所有节点
CRC 错误 接收的 CRC 与本地计算不一致 接收节点
形式错误 固定格式位(如 EOF、ACK 界定符)电平错误 所有节点
应答错误 发送节点未收到 ACK 发送节点

一旦某个节点检测到错误,它会立刻发送一个错误帧。错误帧由两部分组成:

  • 错误标志:6 个显性位。这会破坏当前正在传输的帧,让所有节点都意识到出错了。
  • 错误界定符:8 个隐性位。用来恢复总线空闲状态。

发送错误帧的节点,内部还有一个错误计数器。每发一次错误帧,计数器加 8;每成功发送一次,计数器减 1。当计数器超过 127 时,节点进入「被动错误」状态——它还能接收,但不能再主动发送错误帧了。当计数器超过 255 时,节点进入「总线关闭」状态——彻底离线。

这个机制有什么好处?我举个例子。假设总线上有个节点坏了,不停地发错误帧。按照传统总线,整个网络就瘫痪了。但 CAN 总线不会——那个坏节点最终会被「踢出」总线,其他节点照常工作。这就是 CAN 总线的「容错」能力。

调试技巧:在 CANalyzer 里,你可以用「Statistics」窗口查看每个节点的错误计数器值。我曾经用它定位过一个间歇性故障——某个节点的错误计数器在 100 到 120 之间反复跳动。这说明它处于「主动错误」和「被动错误」的边缘。后来发现是它的 CAN 收发器接触不良,导致发送的电平信号衰减严重。

好了,CAN 2.0 协议的核心内容就这些。帧结构让你知道数据怎么装,仲裁机制让你知道谁先说话,位填充保证时钟同步,错误帧提供自我修复。这四个机制加在一起,造就了 CAN 总线在汽车电子领域不可撼动的地位。下一章,咱们聊聊 CAN FD——它把 CAN 2.0 的带宽又往上提了一大截。