第 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_{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 上报"避免抖动节点反复上总线。