本篇文章参考自:
Vector 中国官方自制
1、CAN 协议发展历史
1987 年,Bosch 发布了 CAN 1.0 规范,定义了基本的 CAN 协议,包括多主控制、仲裁机制、错误检测等特性。
1988 年,飞利浦(Philips,现 NXP)推出了世界上第一款 CAN 控制器芯片——82C200,推动了 CAN 技术的实际应用。
1991 年,Bosch 发布 CAN 2.0 规范,分为:
- CAN 2.0A(标准帧):11 位标识符(最多支持 2048 个节点)
- CAN 2.0B(扩展帧):29 位标识符(支持更多设备,增强兼容性)
1993 年,国际标准化组织(ISO) 正式将 CAN 定为国际标准:
- ISO 11898-1(数据链路层与物理信道)
- ISO 11898-2(高速 CAN,总线速率可达 1 Mbps)
- ISO 11898-3(低速容错 CAN,适用于更高抗干扰需求的场景)
随着汽车电子和工业自动化的发展,传统 CAN 2.0 的 1 Mbps 速率限制 成为了瓶颈。
2012 年,Bosch 发布 CAN FD(Flexible Data-rate,灵活数据速率):
- 数据传输速率从 1 Mbps 提升至 5 Mbps 或更高
- 数据帧长度从 8 字节扩展到 64 字节,提高数据传输效率
- 保持向下兼容,可与传统 CAN 设备共存
2015 年,CAN FD 被 ISO 11898-1 正式采纳,成为新的国际标准
2、CAN 物理层
2.1 经典拓扑结构
如图 2-1,CAN 总线由两条差分线 CANH 和 CANL 组成,各个节点通过较短的支线接入 CAN 总线。各节点从通信协议而言是没有主从和地址区分的,每个节点均可以平等的收发数据。另外,在 CAN 总线的两端各有一个 120Ω 的终端电阻,来做阻抗匹配,以减少回波反射。

连接在 CAN 总线上的设备叫做节点设备(CAN Node),CAN 网络的拓扑一般为线型。线束最常用的是双绞线,线上传输为对称的差分电平信号。
在最高传输速率 1 Mbit/s的情况下,允许的最大长度是40米。在 CAN 网络的末端,总线终端电阻(termination resistor)有助于抑制信号反射现象。ISO 11898 规定 CAN 节点的最大数量为 32。
2.2 物理特性
如下图:显性电平对应逻辑“0”,CANH 和 CANL 压差 2.5V 左右。而隐性电平对应逻辑“1”,CANH 和 CANL 压差为 0V。
在总线上,显性电平具有优先权,只要有一个节点输出显性电平,总线上即为显性电平。而隐形电平则具有包容的意味,只有所有的节点都输出隐性电平,总线上才为隐性电平。
这里可以提前剧透一下,CAN 总线和 I2C 总线一样,采用了 “线与” (Wired-AND)机制。
- 总线上的实际电平 = 所有驱动它的器件的逻辑逻辑与(AND)运算结果
- 低电平(0)“更强”,即任何设备如果想输出低电平(0),则总线线上一定是低电平(显性)
- 高电平(1)需要所有设备都不拉低总线,才能保持高(隐性)
2.3 寻址方式
所谓寻址方式的话,就是去定义数据从哪里来、到哪里去,确定其发送与接收的一个关系。常见的寻址方式主要有以下两种:
- 点对点寻址:通常在发送的数据里面会包括目标地址、源地址等信息(像 I2C、USB 总线等)
- 广播寻址:可以实现 1 对多的发送。发送节点在数据发送过程中,只负责将数据发送到总线上,不会去指定具体的一个接收节点。所有挂在该总线上的节点,都可以接收到这样的数据。但具体是否接收这些数据,是由总线上的接收节点自己决定(CAN 总线采用的就是这种寻址方式)。
接收节点内部结构中,通常会有一个 Filter,接收过滤器,通过一定的过滤规则,设定想要接收的数据。规则之外的数据,硬件则会自动屏蔽掉。
2.4 非破坏性仲裁
CAN 网络中,节点是没有主从之分的。每一个节点在总线空闲的时候,都可以去访问总线。因为是共享传输介质,那么总线在任意时刻只能被一个节点去访问。所以,节点在访问总线的过程中,可能会出现访问冲突的情况。
CAN 总线中,采用的是“非破坏性仲裁”机制。如果在同一时刻有 2 个或 2 个以上的设备向网络上传输数据,会触发总线仲裁,判断报文优先级,优先级较高的报文则继续发送。等待总线空闲时间,将重新发送优先级较低的报文,以此避免总线冲突。
优先级的判定,主要是依赖 CAN 帧中的 “仲裁场”(后面讲帧格式的时候会讲到)。当总线上传递的 ID 时,出现 bit 位不一样时,例如下图中 A 节点在传递 ID 的第 4 位时,与其它几个节点不同,这时候会通过一个 “线与”(Wired-AND) 的机制,总线输出的实际电平为“0”。A 节点在进行回读时,发现总线实际电平和其发送的电平不一致,这时候,节点 A 会认为自己输掉了仲裁,会从一个发送节点转变为一个接收节点… …以此类推,实现了 CAN 总线的仲裁机制。
3、CAN 帧格式
在 CAN2.0 技术规范中,分为 A、B 两部分,2.0A 的消息帧格式只允许标识符(ID)11位的标准帧,基于 2.0A 的总线网络只能收到这种格式的消息,标准帧结构如图 3-1 所示。
在 CAN2.0B 中额外增加了扩展帧,它拥有 29 位标识符(ID),前 11 位与 CAN2.0A 的标识符相同,在此之后又增加了 18 位 CAN2.0B 专用的扩展标识符,其余结构完全相同,基于 2.0B 的总线网络既可以接收标准帧也可以接收扩展帧,但是在同一网络中通常只能出现一种帧格式,必须提前设定该网络是接收标准帧还是扩展帧,扩展帧结构如图 3-2 所示。
CAN 消息帧根据用途分为四种:
- 数据帧:用于数据交互;
- 远程帧:用于远程请求发送数据;
- 错误帧:用于标记总线上出现的错误;
- 过载帧:用于延迟下一条数据帧或远程帧的发送。
3.1 数据帧
也叫做 “消息帧” ,对应的就是上图 3-1 、图 3-2。
- 帧起始(SOF):长度 1 bit,表示数据帧的开始,在总线空闲时向总线发出一个显性位边沿信号,所有接收节点将根据最先发送数据的节点帧起始为位同步时钟信号
- 仲裁场:在标准帧格式中,由 11 位标识符和RTR位组成,在扩展帧格式中,由 29 位标识符和 SRR 位、IDE 位、RTR 位组成,ID 值越小,优先级越高
- RTR:长度 1 bit,该位用于远程帧请求时为隐性位 1,在数据帧的情况下必须是显性位 0
- SRR:长度 1 bit,在扩展帧格式中始终为隐性位 1,总线通过接收到的 RTR 位和 IDE 位可以判断出当前数据的帧类型
- IDE:长度 1 bit,表示标识符扩展位,在标准帧格式中表示显性位 0,扩展帧格式中表示隐性位 1,同一网络不能出现两种格式的帧
- 控制场:由保留位和 DLC 组成,在标准帧格式中,IDE 与保留位相同,均为显性位 0
- DLC:长度 4 bit,数据长度码,范围 0-8,用于设定数据场长度 0-8 字节
- 数据场(Data Field):0~8个字节,用于节点之间传递有效数据
- CRC场:长度 16bit,包括 CRC 序列和 CRC 界定符,用于校验数据的准确性
- CRC Sequence:长度 15 bit,校验序列,发送方从 SOF 到 Data Field 的所有数据进行编码后填写 CRC 序列并发送,接收方将对接收数据的 SOF 到 Data Field 的所有数据进行编码再与 CRC 序列进行比对,判断数据是否正确
- DEL:长度 1 bit,隐性界定符,校验场和应答场的界定符作用相同,均为隐性位 1
- 应答场:长度 2 bit,由应答间隙和应答界定符组成,确认数据是否正常接收
- ACK:发送方将发送隐性位 1,由接收方进行确认,若收到消息,则返回一个显性位 0,如果没有一个节点收到消息,应答间隙则保持隐性电平,将会报错
- 帧结尾(EOF):长度 7 bit,每个数据帧和远程帧的帧结尾均由 7 个连续的隐性位组成,表示帧结束。
3.2 远程帧
节点可以通过发送远程帧要求源节点发送数据(请求数据),远程帧由6个部分,即帧起始,仲裁场、控制场、校验场、应答场、帧结尾,结构如下图所示。和数据帧相比,少了 “数据场”。
3.3 错误帧
当某一节点检测到错误时,向总线发送错误帧,通知其他节点,错误帧的结构由错误标志和错误界定构成,帧结构如下图所示。
错误标志(Error Flag):长度 6 bit,当某一节点检测到错误,会向总线发送“错误标志”,主动错误节点会发送 6 个连续的显性位,被动错误节点会发送 6 个连续的隐性位。错误一旦产生就不是一个节点的问题了,发送节点与接收节点都会对错误做出响应。其他节点若检测到错误,发送节点停止发送报文,接收节点也停止接收报文,一同向总线发送错误标志。
第二个错误标志(Error Flag):用来叠加其他节点发送的错误标志,由于接收节点发现错误的时间可能不同,总线上的错误标志可能由6~12个显性位组成。
错误界定(Error Delimiter):由 8 个隐性位组成。当发现错误的节点发送完错误标志后,开始发送错误界定符,即 8 个隐性位,但是可能会出现另外的节点仍在发送错误标志,导致错误界定被显性位的错误标志覆盖,直到检测到总线上出现 8 个隐性位的界定符,才表示错误帧结束。
错误帧的作用就是用来标记总线上出现的错误,错误类型分为位错误,位填充错误,CRC错误,应答错误,格式错误。
- 位错误:由发送节点对通过总线竞争的节点进行检测,节点向总线发送数据的某一位与回读的数据不同,则判定出现了位错误
- 位填充错误(后面的位填充章节会讲到):由接收节点进行检测,若总线上 SOF~CRC 出现 6 个连续的相同极性位,就判定出现了位填充错误
- CRC 错误:由接收节点检测,接收节点对 SOF~Data Field 的数据进行模 2 运算,计算出的校验序列与数据块中的校验序列进行比较,若不同,则判定出现了 CRC 错误
- 应答错误:由发送节进行点检测,应答间隙无人应答,则判定出现了应答错误
- 格式错误:由接收节点进行检测,CRC 校验序列往后,从校验场的 DEL 开始不进行位填充,应该是固定格式,除了应答间隙(ACK)以外都应该是隐性位,若出现显性位,则判定出现了格式错误
3.4 过载帧
过载帧,已经被淘汰,与错误帧格式相同,包括过载标志和过载界定符,以下两种情况会发送过载帧:
- 接收单元要求发送节点延迟下一个数据帧或远程帧的发送
- 在帧间隔(ITM)的3 位内,前两位检测到显性位。
超载标志由6个显性位构成,它破坏了帧间空间的固定格式,其他节点检测到后会各自发送一个超载标志。
超载界定符由8个隐性位自称,超载标志发送完毕后,每个节点都会对总线进行监测,直到出现一个隐性位,此时每个节点的超载标志全部发送完成,所有节点将发送7个隐性位,直到总线上检测到8个连续的隐性位,超载帧发送结束。
4、位同步
4.1 位时间
首先了解几个基础概念:
- CAN 时钟:Fclk,CAN 时钟是由系统时钟分频而来的一个长度值
- CAN 波特率:指数据在总线上传输的速度,通常以 bps(bit per second,位/秒) 为单位
- Tq:Tq 表示1个 CAN 时钟周期,Tq = 1 / Fclk
- BRP:叫做波特率预分频值(baud rate prescaler)。它的作用是降低 CAN 时钟频率,以产生 Tq(时间量子)
位时间概念:位时间表示一个二进制位在总线上传输时所需要的时间。如下图所示
- 一个位可以分为四个时间段;
- 每个段又由若干个 Tq 构成,位时间大概包含 8~25 个 Tq
- 位时间 = 1/波特率
这里要注意,位时间和波特率有关,和系统时钟无关;而波特率和系统时钟有关,对应的波特率需要对应的时钟,可参考串口时钟
有一个公式很重要:
BRP = Fclk / (Q * Baudrate)
注:其中, Q = 传输一个二进制位所需要的 Tq 数
4.2 位时间分段
-
同步段(Synchronization Segment)
- 固定长度为一个 Tq
- 一个位的传输时从同步开始的
- 同步段用于同步总线上的各个节点,多个连接在总线上的单元通过此段实现时序调整,从而能同步进行接收和发送的工作,一个位的跳变沿在此时间段内;
-
传播段(Propagation Segment)
- 传播段时长可编程(1~8个时间份额 Tq)
- 传播段用于补偿报文在总线和节点上传输时所产生的时间延迟;
-
相位缓冲段1(Phase Buffer Segment1)和 相位缓冲段2(Phase Buffer Segment2)
- 用于补偿总线上的边沿相位误差
- 用于补偿节点间的晶振误差
- 允许通过重同步延长 PBS1 或缩短 PBS2 从而补偿同步误差(因为时钟的偏差,传送延迟等,各单元会有同步误差);
- PBS1 < PBS2
- 在 PBS1 时间段的末端进行总线状态的采样
-
采样点(Sample Point)
- 采样点一般位于相位缓冲段 1 之后,采样点是读取总线电平,并解释各位的值的一个时间点,采样点对 CAN 总线来说也非常重要,尤其在组网的时候,多个节点尽量保持同一个采样点,且最好在但不超过 7/8 位时间点上
-
SJW(reSynchronization Jump Width)重新同步补偿宽度
- 允许的范围是 1 ~ 4。用于设置 Tpbs1 和 Tpbs2 可以调整的节拍数
5、CAN 同步机制
5.1 硬同步
接收单元在总线空闲状态检测出帧起始时进行的同步调整。在检测出边沿的地方不考虑 SJW 的值而认为是 SS 段。硬件同步的过程如下图所示。
硬同步(Hard Synchronization)仅发生在帧起始位(SOF),用于强制对齐所有节点的比特时间,但无法修正帧传输过程中的时序误差。因此,在帧传输过程中,需要依靠重同步(Resynchronization)机制来调整采样点,补偿时钟漂移
5.2 重同步
在接收过程中检测出总线上的电平变化时进行的同步调整。
每当检测出边沿时,根据 SJW 值通过加长 PBS1 段,或缩短 PBS2 段,以调整同步。但如果发生了超出 SJW 值的误差时,最大调整量不能超过 SJW 值。
重同步如下图所示:
在 CAN 通信中的 重同步(Resynchronization)过程中,相位缓冲段(PBS1 和 PBS2)的调整是由硬件自动完成的,具体调整范围由同步跳跃宽度(SJW, Synchronization Jump Width)参数来限制
5.3 CAN 驱动中的计算
以 RK3568 平台为例,在其芯片手册 CAN 章节中,有这样一个寄存器。我们在设置 CNA 波特率时,需要设置这个寄存器。计算方法,基础原理就是通过这个公式:
BRP = Fclk / (Q * Baudrate)
注:其中, Q = 传输一个二进制位所需要的 Tq 数
这里简单解释下寄存器中的两个公式:
- CAN 时钟时间片(Tq)计算公式:
Tsclk = 2 x Tclk x(brp + 1)
- Tsclk 是 CAN 时钟的一个时间片(Time Quantum, Tq)的时长
- Tclk 是 CAN 模块输入时钟(通常是系统时钟或分频后的时钟) 的周期
- brp 是波特率预分频器(Baud Rate Prescaler, BRP)的值,它用于降低时钟频率,以适应 CAN 总线的波特率要求
该公式表示,CAN 总线的最小时间单位 Tq 取决于输入时钟,并受到波特率预分频器的影响。这里的 2 倍是由于 CAN 控制器的具体实现,有些 CAN 控制器会对预分频后的时钟再进行 2 分频。
- 相位缓冲段 2 时间计算
Tphase_seg2 = Tsclk x (tseg2 + 1)
- Tphase_seg2:是相位缓冲段 2(Phase_Seg2)的时间长度
- Tsclk:是 CAN 时钟时间片(Time Quantum, Tq) 的时间长度
- tseg2:是 tseg2 配置寄存器的值,它定义了 相位缓冲段 2 的时间片个数
为什么要 +1 呢?因为 tseg2 寄存器的值是 0-based 的,而实际上最小值应为 1 个时间片
6、位填充
位填充是为防止突发错误而设定的功能。当同样的电平持续 5 位时则添加一个位的反型数据。位填充的构成如下图所示:
R:隐性电平(Recessive Level)
D: 显性电平(Dominant Level)
(1) 发送单元的工作
在发送数据帧和遥控帧时,SOF~CRC 段间的数据,相同电平如果持续 5 位,在下一个位(第 6 个位)则要插入 1 位与前 5 位反型的电平。
(2) 接收单元的工作
在接收数据帧和遥控帧时, SOF~CRC 段间的数据,相同电平如果持续 5 位,需要删除下一个位(第 6 个位)再接收。如果这个第 6 个位的电平与前 5 位相同,将被视为错误并发送错误帧。
6.1 位填充的意义
-
避免与CAN 错误帧冲突
CAN 协议采用填充位机制,就是为了避免与 “错误帧” 处理机制起冲突。如果不采用位填充机制,如果 CAN 帧数据段发送 0x00或0xFF,就会产生连续 6 位显性位或显性位。会产生异常错误。
与 “帧结束标志” 的冲突同样如此。 -
位填充是与位同步机制相配合的
我们回顾下 “重同步” 章节。重同步实现的方法有一个必要条件,就是总线电平发生跳变(即图 5-2 中的隐性电平向显性电平的跳变)。而电平跳变就意味着数据发生改变。如果一直是显性电平或者一直是隐性电平,则位同步中的 “重同步” 将会失效。
7、过滤规则
CAN 节点中,通常会包含有 CAN 过滤器。CAN过滤器是用来过滤CAN总线上收到的消息,从而确保只处理特定的数据帧。在CAN通信中,过滤器的配置对于性能和消息处理的准确性非常重要。
还是以 RK3568 为例,
芯片手册中:
硬件过滤方式在驱动:drivers/net/can/rockchip/rockchip_canfd.c 中实现,具体代码:
static int rockchip_canfd_start(struct net_device *ndev)
{
......
rockchip_canfd_write(rcan, CAN_INT_MASK, 0);
/* RECEIVING FILTER, accept all */
rockchip_canfd_write(rcan, CAN_IDCODE, 0);
rockchip_canfd_write(rcan, CAN_IDMASK, CAN_RX_FILTER_MASK);
rockchip_canfd_write(rcan, CAN_IDCODE0, 0);
rockchip_canfd_write(rcan, CAN_IDMASK0, CAN_RX_FILTER_MASK);
rockchip_canfd_write(rcan, CAN_IDCODE1, 0);
rockchip_canfd_write(rcan, CAN_IDMASK1, CAN_RX_FILTER_MASK);
rockchip_canfd_write(rcan, CAN_IDCODE2, 0);
rockchip_canfd_write(rcan, CAN_IDMASK2, CAN_RX_FILTER_MASK);
rockchip_canfd_write(rcan, CAN_IDCODE3, 0);
rockchip_canfd_write(rcan, CAN_IDMASK3, CAN_RX_FILTER_MASK);
rockchip_canfd_write(rcan, CAN_IDCODE4, 0);
rockchip_canfd_write(rcan, CAN_IDMASK4, CAN_RX_FILTER_MASK);
......
}
从以上代码可以看出,每次执行 CAN 软件都会重新配置 CAN 控制器寄存器不过滤任何数据。
通过 2 个寄存器来实现硬件过滤:CAN_IDCODEn、CAN_RX_FILTER_MASK
- 假设在过滤时只想接收帧 id 最低位为 1 的报文,即可设置参数为:
CAN_IDCODE = 0x1;
CAN_IDMASK = 0x1ffffffe;
CAN_IDMASK 最低位为 0 ,表示硬件过滤时仅关心最低位的值。查看 CAN_IDCODE 最低位的值为 1,即为只接收 id 最低位为 1 的报文
- 若只想接收特定 id 号的报文,即可直接设置 CAN_IDMASK 为 0:
CAN_IDCODE = 0x1;
CAN_IDMASK = 0x0;