跳转至

第 3 章 CAN 基础

CAN(Controller Area Network)由 Bosch 于 1980 年代为汽车电子设计,1991 年发布 CAN 2.0 规范,1993 年成为国际标准 ISO 11898;CAN FD(Flexible Data-rate)在 2012 年由 Bosch 发布,2015 年并入 ISO 11898-1 修订版。经典 CAN 的关键机制——帧结构、位仲裁、位时序与错误处理——是后续 CAN FD(第 4 章)与 CANopen(第 5 章)的公共前提。

阅读本章前建议先接通一条最小 SocketCAN 总线(第 9 章「SocketCAN 栈与内核侧」小节里有 vcan 虚拟总线搭法),把 candump 挂着对照读,帧结构会直观很多。

帧、仲裁与位时序三件套的先后关系:

flowchart LR
    frame["帧结构<br/>仲裁段 + 数据段 + CRC"] --> arb["位仲裁<br/>非破坏性多主,ID 小者优先"]
    arb --> timing["位时序<br/>同步段 + 传播段 + 采样点"]
    timing --> err["错误处理<br/>TEC / REC 与状态机"]

1. CAN 分层与帧类型速览

CAN 协议栈通常画成两层:

  • 数据链路层(Data Link Layer,DLL):帧的组装/拆解、位填充、CRC、仲裁、错误处理、应答。规范主体在 ISO 11898-1。
  • 物理层(Physical Layer,PHY):位时序到差分电压的映射、驱动器与接收器、总线拓扑。规范主体在 ISO 11898-2(高速)、ISO 11898-3(低速容错)与 ISO 11898-6(带选择性唤醒的高速收发器)。

CAN 规范本身不定义应用层——一帧 8 字节的载荷怎么解释交给上层(CANopen、J1939、CAN Kingdom、厂商自定义等)。这也是 CAN 常被误当作"不完整协议"的原因:它只到链路层,跨厂商互通需要 profile。

链路层可见的帧有四种:

帧类型 用途 何时出现
数据帧(Data Frame) 携带数据发送 常态
远程帧(Remote Frame) 请求某 ID 的数据 需要"拉取"语义时
错误帧(Error Frame) 报告帧错误 任一节点检出错误
过载帧(Overload Frame) 请求延长帧间间隔 节点来不及准备接收

日常调试 90% 时间只见到数据帧与偶发的错误帧;远程帧因优先级反转风险(见「远程帧与优先级反转防护」小节)在现代 CANopen、CiA 402 中已很少使用。

规范与速率的演进顺序:

timeline
    title CAN 规范的版本线
    1980s : Bosch 为汽车电子设计
    1991 : CAN 2.0 发布
    1993 : 成为 ISO 11898
    2012 : Bosch 发布 CAN FD
    2015 : 并入 ISO 11898-1 修订版

2. 帧结构与位编码

CAN 数据帧由七个场(field)串成;每个场的位定义与时序都有明确规范。

2.1 标准数据帧的七场

SOF | 仲裁场 | 控制场 | 数据场 | CRC 场 | ACK 场 | EOF | IFS
    ID+RTR   IDE,r0,DLC  0..8B  15+1bit  1+1bit  7bit  3bit

七个场依次是:

  • SOF(Start of Frame):1 位显性,标志帧开始,同时触发全网硬同步(见「位时序与采样点」)。
  • 仲裁场(Arbitration Field):标准帧为 11 位标识符(Identifier,ID)+ RTR(Remote Transmission Request);扩展帧在此场中依次是 11 位 base ID、SRR、隐性 IDE、18 位扩展 ID 与 RTR。
  • 控制场(Control Field):标准帧为 IDE(显性)+ r0(显性)+ 4 位 DLC(数据长度码,0–8 字节);扩展帧的 IDE 已在仲裁场中给出,控制场改为 r1 + r0 + DLC。
  • 数据场(Data Field):0–8 字节,按字节序号从 MSB 起逐位发送。
  • CRC 场(CRC Field):15 位 CRC 序列 + 1 位 CRC 界定符(隐性)。
  • 应答场(ACK Field):1 位 ACK 槽(发送器发隐性,任一接收器把它改成显性即应答)+ 1 位 ACK 界定符(隐性)。
  • 帧结束(EOF):7 位隐性;帧间间隔(IFS):3 位隐性,之后总线回到空闲。

位速率与帧时间:经典 CAN 单帧最大位数(含位填充)约 130 位;8 字节数据帧在 500 kbps 下约 230–260 μs。负载率算式在第 9 章给出。

2.2 扩展帧与远程帧

  • 扩展帧(Extended Frame,ISO 11898-1 §5.2.4.4):仲裁场 29 位 ID(11 位 base ID + 18 位 extend ID),SRR(Substitute Remote Request)位与 IDE 位组合标识扩展。物理层与错误处理与标准帧完全相同,只是仲裁段更长。
  • 远程帧(Remote Frame):RTR 位为隐性,无数据场,DLC 指示"期望的响应数据长度"。远程帧触发一个具有相同 ID 的节点回一条数据帧,用于"拉取"模式。

CAN 不是主从协议。CAN 帧类型里没有"命令"与"响应"的角色区分,任何节点在任何时刻只要总线空闲即可发起发送——这就是"非破坏性多主"(multi-master, non-destructive arbitration)。远程帧的"请求→响应"看起来像主从,但它仍是基于 ID 的订阅,任何节点都能发同 ID 的数据帧应答,不绑定发送者身份。

2.3 NRZ 编码与位填充

CAN 用非归零制(Non-Return-to-Zero,NRZ)编码:显性电平(逻辑 0)与隐性电平(逻辑 1)在位时间内保持不变,没有中间翻转。NRZ 简单但缺少自时钟——连续多个同电位平会让接收器失去位边界。CAN 用位填充(bit stuffing)解决:从 SOF 起,编码电路监视已发送位序列,一旦检测到连续 5 个同电平位就在下一位强行插入一个反电平位。接收器做位剥除(bit de-stuffing)恢复原序列;若监视到连续 6 个同电平位,判为填充违规错误。

位填充同时提供了软同步(re-synchronization)窗口——每个非填充跳变都可用作接收器调整本地位计数器的参考点(见「硬同步与再同步」小节)。

标准帧的位域排布(从 SOF 到 EOF):

packet-beta
    0-0: "SOF"
    1-11: "11 位标识符"
    12-12: "RTR"
    13-13: "IDE"
    14-14: "r0"
    15-18: "DLC"
    19-82: "数据场 0–8 字节"
    83-97: "15 位 CRC"
    98-98: "CRC 界定符"
    99-99: "ACK 槽"
    100-100: "ACK 界定符"
    101-107: "EOF"

3. 位仲裁与非破坏性多主

CAN 最独特的机制是仲裁:在同一物理总线、同一时刻,多个节点同时发起发送时不冲突、不重发,胜出方继续传输,失败方自动退避到下一帧。这是靠显性/隐性电平的不对称与"发送即回读"实现的。

3.1 仲裁窗与 ID 优先级

仲裁发生在仲裁场(标准帧前 11 位 ID + RTR + IDE,或扩展帧 29 位)。规则:

  • 每个候选发送节点一边发自己的位、一边回读总线上的实际电平。
  • 发出隐性位(1)但回读到显性位(0)——即总线上有其他节点同时发 0——立即退出仲裁、转入接收模式,等下一次总线空闲重试。
  • 发出显性位(0)永远与总线一致,继续。

结果:ID 数值最小的帧在仲裁中必赢。0x000 优先级最高,0x7FF 最低(标准帧);扩展帧同理在 29 位上比较。这就是"基于内容寻址的优先级系统"——不需要预分配时隙、不需要主站轮询,帧一发就带优先级。

3.2 线与机制与短路容忍

上面这套能成立,靠的是物理层"线与"(wired-AND):显性电平是低阻驱动、隐性电平是高阻释放。任一节点发显性都会把总线拉到显性;所有节点都发隐性时才读到隐性。这与 RS-485 推挽输出的差别就是关键——RS-485 两个节点同时发相反逻辑会打电流、可能烧器件;CAN 允许"冲突",冲突结果就是 0,且胜出方的 0 不会被 1 覆盖。

ISO 11898-2 规定显性电平 CAN_H − CAN_L ≥ 1.5 V、隐性 ≤ 0.5 V;总线两端 120 Ω 终端电阻下,显性时 CAN_H ≈ 3.5 V、CAN_L ≈ 1.5 V,隐性时两者均 ≈ 2.5 V。

3.3 远程帧与优先级反转防护

远程帧的坑:同 ID 的远程帧与数据帧在仲裁场上 RTR 位不同(远程帧 RTR 隐性、数据帧 RTR 显性)。同 base ID 时数据帧在仲裁场上多一个显性位、必然胜出远程帧。这在标准里被视为"同 ID 数据帧优先于远程帧"。若应用层让远程帧去请求一个高负载节点,可能形成"远程请求 → 数据帧 → 又一远程请求 → 又一次数据帧"的优先级反转链,其他中等优先级帧被挤压。工程惯例:

  • 不使用远程帧做数据拉取;改用"周期广播 + 订阅"模型(CANopen PDO 就是这样)。
  • 若必须用远程帧,为它分配一个独立的高优先级 ID(不是与被请求 ID 相同),并限制发送频率。

4. 位时序与采样点

位仲裁要求所有节点在同一时刻读到同一位——但总线传播时延、振荡器偏差与相位差使得不可能完美同步。CAN 用一套位时间分段与相位缓冲机制解决:每一位时间被切成四段,接收器在硬同步与再同步窗口内动态调整采样位置。

4.1 位时间的四段结构

一个位时间(Bit Time,\(t_{BT}\))由四段串成,各段长度以时间量子(Time Quantum,\(t_q\))为单位整数倍配置:

  • 同步段(Synchronization Segment,SYNC_Seg):1 \(t_q\),用于硬同步。
  • 传播段(Propagation Segment,PROP_Seg):1–8 \(t_q\),覆盖总线往返传播时延(\(2 \times t_{prop}\),含两端收发器与线路延时)。
  • 相位缓冲段 1(Phase Buffer Segment 1,PHASE_Seg1):1–8 \(t_q\),可通过再同步延长吸收相位滞后。
  • 相位缓冲段 2(Phase Buffer Segment 2,PHASE_Seg2):长度不小于信息处理时间(典型 2 \(t_q\)),可通过再同步缩短吸收相位提前。

采样点(sample point)位于 PHASE_Seg1 与 PHASE_Seg2 之间。位时间内其余时间都在为传播时延与相位容差留余量。

再同步跳跃宽度(Synchronization Jump Width,SJW):1–4 \(t_q\),且不超过 PHASE_Seg1,规定一次再同步最多能把 PHASE_Seg1 延长或把 PHASE_Seg2 缩短多少。

位时间总长通常取 8–25 \(t_q\):SYNC 固定 1 \(t_q\),PROP 与 PSEG1 各 1–8 \(t_q\),PSEG2 取 2–8 \(t_q\),SJW 1–4 \(t_q\) 且不大于 PSEG1。各控制器实现可在寄存器定义上收紧这些范围,具体上限以所用控制器手册为准。

4.2 硬同步与再同步

  • 硬同步(hard synchronization):总线空闲(隐性)转到 SOF(显性)的跳变触发接收器把内部位计数器归零,SYNC_Seg 对齐到跳变点。此后开始本位时间。
  • 再同步(resynchronization):帧内后续每次跳变,如果早于预期就在 SJW 范围内延长 PHASE_Seg1、如果晚于预期就缩短 PHASE_Seg2;调整幅度不超过 SJW。

这套机制让接收器在整帧期间能容忍一定的振荡器偏差(具体上限由位时序参数与 SJW 决定,Bosch 规范给出典型量级约 ±1.5%),配合位填充提供的密集跳变窗口维持位边界。

4.3 波特率预算:以 500 kbps 为例

\[f_{CAN} = \frac{1}{t_{BT}} = \frac{1}{(\text{SYNC} + \text{PROP} + \text{PSEG1} + \text{PSEG2}) \times t_q}, \quad t_q = \frac{\text{BRP}}{f_{sys}}\]

选:\(f_{sys} = 80\) MHz(很多 CAN 控制器主频)、\(\text{BRP} = 10\)、\(\text{SYNC} = 1\)、\(\text{PROP} = 8\)、\(\text{PSEG1} = 8\)、\(\text{PSEG2} = 7\)、\(\text{SJW} = 4\)。

  • \(t_q = 10 / 80\ \text{MHz} = 125\ \text{ns}\)
  • \(t_{BT} = (1 + 8 + 8 + 7) \times 125\ \text{ns} = 24 \times 125 = 3000\ \text{ns}\)
  • 位速率 \(= 1 / 3.0\ \mu s = 333.3\ \text{kbps}\)

要拿到 500 kbps,把 \(t_{BT}\) 缩到 20 \(t_q\):\(\text{BRP} = 8\)、\(\text{SYNC} = 1\)、\(\text{PROP} = 8\)、\(\text{PSEG1} = 7\)、\(\text{PSEG2} = 4\)。\(t_q = 8 / 80 = 100\) ns;\(t_{BT} = 20 \times 100 = 2000\) ns;位速率 500 kbps。

采样点位置 $= (\text{SYNC} + \text{PROP} + \text{PSEG1}) / (\text{位时间总 \(t_q\) 数}) = (1 + 8 + 7) / 20 = 80\%$。

采样点的常用区间是位时间的 75%–87.5%(ISO 11898-1 与常见控制器手册的推荐值),CiA 301 对标准位速率取 87.5%,80% 是通用取法。低于 70% 抗振荡器偏差能力不足、高于 90% 抗反射与延迟能力不足。

总线长度反推:PROP 段必须容纳两次单程传播时延(\(2 \times t_{prop}\))。PVC 双绞线传播速度约 \(5\ \text{ns/m}\);500 kbps 下 PROP = 8 \(t_q\) = 800 ns,\(2 \times 5 \times L \leq 800\) 得 \(L \leq 80\) m。ISO 11898-2 的参考曲线给 500 kbps 约 100 m,差异来自段长取值与收发器容差,实际布线需按所选控制器与收发器手册复核。

SocketCAN 命令行下配置这一组位时序:

## 运行环境:Ubuntu 22.04(Linux 5.15),can-utils 20210224;所用 CAN 接口以 can0 为例
## 与上方手算一致:tq 100 ns、(1 + 8 + 7 + 4) = 20 tq → 2000 ns → 500 kbps
sudo ip link set can0 type can bitrate 500000 \
     tq 100 prop-seg 8 phase-seg1 7 phase-seg2 4 sjw 2 \
     sample-point 0.800
sudo ip link set can0 up

ip link 直接接受四段参数;驱动会自行算 BRP,不同硬件 CAN 控制器的 BRP 上限可能迫使 tq 微调,用 ip -details link show can0 读回实际生效参数与手算对照。若只想给目标波特率与采样点、不关心中间段长度,可以只给 bitrate 500000 sample-point 0.800,内核默认方案也在推荐区间内;FD 接口另加 dbitrate 2000000 dsample-point 0.800 fd on。

5. 错误检测、错误帧与状态机

CAN 的可靠性来自五路并行错误检测加位级错误惩罚——一帧被任一检测机制判错,节点立即发错误帧污染本帧、其他节点跟着补错误帧,发送器退避重试;连续错误累积进 TEC/REC 计数器,超阈值降级到 error-passive 或 bus-off。

5.1 五种错误检测机制

检测 触发条件 谁检测
位监控(bit monitoring) 发送一位后回读发现不一致(仲裁段除外) 发送器自身
位填充(bit stuffing) 观察到连续 6 个同电平位(在非 EOF / IFS 场) 所有节点
CRC 校验 15 位 CRC 与本地重算不符 接收器
格式检查 固定形式位(EOF、IFS、ACK 界定符、CRC 界定符)不是规定的电平 所有节点
应答缺失(ACK error) 发送器在 ACK 槽读到隐性(无节点应答) 发送器

任何一项失败即判为错误,节点进入错误处理。

5.2 错误帧与过载帧

错误帧由两段组成:错误标志(Error Flag,连续 6 位显性或隐性,取决于发送者状态)+ 错误界定符(Error Delimiter,8 位)。两类错误标志:

  • 主动错误标志(Active Error Flag):error-active 节点发出,6 位显性;会把总线拉低让其他节点也检出错误。
  • 被动错误标志(Passive Error Flag):error-passive 节点发出,6 位隐性;不打扰其他节点的显性发送。

过载帧结构与错误帧类似,由过载标志(6 位显性)+ 过载界定符(8 位)组成,节点在需要额外帧间延时(比如接收缓冲来不及腾空)时发出。过载帧只在 EOF 之后与 IFS 期间被认可。

5.3 TEC / REC 与三态转换

每个 CAN 控制器维护两个计数器:发送错误计数器(Transmit Error Counter,TEC)与接收错误计数器(Receive Error Counter,REC)。规则:

  • 检出错误并作为发送器发主动错误标志 → TEC += 8(error-passive 下检出 ACK 错误不增加;发送主动错误标志期间检出位错误也不增加);
  • 作为接收器检出错误 → REC += 1;若该错误发生在接收器发送主动错误标志或过载标志期间(位错误) → REC += 8;
  • 一帧成功发送(ACK 正常) → TEC − 1(有下限 0);
  • 一帧成功接收且已应答 → REC − 1(REC 在 1–127 时减 1;大于 127 时置回 119–127 区间)。

三态由 TEC 与 REC 阈值定义:

  • error-active(TEC < 128 且 REC < 128):可正常发送,出错时发主动错误标志(显性 6 位),能打断其他节点。
  • error-passive(TEC ≥ 128 或 REC ≥ 128):仍能收发,但发被动错误标志(隐性 6 位),不打扰其他节点;成功发送一帧后需额外等待 8 位(suspend transmission)才能发起下一帧。
  • bus-off(TEC ≥ 256):控制器进入"电路断开"状态,不参与收发;只等外部复位或自动恢复条件。

这套惩罚机制保证:故障节点会先自己降级、再被打断发送、最后被关闭,健康节点受影响最小。

惩罚机制让故障节点先降级、再停发:

stateDiagram-v2
    state "error-active" as EA
    state "error-passive" as EP
    state "bus-off" as BO
    [*] --> EA
    EA --> EP: TEC ≥ 128 或 REC ≥ 128
    EP --> EA: 计数回落
    EP --> BO: TEC ≥ 256
    BO --> EA: 128 次总线空闲后复位

5.4 bus-off 恢复策略

bus-off 退出条件(ISO 11898-1 §12):连续观察到 128 次总线空闲(11 个隐性位序列) 后,控制器可回到 error-active(TEC/REC 清零);恢复策略由节点自己选:

  • 快速自动恢复:观察到 128 次空闲立即恢复。适合调试总线;生产环境可能导致故障节点反复上总线。
  • 慢速自动恢复:由上层协议或驱动器固件决定重试节奏,常见从数百毫秒到数秒的退避间隔。
  • 手动恢复:应用层显式复位(重启、NMT Reset)。适合需要人工确认故障的场景。

工程惯例:机器人关节驱动器多用慢速自动 + NMT 上报——bus-off 后节点自动重试、同时把自身故障状态经 EMCY 消息上报主站,让主站决定是否禁用。

6. 最小 SocketCAN 收发与错误观测

SocketCAN 是 Linux 内核对 CAN 的原生支持(内核 ≥ 2.6.25),把 CAN 接口暴露成网络套接字。以下示例展示发一帧、读一帧、看一次错误帧。运行环境:Ubuntu 22.04、Linux 5.15、can-utils 20210224;硬件用 PEAK PCAN-USB Pro FD 或 Kvaser Leaf Light v2 均可(USB-CAN backend 差异见第 9 章)。

## 建立 vcan0(虚拟 CAN,无真硬件也可完成收发;用于本地调试)
sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0

## 终端 A:挂一个监听器
candump vcan0

## 终端 B:发一帧
cansend vcan0 123##4deadbeef      # ID=0x123,DLC=4,数据 deadbeef

candump 输出:

  vcan0  123   [4]  DE AD BE EF

真实硬件上跑错误观测:

## 配置 500 kbps,up 起接口
sudo ip link set can0 type can bitrate 500000 dbitrate 2000000 fd on
sudo ip link set can0 up

## 只抓错误帧(-x 扩展帧格式,--loop 单机回环)
candump -e can0
## 或同时收数据帧与错误帧
candump can0,0:0,#FFFFFFFF

拔掉终端电阻、或把 CAN_H 与 CAN_L 短接一瞬,candump -e 会打出 can0 ERROR 行;ip -details -statistics link show can0 看到 state BUS-OFF、berr-cnt tx=256 rx=0——这是最直观的 bus-off 触发实验。

7. 要点

  • CAN 只到链路层:帧类型、位仲裁、位时序、错误处理在 ISO 11898-1,物理层电气在 ISO 11898-2/-6;应用层语义交给 profile。
  • 数据帧七场:SOF / 仲裁场 / 控制场 / 数据场 / CRC 场 / ACK 场 / EOF + IFS;NRZ + 位填充(连续 5 同电平插 1 反位)保证无时钟自同步。
  • 位仲裁靠"线与"实现:显性优先、发出隐性读到显性立即退出;ID 数值最小的帧必赢,非破坏性、失败方自动退避;CAN 是多主协议而非主从协议。
  • 位时间四段:SYNC / PROP / PSEG1 / PSEG2,采样点在 PSEG1 与 PSEG2 之间;推荐 75%–87.5%(工程常取 80%);PROP ≥ 2 × 单程传播时延决定总线长度上限(500 kbps 下约 100 m)。
  • 五种错误检测并行:位监控、位填充、CRC、格式、ACK;错误帧分主动/被动两种标志;TEC / REC 惩罚驱动 error-active → error-passive → bus-off 三态。
  • bus-off 恢复有快慢两路:连续 128 次总线空闲可退;工程上多用"慢速自动 + 上层 NMT/EMCY 上报"避免抖动节点反复上总线。

参考文献与延伸阅读

  • Linux Kernel. SocketCAN 文档. 文档
  • python-can. 文档. 文档
  • can-utils. 命令行工具仓库. 仓库