3.25. CAN 总线基础

CAN(Controller Area Network,控制器局域网)最初由 Bosch 于 20 世纪 80 年代设计,用于将汽车中所有的电子控制单元连接到一条短小的共享总线上。它至今仍主导着汽车线束领域,但其同样出色的鲁棒性、实时性以及多主设计也使它成为自动化、机器人、农业和各类工业设备中默认的现场总线。

CAN 不像 SPI 和 I2C 那样有单一的“控制器”和一组“外设”。总线上的每个节点都是对等的,任何节点都可以在总线空闲时发送数据。使这种架构能够扩展的,是总线的“广播加仲裁”设计。

3.25.1. 带优先级仲裁的广播

CAN 总线上的每条消息都携带一个标识符——经典标准帧为 11 位,扩展帧为 29 位。该标识符并不是地址;它标记的是这条消息关于什么(发动机转速、刹车踏板位置、电池电压等)。当某个节点想要发送一个数值时,它会用相应的标识符给该值打上标签并广播出去,总线上的每个节点都能看到这次广播。每个接收方都在硬件层面对总线进行过滤,只挑选出自己关心的 ID。

如果两个节点试图同时发送,总线的电气设计会让数值较小的标识符所对应的消息胜出,且不会丢失任何位。

其中的诀窍在于总线的两种电气状态:显性(逻辑 0)和隐性(逻辑 1)。当没有节点发送数据时,CAN 线由终端电阻保持为高电平(隐性);任何节点都可以通过其收发器灌入电流,将线拉低(显性)。其结果是一种线与(wired-AND)逻辑:只要有任意一个节点将总线驱动为显性,线就读作显性;只有当每个节点都释放总线时,才读作隐性。显性总是胜出。(有些参考资料将同样的机制称为线或(wired-OR),把“显性”视为有效信号,而不是隐性位的与运算——无论哪种说法,其物理行为都完全相同。)

A single bus line held high by a termination resistor to Vcc. Three nodes (A, B, C) hang off the bus. Each node's output stage is a diode in series with a switch to ground -- the diode points from the bus down to the switch, so closing the switch pulls the bus low through the diode but no node can drive the bus high.

概念形式的线与逻辑。真实的 CAN 在一对差分线(CAN_H / CAN_L)上运行同样的逻辑,收发器和终端电阻分布在两根线上,但总线上的规则是相同的:任何节点都可以拉为显性,只有当每个节点都释放时它才读作隐性。

仲裁正是直接利用了这种不对称性。每个发送节点都逐位发送其 ID,最高有效位(MSB)在前,并在发送的同时监视总线。如果某节点在线上送出一个隐性位却读回了显性位,它就知道有另一个 ID 更小的节点正在同时发送,并已在该位位置上胜出。它会立即停止驱动总线并转为侦听。与此同时,胜出的节点看到自己送出的位原样保持不变——从它的角度看,并没有发生什么异常。编号较小的 ID 之所以胜出,是因为它的显性位覆盖了编号较大的 ID 本应在相同位置发送的隐性位。

失败的节点随后会等待总线变为空闲,并自动重试。这里没有冲突,也不会丢失消息——只有确定性的优先级排序。

正是这种发布/订阅风格使 CAN 能够大规模工作:任何人都可以发送,任何人都可以接收,而 ID 使得分发隐式完成。

3.25.2. 物理总线

MCU 的 CAN 控制器并不直接驱动总线。它只暴露两个 3.3 V 的 CMOS 引脚——TX(它想要发送的位)和 RX(它在总线上看到的位)——这两个引脚连接到一颗称为 CAN 收发器(transceiver)或 PHY 的独立芯片。收发器位于控制器和真实的 CAN 线之间;它才是懂得如何与总线通信的部件。

总线本身是一对 5 V 的差分线:

  • CAN_H——这对线中的“高”线。

  • CAN_L——“低”线。

在隐性状态下,两根线都大约处于 2.5 V(总线 5 V 供电的中点)。在显性状态下,收发器将 CAN_H 拉高到约 3.5 V,将 CAN_L 拉低到约 1.5 V,从而在这对线之间产生约 2 V 的差分电压。接收方采样的是两根线之间的差值,这使得信号对长距离电缆上拾取的共模噪声免疫。

收发器的双向工作:

  • 在 TX 一侧,它读取控制器 3.3 V 的单端 TX 引脚,对于显性状态将 CAN_H 和 CAN_L 拉开,对于隐性状态则释放二者。

  • 在 RX 一侧,它读取差分的 CAN_H / CAN_L 线对,并在其 RX 引脚上向控制器报告一个单端的 3.3 V 电平。

两个 120 欧姆的终端电阻,分别位于电缆的两个物理端点,在没有节点驱动时将总线保持在隐性中点电压,并抑制反射——否则反射会在长距离传输时破坏差分信号。

3.25.3. 数据帧

标准 CAN 数据帧在线上看起来是这样的:

Eight fields drawn in sequence: SOF (1 bit), ID (11 bits), RTR (1 bit), control (6 bits), data (0 -- 8 bytes), CRC (16 bits), ACK (2 bits), and EOF (7 bits).

标准 CAN 数据帧:SOF、ID、RTR、控制、数据、CRC、ACK 和 EOF 字段。

每个字段都有特定的作用:

  • SOF(帧起始)。一个显性位,表示一个新帧正在开始,并使每个节点的位时钟同步。

  • ID(标识符)。总线据以仲裁的 11 位标识符。数值越小 = 优先级越高。

  • RTR(远程传输请求)。当帧是对数据的请求而非对数据的发送时置位;具有匹配 ID 的接收方会通过自行发送数据来响应。

  • Control(控制)。 一个 6 位字段,编码数据长度(DLC)以及若干管理位。

  • Data(数据)。 0 到 8 字节的有效载荷(CAN Classic;CAN FD 将其扩展到 64 字节)。

  • CRC。 共 16 位:对前面各字段计算的 15 位 CRC 加上 1 位 CRC 定界符。

  • ACK。 共 2 位。在第一位——ACK 槽——发送方释放总线,任何正确接收到该帧的节点都会将其拉低;第二位是一个隐性定界符。缺少 ACK 则告诉发送方没有节点收到该帧,应当重新发送。

  • EOF(帧结束)。七个隐性位,用于结束该帧。

所有这些都由 CAN 控制器在硬件中生成和解码;软件只看到 ID、数据和少数几个标志位。

3.25.4. CAN 的定位

CAN 的设计取舍决定了它的适用领域:

  • 鲁棒。 双绞线上的差分信号、内建的错误检测、确定性的优先级仲裁以及自动重试,使 CAN 能够在 UART 和 SPI 会损坏数据的电气噪声环境中正常工作。

  • 多主。 任何节点都可以在任何时刻发送。不需要中央控制器,也没有任何节点会成为单点故障。

  • 带宽受限。 经典 CAN 的上限约为 1 Mbit/s;CAN FD 对此进行了扩展。当链路位于电路板或模块之间、通常要跨越数米电缆、且以可靠性为优先时,CAN 是正确的选择。

  • 高层协议。 裸 CAN 总线并不规定一个 ID 的含义,也不规定如何将一条长消息拆分成分片。CANopen、J1939、ISO-TP/UDS、NMEA 2000 等应用层协议在其之上叠加了这些规则。另一个值得了解的东西是 DBC 文件:一种厂商专用的文本格式,记录在某个特定车辆或系统上哪些 ID 携带哪些位级信号。DBC 文件是描述某条总线消息布局的元数据格式,本身并不是一种协议。

代码中的 CAN 总线 中的驱动处理线级的 CAN 帧;将 ID 映射到含义则是应用程序的工作。