【通信协议】CAN 协议基础

1. 引言

由于 CAN 协议高可靠性、实时性强、多主通信、抗干扰能力强的特点,在汽车领域和工业控制领域应用的极为广泛。这篇文章就集百家之长,讲讲 CAN 协议的一些干货吧!

相关参考资料、视频:

2. CAN 协议特征

  • 双线差分信号线通信(CAN_H、CAN_L),无需共地,抗干扰能力强。
  • 高速 CAN(ISO11898):125 k ~ 1 Mbps,< 40 m。
  • 低速 CAN(ISO11519-2):10 k ~ 125 kbps,< 1 km。
  • 异步,无需时钟线;半双工,可挂载多设备广播通信,同一时刻只能有一台设备发送。
  • 多节点,CAN 总线上挂载的每一个设备都是一个主机节点。
  • 11 位/29 位报文 ID,用于区分消息功能同时区分优先级;可配置 1 ~ 8 字节有效载荷。
  • 可实现广播式和请求式两种传输方式;拥有应答、CRC 校验、位填充、位同步、错误处理等特性。

3. CAN 主流协议版本对比

这里指的是 CAN 主流版本的更新,当然其中也包含低速 CAN(10 k ~ 125 kbps)。

image

  • CAN 1.x / 2.0 之前:只有标准格式,IDE 位置当时是保留位 r1,没有标准/扩展的说法;
  • CAN 2.0:11 位 ID 不够用 → 扩展出 29 位 ID,并把 r1 赋予新功能变成 IDE。

4. CAN 总线硬件结构

4.1 CAN 总线的终端电阻

高速 CAN 使用闭环网络,CAN_H 和 CAN_L 两端添加 120 Ω 的终端电阻:
image

高速 CAN 总线的终端电阻作用:

  1. 防止回波反射。回波反射会导致当信号跳变时,它会在边沿振荡几下后才会回平;加上终端电阻后,信号跳变的波形就会非常平稳。
  2. 收紧电压。保证在没有设备操作总线的时候,将电压 CAN_H 和 CAN_L 拉至同一电平,保证 CAN 总线空闲逻辑电平为隐性电平 1。

低速 CAN 使用开环网络,CAN_H 和 CAN_L 两端添加 1.2 kΩ 的终端电阻:
image

4.2 CAN 协议电平标准

image

4.3 CAN 总线收发器

  1. 由于 MCU 上的 CAN 控制器由于在物理层上与 CAN 协议的电平标准和通信方式不同,此时就需要用 CAN 收发器(CAN Transceiver)来转换电平;
    将 MCU 的逻辑电平变成 CAN 的差分信号,增强抗干扰能力,还提供 ESD TVS 防静电的功能;
  2. CAN 收发器的型号推荐:TJA1050 和 TJA1042;
  3. 当 MCU 上的 CAN 控制器不足的时候,可以使用 SPI 转 CAN 控制器的模块,用 SPI 来驱动 CAN 协议总线。

image

5. 帧机制

5.1 帧类型

CAN 协议里的 5 类帧类型(CAN 自身定义的):
image

5.2 CAN 不同版本帧结构

CAN 2.0 版本和 CAN FD 版本的帧结构区别:
image

帧结构可以用来区分 CAN 的版本:CAN 2.0A / CAN 2.0B / CAN FD;帧类型是不同版本间都有的不是一个层级。

表1:CAN 2.0 经典基础帧(CBFF)关键位域详解

位域名称 长度 说明
SOF(Start Of Frame) 1 bit 帧起始,固定显性位0,同步所有节点,标记帧开始
11-bit identifier 11 bit 报文ID,仲裁优先级,ID值越小优先级越高
RTR(Remote Transmission Request) 1 bit 远程请求位:0=数据帧,1=远程帧(无数据段)
IDE(Identifier Extension) 1 bit 0=标准帧(本图Base Frame);1=扩展帧(29bit ID)
r0 1 bit 保留位,固定显性0,用于区分CAN2.0与CAN-FD
DLC(Data Length Code) 4 bit 数据长度码,取值0~8,代表数据段字节数
Data field 0~8 Byte 载荷数据,CAN2.0最大8字节
CRC 15 bit 循环冗余校验,校验SOF到数据段
CRC DEL(CRC Delimiter) 1 bit CRC分界符,固定隐性1
ACK Slot 1 bit ACK应答槽:发送方发隐性1;接收正确则拉低为显性0应答
ACK DEL(ACK Delimiter) 1 bit ACK分界符,固定隐性1
EOF(End Of Frame) 7 bit 帧结束段,连续7个隐性1,标记帧结束
IFS(Inter Frame Space) 3 bit 帧间隔,3个隐性1,帧与帧之间的间隔
Bus Idle - 总线空闲,持续隐性电平,可开始新帧

表2:CAN-FD 基础帧(FBFF)关键位域详解

位域名称 长度 说明
SOF(Start Of Frame) 1 bit 帧起始,显性0,同步节点
11-bit identifier 11 bit 标准ID,仲裁阶段优先级判定
r1 1 bit 保留位,固定0
IDE 1 bit 0=标准帧;1=扩展帧
EDL(Extended Data Length,FDF) 1 bit 扩展数据长度位:1=CAN-FD报文,0=CAN2.0报文
BRS(Bit Rate Switch) 1 bit 速率切换位:1=数据段切换高速波特率;0=全程仲裁波特率
ESI(Error State Indicator) 1 bit 错误状态指示:0=主动错误(Error Active);1=被动错误(Error Passive)
DLC(Data Length Code) 4 bit 数据长度码,支持0~64字节映射(非直接等于字节数)
Data field 0~64 Byte CAN-FD载荷,最大64字节
CRC 17 / 21 bit CRC校验:0~16字节用17bit CRC;17~64字节用21bit CRC
CRC DEL 1 bit CRC分界符,隐性1
ACK Slot 1 bit 应答槽,接收正确则拉低应答
ACK DEL 1 bit ACK分界符,隐性1
EOF 7 bit 帧结束,7个隐性1
IFS 3 bit 帧间隔,3bit隐性
Bus Idle - 总线空闲

5.3 CAN 2.0(Classical CAN)帧结构详解

5.3.1 数据帧

image

  • 灰色 = 显性 0(Dominant),必须发 0
  • 白色 = 隐性 1(Recessive),必须发 1
  • 紫色 = 显性或隐性,根据实际数据可 0 可 1
  • 半灰半白 = 应答位(ACK)特有:发送方必须发隐性 1,接收方必须发显性 0——发送方释放总线,接收方拉低总线

关键 bit 详解:
image

5.3.1.1 数据帧标准格式

1. SOF 帧起始(1 位,必须显性 0)
发送数据帧前总线必须处于空闲状态(隐性 1),所有设备都不碰总线。要开始发帧,第一位就必须“拉开总线”发出显性 0,打破空闲——这就是帧的开始(与串口的起始位思想相同)。如果第一位发隐性 1,就和空闲状态融为一体,没人知道你开始发数据了。
2. 仲裁段 = 11 位报文 ID + 1 位 RTR
- 报文 ID:表示后面数据的功能(总线上各种消息都有,没有 ID 就混了);同时用于区分优先级——多个设备同时发送时,按仲裁规则 ID 小的优先发送(仲裁规则在第 4 章细讲)。

  • RTR(远程请求标志位):数据帧必须为显性 0,遥控帧为隐性 1。
  • 类比 I²C:这里的“11 位 ID + 1 位 RTR”与 I²C 的“7 位从机地址 + 1 位读写位”异曲同工。区别在于 CAN 不分配从机地址、而是分配报文消息 ID,好处是同一条报文可被多个设备同时接收(真正的广播)。

3. 控制段 = IDE + r0 + 4 位 DLC

  • IDE(扩展标准位):区分标准格式(显性 0)还是扩展格式(隐性 1)。没有这一位,接收方就不知道该按哪种格式解析。
  • r0(保留位):必须显性 0。
  • DLC(数据长度代码,4 位):指定数据段有几个字节(1~8 字节可灵活指定),比如发 1 个字节 DLC 就给 0001。DLC 必须与数据段配套,不能 DLC 给 1、数据段却发 8 个字节。

4. 数据段(064 位,08 字节)
有效载荷。长度须为 8 的倍数(不能只给 7 位)。长度也可以是 0,但一般传输数据时都会大于 0。
5. CRC 段 = 15 位 CRC + 1 位 CRC 界定符(隐性 1)
CRC 是漏检率非常低的校验算法(对比:串口的奇偶校验漏检率很高、基本不用),对 SOF 到 DATA 的所有位计算校验码附在后面;接收方重算比对,不一致即判传输有误。CRC 由 CAN 控制器硬件自动生成和校验,不需要人工操心。
6. ACK 段 = ACK 槽 + ACK 界定符(各 1 位)
这是应答设计,与 I²C 应答又一曲同工:

  • 发送方发完主体数据后,在 ACK 槽释放总线(发隐性 1),总线回到隐性;
  • 若接收方正确收到了数据,就在 ACK 槽主动拉低总线(显性 0);
  • 发送方读回总线:读到显性 0 → “我释放了总线却读到 0,说明必有接收方回应”,安心,数据被正确接收;读到隐性 1 → 无人应答,发送失败,可配置自动重发或不管。
  • 两个注意点:① 一个报文可被多个设备同时接收,多个接收方可同时拉低总线(线与特性,输出显性 0 没问题);② 应答是夹杂在发送过程中的——发送方每发一位接收方就实时收到一位,整个帧还没发完接收方其实已收完,短暂交接总线控制权打上应答,之后发送方才发 EOF。ACK 槽前后的 CRC 界定符、ACK 界定符就是给“控制权交接”留出的释放时间。

7. EOF 帧结束(7 位隐性 1);
与串口停止位一个意思。

设计思路复盘:

想要波形承载数据 →
要有数据段;字节数可调 →
加 DLC;区分数据功能 →
前面加 ID;区分广播(写入)/请求(读取)→2026-10-29 23:50:26 星期四
RTR;判断传输是否正确 →
CRC;判断有没有人收 →
应答;为升级留余地 →
r0/r1 保留位;
最后前面套起始、后面套结束——整个帧的设计大功告成。

5.3.1.2 数据帧扩展格式

出现原因:标准格式 11 位 ID 不够用了,且必须兼容b标准格式。设计手法:

  • 前 11 位 ID 保持不变,后面再加 18 位 ID(合计 29 位);
  • 中间插入两位:SRR(替代远程请求位)——本来 RTR 应该在所有 ID 之后(仲裁时先比 ID、后比 RTR),原位置空出来了但也不能挪作他用,必须给隐性 1,以保证仲裁时标准帧优先级高于扩展帧;IDE = 1 表示扩展格式;
  • 接收方解析到 IDE 位时才判断格式:IDE=0 按标准格式解析,IDE=1 按扩展格式解析(IDE 之前两种格式无差异,无需提前区分);
  • 后面的 r1、r0、DLC、数据段、CRC、ACK、EOF 与标准格式完全照搬。

5.3.2 遥控帧

image

  • 一句话定义:无数据段,RTR 为隐性 1,其余与数据帧相同。
  • 使用场景:CAN 数据主要靠发送方定时广播。但若某个数据使用频率低、偶尔才集中用几次,定时广播要么浪费总线资源、要么该用时拿不到——这时就改为请求式:需要数据的设备先广播一个遥控帧(ID 表示想请求的数据),拥有该 ID 数据的设备再用相同 ID 的数据帧回发。一来一回两个帧,正好适合低频数据;高频数据就直接广播,不必用遥控帧浪费总线。
  • 优先级:相同 ID 下,数据帧优先级高于遥控帧。

5.3.3 错误帧 / 过载帧 / 帧间隔(了解即可)

错误帧:总线上所有设备都在监督总线数据,一旦发现位错误、填充错误、CRC 错误、格式错误或 ACK 错误,便发出错误帧破坏当前数据,并中止发送中的设备。

image

  • 主动错误状态(默认):检测到错误时连发 6 个显性位拉低总线。由于线与特性,总线只要有一个人拉低就必然显性,所以这 6 个显性位必然破坏掉正在传输的数据,告诉大家“这帧数据有错,都别用了”;
  • 被动错误状态:如果某设备错误太频繁(不可靠了),就降级为被动错误状态,检测到错误时只能发 6 个隐性位——不去碰总线,不破坏别人的数据,只破坏自己发的数据;
  • 错误标志后跟 8 位隐性 1 作错误界定符;且一个设备的错误标志可能引发其他设备连锁出错,标志位会延长 0~6 位。

过载帧:格式与错误帧类似,但产生条件不同——不是出错,而是发送方发太快、接收方处理不过来。接收方无法直接命令发送方降速,只好发一个类似错误帧的过载帧把数据破坏掉,发送方发不出去就会重试,数据在破坏与重试中被延缓,间接告诉发送方“慢一点”,以平衡总线负载、避免丢数据。

image

帧间隔:把数据帧/遥控帧与前面的帧分开。主动错误状态设备为 3 位;被动错误状态设备为 3 位 + 8 位延迟传送——不可靠的设备就别急着发数据,减少它对总线的干扰。

image

5.3.2 位填充(重要!)

发送方每连发 5 个相同电平后,自动追加 1 个相反电平的填充位;接收方检测到填充位后自动移除、恢复原始数据。即位填充是发送方的最后一步处理、接收方的第一步处理,双方真正收发的有效数据一致,不会出错。

image

  1. 出现连续 5 个 0 → 后面插一个 1;
  2. 1 00000 1111 00:5 个 0 后插 1,但插的 1 与原有 4 个 1 合并成了 5 个 1 → 填充位要和后续数据合并进行新的判定,再补一个 0(共两个填充位);
  3. 一长串 1:每满 5 个 1 就补一个 0,依次类推。

为什么要位填充(三大作用):

  1. 增加波形的跳变信息,利于接收方再同步。极端例子:数据段 8 个字节全为 0xFF,直接发就是总线连续 64 个 1、长时间完全不变,位与位之间没有分界点,接收方时钟稍有偏差就可能数成 65 或 63 个 1,也无法利用跳变沿做再同步误差补偿。经位填充后波形最多连续 5 个相同位,跳变频繁,采样点才抓得准。
  2. 把正常数据流与错误帧/过载帧区分开。错误帧/过载帧的标志是连续 6 个相同电平;而正常数据经位填充后不可能出现连续 5 个以上相同位,所以 6 连同电平一出现,立刻就能识别出是错误帧/过载帧叠加在数据帧上。
  3. 保持总线活跃,防止被误判为空闲。CAN 规定总线出现**连续 11 个隐性 1 **即视为空闲。若没有位填充,数据段最多可能连续 64 个 1,其他设备会误以为总线空闲而插进来发送,产生冲突。有了位填充,正常发帧时不可能连续超过 5 个 1,就不会被误判。

5.4 波形示例

测试方法:示波器两个探头,地端与 STM32 共地,探头分别接** CAN_H 和 CAN_L**,测对地电压、观察差分波形;按键触发 STM32 发出指定帧,再在波形上画每一位的判定线解析。

标准数据帧,报文ID为0x555,数据长度1字节,数据内容为0xAA:
image

例 1:标准数据帧,ID=0x55,DLC=1,数据=0xA1
空闲时两线收紧;SOF 拉开发显性 0 打破空闲 → 11 位 ID(0x55,注意 11 位转十六进制要按 3-4-4 分组)→ RTR=0(数据帧)→ IDE=0(标准帧)→ r0=0(保留位给 0 也不随便给:仲裁时 0 优先级更高,将来保留位启用后,现有帧的优先级仍高于新扩展定义)→ DLC=0001(此处波形出现了填充位:因为前面已连续 5 个 0,硬件自动插了一个 1,解析时需移除)→ 数据段 1010 0001 = 0xA1(可见 CAN 是高位先行)→ 15 位 CRC(里面同样有填充位;找填充位的诀窍——先找到连续 5 个相同电平,紧随其后的那位必是相反的填充位;若 5 个相同电平后还是相同电平,那就是 6 连,即错误帧/过载帧)→ CRC 界定符(隐性 1,让发送方在 ACK 槽前释放总线)→ ACK 槽显性 0(这一位不是发送方拉的,是接收方拉的,说明有设备应答了)→ ACK 界定符(接收方释放总线、交还控制权)→ 7 位 EOF 隐性 1。

标准数据帧,报文ID为0x666,数据长度2字节,数据内容为0x12, 0x34:
image

例 2:标准数据帧,ID=0x666,DLC=2,数据=0x12 0x34
与例 1 对比演示数据段长度可变:DLC=0010,数据段就有两个字节;若 DLC 为最大 8,波形还会更长。

扩展数据帧,报文ID为0x0789ABCD,数据长度1字节,数据内容为0x56:
image

例 3:扩展数据帧,29 位 ID
SOF → 11 位基本 ID → SRR=1、IDE=1(从这里起按扩展格式解析)→ 18 位扩展 ID,前后拼成完整 29 位 ID。特别提醒一个易错点:29 位不是 4 的整数倍,所以扩展 ID 范围是 0x0000000 ~ 0x1FFFFFFF(0x6789ABCD 超范围);同理标准 11 位 ID 范围是 0x000 ~ 0x7FF。之后 RTR=0、r1/r0=0,其余与标准帧一样。

标准遥控帧,报文ID为0x088,数据长度1字节,无数据内容:
image

例 4:标准遥控帧
RTR=1(遥控帧标识),IDE=0,DLC 之后没有数据段,直接跟 15 位 CRC,最后 ACK 和 EOF。波形里正好演示了位填充的连锁特性:5 个 0 后插 1,使原本的 4 个 1 变成 5 个 1,于是又得再补一个 0。遥控帧最核心的还是 ID——表示“我要请求这个 ID 的数据帧”。

字段 位数 含义
SOF 1 帧起始,显性 0,打破总线空闲
ID(仲裁段) 11(标准)/ 11+18=29(扩展) 区分报文功能,决定优先级(ID 小者优先)
RTR 1 数据帧=显性 0,遥控帧=隐性 1
SRR 1 仅扩展格式有,必须隐性 1,保证标准帧优先级高于扩展帧
IDE 1 标准格式=0,扩展格式=1
r0 / r1 各 1 保留位,必须显性 0,为升级留空间
DLC 4 数据段长度 1~8 字节
Data 0~64 有效数据,高位先行
CRC 15 + 1 界定符 低漏检率校验;界定符给 ACK 前释放总线留时间
ACK 1 槽 + 1 界定符 接收方在槽内拉低总线应答;可多设备同时应答
EOF 7 帧结束,隐性 1

某个设备想发送数据时,就操作总线拉开/释放,产生一段规定格式的波形;发送方按帧格式定义发,接收方按同样定义接收解析,所有 CAN 设备都遵守 CAN 协议,彼此才能互相理解波形的意义。

5.5 本章总结

总结:某个设备想发送数据时,就操作总线拉开/释放,产生一段规定格式的波形;发送方按帧格式定义发,接收方按同样定义接收解析,所有 CAN 设备都遵守 CAN 协议,彼此才能互相理解波形的意义。

6. 数据位采样同步

发送方在总线上产生波形后,接收方如何准确地采样到每一个数据位是 1 还是 0?

6.1 数据采样

image

  1. CAN 总线没有时钟线,属于异步通信。所有设备通过约定波特率的方式确定每个数据位的时长;
  2. 波特率必须由编程者在程序中指定,且所有设备保持一致。例如约定 1Mbps,则所有设备都认为一个数据位时长 1μs——发送方每隔 1μs 输出一位,接收方每隔 1μs 采样一次总线电平;
  3. 传输波形时每一位时长都一致,中途不能有任何停顿(不能发一半歇会儿);
  4. 理想状态:接收方的采样点位于数据位中心附近。采样点位置其实可配置(偏前/偏后),实际项目一般配在中心偏后一点,因为此时波形更稳定。

6.2 数据采样问题

接收方以约定的位时长进行采样,但是采样点没有对齐数据位中心附近:
image

假设约定 1Mbps,双方位间隔都正确,但接收方每次采样点恰好都踩在数据位的跳变沿上(或离跳变沿太近)——在跳变沿上采样,是 1 还是 0 根本无法确定,数据完全不可靠。
解决思路:以第一个跳变沿为参考,延迟约半个数据位再做第一次采样,之后按固定间隔采样即可。
→ 这就是后面“硬同步”的核心思想。

接收方刚开始采样正确,但是时钟有误差,随着误差积累,采样点逐渐偏离:
image

第一个采样点对齐了,但收发双方时钟有误差。例如发送方每 1μs 发一位、接收方每 1.05μs 才采一位,每次差一点点,误差不断累积。CAN 一帧数据最多上百个数据位,即使时钟只偏差一点点,累积到最后采样点也会偏得无法忍受,导致采样出错。误差来源很实际:有些设备时钟不够稳定,温度一变就偏一点。
解决思路:以正常数据位的跳变沿为参考,一旦察觉采样点有偏离的苗头——

  • 采样太慢了 → 把下一次的采样间隔缩短一点,后面的采样点整体往前提;
  • 采样太快了 → 把一次采样间隔延长一点,整体往后拖。

→ 这就是“再同步”的核心思想。

6.3 位时序(实现同步的前置设计)

为了灵活调整每个采样点的位置,使采样点对齐数据位中心附近,CAN总线对每一个数据位的时长进行了更细的划分,分为同步段(SS)、传播时间段(PTS)、相位缓冲段1(PBS1)和相位缓冲段2(PBS2),每个段又由若干个最小时间单位(Tq)构成:

image

要能把采样点灵活地往前/往后挪一点,就必须把一个数据位的时间再细分,这就是位时序。CAN 把一个数据位划分为 4 个段,每段由若干个最小时间单位 TQ 构成:

段 名称 可配置范围 作用
SS 同步段 固定 1 TQ 判断同步的“标尺”:数据跳变沿正好落在 SS 段内,说明设备与波形同步;不在,就要调整位时序使其回到 SS 段(这就是“同步过程”)
PTS 传播时间段 1~8 TQ 吸收网络上的物理延迟(发送单元输出延迟 + 总线信号传播延迟 + 接收单元输入延迟),PTS 时长为这些延迟之和的两倍。信号刚变化后稍微等一等再采样
PBS1 相位缓冲段 1 1~8 TQ 与 PBS2 一起决定采样点位置
PBS2 相位缓冲段 2 2~8 TQ 同上

几个要点:

  • TQ 是最小时间单位,在程序中自己指定(例如 1 TQ = 0.5μs);
  • 采样点位于 PBS1 和 PBS2 的交界处:PBS1 长、PBS2 短 → 采样点靠后;PBS1 短、PBS2 长 → 采样点靠前;
  • 示例:一位 10 TQ(SS=1,其余各 3)采样点居中;PTS/PBS1/PBS2 配 2/3/4 → 采样点靠前;配 4/3/2 → 靠后;
  • 实际项目中各段 TQ 数一般有统一规定,按项目要求来;波形很稳定时,采样点偏前偏后影响不大。

6.4 硬同步(初始位置同步)

解决的问题:让接收方第一个采样点与波形第一位对齐,防止第一次采样踩在跳变沿附近。

image

“秒表”比喻:每个设备内部都有一个位时序计数周期,把它想象成一块秒表,转一整圈正好是一个数据位的时间。

  • 最佳状态:所有设备的秒表同步转动——发送方在自家表转到 SS 段时开始新的一位(所以发送方的每个跳变沿都诞生于它的 SS 段);接收方在自家表转到 PBS1/PBS2 之间时采样。只要大家的表同步转,采样点必然落在数据位中心附近。

硬同步规则:

  • 每当某设备发出报文,所有接收方收到 SOF 的下降沿(逻辑电平下降沿,即差分电压“张开”沿,也就是帧的第一个数据跳变沿)时,直接把自己的手表拨到 SS 段,与发送方对齐——就像发送方鸣响一发信号枪,全体对表;
  • 硬同步只在帧的第一个下降沿(SOF 下降沿)有效;
  • 若硬同步后所有设备时钟毫无误差,一次初始对表之后大家永远同步,后面每个采样点都稳稳落在数据位中心附近。

6.5 再同步(重新同步,补偿时钟误差)

解决的问题:实际中每块“手表”都有误差,转久了又会不同步,采样点逐渐漂移(即问题 2)。若所有设备时钟绝对精准,就根本用不到再同步。

image

参考基准:SOF 之后每一个普通数据位的跳变沿都是再同步的机会。接收方观察波形跳变沿落在自己位时序的什么位置:

  • 跳变沿出现在 SS 段之后 → 说明接收方的表跑得比发送方快(自己以为该跳变了,波形还没跳)。注意这不一定是接收方时钟的问题,也可能发送方时钟慢了——但波形是发送方发出的,它不会改自己时钟,误差补偿只能由接收方做。做法:把 PBS1 加长 SJW 指定的宽度,下一轮 SS 段整体后移,让新的跳变沿退回 SS 段;
  • 跳变沿出现在 SS 段之前 → 接收方的表跑慢了。做法:把 PBS2 缩短 SJW 指定的宽度,让 SS 段提前,数据边沿回到 SS 段。

呼应 P2 的位填充:这里正好解释了上一章位填充的作用——防止波形长时间无变化。没有跳变沿就无法执行再同步,时钟误差长时间得不到补偿,累积起来就会接收出错。

6.5.1 SJW(再同步补偿宽度)

SJW 可配置 1~4 TQ,相当于手表的“时间补偿选项”(快了减 10 秒、慢了加 10 秒)。

SJW 是每次补偿的最大宽度,不是固定补偿量。实际补偿多少由误差大小和 SJW 共同决定:

  • 误差 ≤ SJW:误差几个 TQ 就补几个 TQ(例:误差 2TQ、SJW=3TQ → 实补 2TQ);
  • 误差 > SJW:无论误差多大,只补 SJW 指定的 TQ 数(例:误差 2TQ、SJW=1TQ → 只补 1TQ)。

为什么要设上限:防止噪声对位时序造成过大影响。如果补偿不设限,正常一位波形中的电平抖动(毛刺)可能被接收方理解成“两位甚至三位数据、只不过位时间误差特别大”,原本一位被收成多位,解析二进制数据流必然出错。所以波形传输开始后,位时序的调整必须保守、有最大限制。

配置建议:设备质量好、时钟误差小 → SJW 设小些(12TQ);设备质量差、时钟误差大 → 设大些(34TQ)。SJW 太小可能补不过来,太大则有接收持续混乱、后续数据出错的风险,需按实际情况权衡。

6.6 波特率计算

  • 术语澄清:波特率本来的单位是波特(码元/秒),bps 是比特率的单位;但二进制调制下两者数值相等,所以工程上经常混用,知道意思即可;
  • 公式:波特率 = 1 ÷ 一个数据位的时长,而位时长 = SS + PTS + PBS1 + PBS2 各段时长之和(每段 = TQ 数 × TQ 时长);

算例:SS=1TQ、PTS=3TQ、PBS1=3TQ、PBS2=3TQ、1TQ=0.5μs
→ 位时长 = (1+3+3+3)×0.5μs = 5μs
→ 波特率 = 1/5μs = 200kbps

6.7 本章总结

总结:本章围绕“接收方如何准确采样每个数据位”——CAN 靠约定波特率确定位时长;用硬同步(以 SOF 第一个下降沿为参考,全体设备把位时序拨到 SS 段)解决初始对齐问题;用再同步(以普通数据位跳变沿为参考,加长 PBS1 或缩短 PBS2,补偿量受 SJW 上限约束)解决时钟误差累积问题;而这一切的基础是把每个数据位细分为 SS/PTS/PBS1/PBS2 四段的位时序设计。

7. 仲裁

多个设备同时想发送数据时,总线资源如何分配?

7.1 多设备同时发送的问题

CAN 总线只有一段差分信号线,同一时间只能有一个设备操作总线。若设备 A 正在发波形的中途,设备 B 也开始发自己的波形,那么总线上的实际波形就是两者按线与特性叠加的结果——只要有任何一个设备拉开总线,总线就呈现显性 0;只有所有设备都释放总线,才是隐性 1。叠加的结果是两个波形必然都损坏,谁的数据都发不出去。所以 CAN 必须设计资源分配规则。

image

7.1.1 解决思路

冲突情况 描述 对应规则
情况 1 一个设备发送中途,另一个设备想开始新发送(时间片重叠、但不是同时开始) 规则一:先占先得
情况 2 两个设备同一时刻开始发送(没法按先来后到判定) 规则二:非破坏性仲裁

情况 1 简单,——直接不允许中途插入;情况 2 复杂,CAN 用一条巧妙的“非破坏性仲裁”规则解决:既能决出高下,又不影响胜利方的正常数据传输。

7.2 规则一:先占先得

  1. 若已有设备正在发送数据帧/遥控帧,其他任何设备不能同时发送。中途再着急也不能打断它。
    • 特例:错误帧/过载帧可以破坏当前数据——它们本来就是用来破坏数据的。
  2. 总线空闲的判定:任何设备检测到连续 11 个隐性电平,即认为总线空闲,只有空闲时才能开始发送数据帧/遥控帧。
  3. 为什么正在发帧时不会出现“连续 11 个隐性 1”?因为发送的波形都经过位填充,总线波形不可能出现连续 5 个以上相同位,更不可能有 11 个连续隐性 1。这里第三次呼应了位填充的作用:保持总线在发送正常数据流时的活跃状态,防止被误判为空闲(否则数据段 8 个 0xFF 会造成连续 64 位的隐性 1,其他设备误判空闲、插进来发送就冲突了)。
  4. 等待时机的算例:设备 B 等总线空闲,从帧尾开始数隐性位——ACK 界定符(1 位隐性)+ EOF(7 位隐性)+ 帧间隔(3 位隐性)= 1+7+3 = 11 位,正好凑满总线空闲条件。
  5. 但注意:总线重新空闲时,可能 B、C、D 都在等待,它们会同时蜂拥而上——这就变成了“因等待而同时到来”的冲突,交给规则二处理。

7.3 规则二:非破坏性仲裁(核心)

基本规则:
多个设备的发送需求同时到来(或因等待而同时到来)时,CAN 协议根据 ID 仲裁段进行非破坏性仲裁:ID 小的优先级高,取得总线控制权;ID 大的仲裁失利,转入接收状态,等下次总线空闲再尝试发送。

  • 仲裁的依据不只是 ID,严格说是仲裁段 = ID + RTR;
  • “同时到来”两种情形:①真同时;②因等待而同时到来(更普遍——总线繁忙、设备多时,一个帧结束的瞬间常有多个等待设备同时竞争,仲裁经常发生);
  • 由此得出工程实践:重要的消息分配小 ID(冲突时优先发送),不重要的消息分配大 ID(冲突了就多等等)。

7.3.1 实现非破坏性仲裁的两个硬件要求

  1. 线与特性
    任何一个设备发显性 0,总线就是显性 0;只有所有设备都发隐性 1,总线才是隐性 1(即 0&X&X=0,1&1&1=1,与逻辑“与”一致,故称线与)。
    这不是凭空规定的,而是电路行为的自然结果:显性 0 = 拉开总线,隐性 1 = 释放总线,总线默认收缩在隐性 1 状态——只要有一个人拉,总线必然显性。

  2. 回读机制
    每个设备每发出一个数据位,都会立刻读回总线当前电平,确认自己发的电平是否被真实发送出去。规律:发 0 读回必然是 0;发 1 读回不一定是 1。若“发 1 读回 0”,说明有别的设备正在和自己抢总线(自己的 1 被别人的 0 破坏了)→ 主动退出竞争。

7.3.2 非破坏仲裁流程

仲裁规则一句话:数据位从前到后依次比较,出现差异且该位为 1 的设备仲裁失利。

image

图中单元一、单元二同时开始发波形(不同时开始的冲突已被先占先得规则排除),第三行为总线实际波形:

  1. SOF:双方都发显性 0 → 线与后总线 0 → 双方回读 0,与自己发的一致 → 继续;
  2. ID 逐位比较:只要双方该位相同(同为 0 或同为 1),总线电平就与各自发送一致,双方回读都正确,继续;
  3. 决出胜负的一位:单元一该位 ID 为 1(释放总线),单元二该位 ID 为 0(拉开总线)→ 线与后总线呈现 0:
    • 单元二:发出 0、读回 0,一致,毫无察觉,继续发送;
    • 单元一:发出 1、读回却是 0!感知到别的设备存在、自己的数据已被破坏 → 仲裁失利,转为接收状态,等下次总线空闲再重试;
  4. 之后单元二继续“发一位、回读一位”,直到发完整帧。

为什么失利的是单元一而不是单元二? 两个角度:
①数据正确性——单元一的数据已被破坏,继续发没有意义;
②设备感知——单元一“发 1 读 0”感知到了竞争者所以退出;单元二每次发出与读回一致,根本感知不到别人的存在。所以单元一让出总线,合情合理。

7.3.3 三个以上设备同时开始怎么办?

没问题。每个设备都执行“发一位、回读一位”,回读不一致就退出。因为每条消息的仲裁段必然不同,逐位比较中设备会陆续主动退出,仲裁段结束时必然只剩一个设备,后续数据位不会再冲突。

7.3.4 为什么 ID 小的优先级高?

ID 越小,二进制里的 1 出现得越晚;而“出现差异且发 1 者失利”。极端例子:ID 全 0 的帧一冲突就赢,ID 全 1 的帧一冲突就输。所以 ID 越小优先级越高是这套机制的自然结果。

7.3.5 仲裁段里有位填充,会不会影响仲裁结果?

位填充的作用范围是帧起始、仲裁场、控制场、数据场、CRC 序列,所以仲裁场内确实会插入填充位(例如 11 位 ID 全 0,每 5 个 0 就会插一个 1,变成两个 00001 段,11 位变 13 位),填充位也参与仲裁,但不会改变仲裁结果和 ID 优先级。证明思路很巧妙:若填充位会改变 A、B 两帧的优先级,则填充位之前的位 A、B 必须完全一样;而前面的位一样,A、B 就得执行完全相同的填充——填充位也就不可能影响优先级了。(找不到“不填充时 A 优先、填充后 B 优先”的反例。)

7.3.6 为什么叫“非破坏性”仲裁?

从胜利者视角:仲裁段每一位发出与读回都相同,它根本没感受到任何竞争者,发送过程没受仲裁的丝毫影响——数据无损,所以“非破坏性”。从失败者视角:它伴随胜利者走过一段时光,数据位相同就还能同行一段,一旦分岔(对方发 0 自己发 1)就默默退出,最终只有胜利者独自走出仲裁段。

7.4 数据帧 vs 遥控帧(ID 相同时)

相同 ID 下,数据帧优先级高于遥控帧。原因是 RTR 的定义:数据帧 RTR=0,遥控帧 RTR=1。

image

典型场景:A 设备发遥控帧请求某 ID 的数据,B 设备恰好同时发送这个 ID 的数据帧。11 位 ID 完全一致分不出胜负,直到 RTR 位:遥控帧发 1、数据帧发 0 → 线与后总线为 0 → 遥控帧“发 1 读 0”仲裁失利退出,数据帧继续。

  • 注意前提是 ID 相同;ID 不同就直接按 ID 大小分。
  • 协议同时规定:相同 ID 的两个数据帧不允许同时出现,相同 ID 的两个遥控帧也不允许同时出现(仲裁段完全相同就永远分不出胜负)。
  • 合理性:我想请求的数据正好已经在路上了,理应让数据帧先行。

7.5 标准格式 vs 扩展格式(标准帧 11 位 ID 与扩展帧高 11 位 ID 相同时)

标准格式优先级高于扩展格式,由 SRR 必须始终为 1 保证。

image

  • 例 1(标准数据帧 vs 扩展数据帧):前 11 位 ID 相同 → 到下一位:扩展帧发 SRR=1,标准帧发 IDE=0 → 总线为 0 → 扩展帧“发 1 读 0”失利退出,标准帧获胜。
  • 例 2(最惊险的情况:标准遥控帧 vs 扩展数据帧,前 11 位 ID 相同):标准遥控帧 RTR=1,扩展数据帧 SRR=1——这一位都是 1,分不出胜负!而且标准遥控帧的仲裁段(11 位 ID + RTR)到此就结束了。幸运的是设计上并未出错:下一位,标准遥控帧发 IDE=0(它是标准帧),扩展数据帧发 IDE=1(它是扩展帧)→ 扩展数据帧“发 1 读 0”失利退出。此时标准遥控帧虽然已经不再执行仲裁,但扩展数据帧仍在仲裁,正是在 IDE 位决出了胜负。

7.6 本章总结

总结:CAN 靠两条规则分配总线——先占先得(检测连续 11 个隐性 1 判空闲;发帧中途不允许打断,类比“只有响应优先级、没有抢占优先级”)+ 非破坏性仲裁(同时竞争时,靠“线与特性 + 回读机制”,按仲裁段 ID 逐位比较,发 1 读 0 者自动退出;ID 小者优先、数据帧优先于同 ID 遥控帧、标准帧优先于高 11 位相同的扩展帧;位填充不改变仲裁结果)。

8. 错误处理

当某个设备检测到帧传输出错时,如何通知全网、如何处置“惹祸”的设备。

8.1 为什么需要错误处理

错误处理是 CAN 总线额外的安全保障机制:某设备检测到总线的帧传输出错时,主动发出错误帧,通知每个设备“这帧错了”,大家都丢掉这帧,不受错误数据影响。没有错误处理 CAN 也能完成数据收发,但在干扰很强的实际环境中,数据位随时可能出错——出错后如何善后,是很重要的设计。

8.2 CAN 规定的 5 种错误类型

image

几个要点:

  • 位错误就是上一章讲的回读机制的直接应用(发一位、读回比对)。但有两类特例不算位错误:①仲裁段发 1 读 0——那是仲裁失利;②应答位发送方发 1 读 0——那被视为接收方的应答。
  • 位错误大部分由发送单元检测(总线大部分时间是发送方在操作总线);接收单元只在发应答、输出错误帧时才输出数据,才有可能检测位错误。
  • CRC 校验只有接收方做(只有接收方有校验需求)——发送方算好 CRC 附在帧尾,接收方对 SOF 到数据段的二进制流用同样算法重算比对。

8.3 错误通知及其隐患

错误通知:任何单元检测到上述错误时,紧接着主动发出错误帧,破坏当前总线上的数据——不管其他设备有没有发现,“反正我发现了,我破坏这帧,大家都别收”。错误帧一旦产生,这帧数据作废、传输终止;错误帧结束后总线回归空闲,谁想发谁再开始新帧。

这样每个设备都是“检查官”:一个或多个设备发现错误就发错误帧通知,即便某些设备自己没发现,收到错误帧后也得知有错——避免错误数据被使用。

隐患:错误通知赋予了每个设备破坏传输的能力。如果有个设备“抽风”了——无论收到什么都认为是错的,本来没问题也报错——它就会不停地破坏数据,总线上正常传输都无法进行。所以必须给错误通知加限制措施,这就是错误状态。

8.4 错误状态(帧格式复习 + 三态设计)

先复习第 2 章中帧机制讲到的错误帧:

  • 错误帧 = 6 位错误标志 + 8 位错误界定符;
  • 主动错误标志 = 6 个显性 0:线与特性下会破坏总线数据(一直把总线拽住不放,谁的数据都得坏);
  • 被动错误标志 = 6 个隐性 1:不影响别人的波形(发 1 就是释放总线),只会让自己的帧作废;
  • 帧间隔:数据帧/遥控帧之间保留 3 位;被动错误状态的设备还要再加 8 位延迟传送。

image

8.5 三种错误状态与权利

状态 参与通信 检测到错误时 说明
主动错误状态(初始态) ✅ 发主动错误帧(6 个显性 0),可以破坏别人数据 权力大,需可靠
被动错误状态 ✅ 只能发被动错误帧(6 个隐性 1),不能破坏别人数据 不可靠者收回破坏权
总线关闭状态 ❌ 不能参与通信 “关小黑屋冷静一会儿”

8.6 判定依据:TEC / REC 错误计数器

每个设备内部维护 TEC(发送错误计数器) 和 REC(接收错误计数器):发送/接收时每发现一个错误就 +,每完成一次正常发送/接收就 −。计数值反映了该设备检测错误的相对频率,据此判断设备是否可靠。

image

状态转移规则:

  • 主动 → 被动:TEC > 127 或 REC > 127(错误报告太频繁了);
  • 被动 → 主动:TEC < 128 且 REC < 128(变好了,恢复权限);
  • 被动 → 总线关闭:TEC > 255。注意只看 TEC、与 REC 无关——REC 大只说明接收有问题,只要它不发数据、不破坏别人,影响不大;只有“不可靠还总发数据、发的还总错”的设备才值得关掉;
  • 总线关闭 → 主动:检测到 128 次连续 11 位的隐性 1 → 满血复活,TEC/REC 清零,从头再来。这个“生路”的权衡:关久了说不定已经好了;不放出来,它的数据可能很重要;放出来没好,就再走一遍这个流程。

8.6.1 TEC/REC 加减细节

增加规则(除第一条外都是 +8):

  1. 接收单元检测出错误 → REC +1(小错误);
  2. 接收单元发送完错误标志后检测到的第一位为显性电平 → REC +8(实践中的经验规则,了解即可);
  3. 发送单元输出错误标志时 → TEC +8;
  4. 发送/接收单元在发送主动错误标志或过载标志时检测出位错误 → TEC/REC +8(“你发错误帧通知别人出错,结果发错误帧这个行为本身还出错,太不可靠了”);
  5. 另有两条罕见情况,发送 +8 / 接收 +8(了解即可)。

减小规则:

  • 发送单元正常发送结束 → TEC −1(减到 0 不再减);
  • 接收单元正常接收结束 → REC 在 1~127 之间 −1(减到 0 为止);REC > 127 时直接减到 127——协议对接收错误更宽容。

权衡算术:错一次 +8、对一次只 −1,一次错误要用 8 次正常传输才能弥补——错误计数器能容忍的错误其实不多。另有一个巧妙设计:若设备是因 REC > 127 进入被动错误状态的,只要完成一次正确接收,REC 直接减到 127,立刻恢复主动状态。

8.7 示波器实测波形(以 ACK 错误为例)

实验技巧:位错误、填充错误、CRC 错误、格式错误都很难人为产生(CAN 控制器发波形是全自动的,没法手动让它出错),但 ACK 错误极易控制——总线上只挂一个设备,它发送数据必然无人应答。

设备处于主动错误状态,发送标准数据帧,正常传输:
image

波形 1:正常发送(对照组)
标准数据帧正常传输,ACK 槽被接收方拉为显性 0,7 位 EOF 结束后再加 3 位帧间隔才能开始下一帧。帧间隔主要是给过载帧准备的:接收方若过载,会在帧间隔的第一位拉开总线产生过载帧。另一个呼应点:ACK 界定符(1) + EOF(7) + 帧间隔(3) = 11 位,正好凑满“连续 11 个隐性 1 = 总线空闲”。

设备处于主动错误状态,发送标准数据帧,检测到ACK错误:
image

波形 2:主动错误状态下的 ACK 错误
单设备发帧 → ACK 槽无人拉低,保持隐性 1 → 发送方检测到 ACK 错误 → 在 ACK 槽的下一位发出主动错误帧:6 个显性 0 的错误标志 + 8 个隐性 1 的错误界定符。正常波形不可能出现连续 6 个 0,所以一看到 6 连 0 就知道是错误帧,叠加了错误帧的波形作废。EOF 给 7 位:为错误标志的叠加留出空间——EOF 太短的话,错误标志还没检测完数据帧就结束了,接收方不好处理。错误界定符(8) + 帧间隔(3) 同样 = 11 位,凑满空闲判定。

设备处于被动错误状态,发送标准数据帧,检测到ACK错误:
image

波形 3:被动错误状态下的 ACK 错误(状态转移实测)
单设备不断发帧、始终无应答:前 16 次是主动错误波形;之后波形突变。原因:每次输出错误标志 TEC +8,16 次后 TEC = 128 > 127,设备转入被动错误状态。被动状态波形:ACK 场无应答 → 输出被动错误标志(6 个隐性位)→ 8 位错误界定符 → 3 位帧间隔 → 再加 8 位延迟传送 → 才能开始下一帧。附带的公平效应:主动设备和被动设备同时想发数据时,两者虽同时检测到 11 个隐性 1(空闲),但被动设备要等 8 位延迟传送,根本赶不上主动设备的脚步——主动设备直接取得总线控制权,连仲裁都不会发生。

更复杂的情况(略过):实际中多个设备都可能输出错误帧、错误种类多、各错误帧互相叠加造成错误标志延长——实验室条件下很少发生,初学用不到,留待实践中理解。

至此,CAN 总线协议理论知识全部结束。

博客导航

博客导航

posted @ 2026-10-28 22:28  膝盖中箭卫兵  阅读(9)  评论(0)    收藏  举报
ORCID iD icon https://orcid.org/0000-0001-5102-772X