1.CAN简介
CAN 的全称是 Controller Area Network,即控制器局域网。
经典高速 CAN 通常采用 CAN_H 和 CAN_L 两根差分信号线传递数据。接收器主要根据两根线之间的差分电压判断总线状态,而不只是判断某一根线对地的电压,这也决定了它具有较强的抗干扰能力。
CAN 的一个重要特点是:一个设备发送报文后,总线上的其他设备原则上都能接收到该报文,再根据 ID 过滤或决定是否处理它。CAN 标识符主要用来区分报文类型和仲裁优先级,而不是简单地表示“这条数据只发给哪个设备”。
2.CAN的两种总线状态:
2.1显性位
典型高速 CAN:CAN_H 约 3.5 V,CAN_L 约 1.5 V,差分电压约 2 V。
2.2 隐性位
典型高速 CAN:CAN_H、CAN_L 都约为 2.5 V,两线差分电压接近 0 V。
CAN 总线采用一种类似“线与”的仲裁逻辑:只要有一个节点发送显性位0,总线就呈现显性状态;只有所有正在参与的节点都发送隐性位1时,总线才呈现隐性状态。
| 节点A发送 | 节点B发送 | 总线实际状态 |
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
3.一个CAN数据帧的组成
CAN 总线不是简单地把几个字节直接发送出去,而是由 CAN 控制器按照协议,把 ID、控制信息、数据、校验位等组合成一个完整的帧(Frame)。
经典 CAN 主要定义四种帧:数据帧、远程帧、错误帧和过载帧。其中日常通信最常用的是数据帧。
CAN的数据帧又分为:CAN数据帧——标准帧和CAN数据帧——拓展帧
标准格式数据帧(Standard Frame):标识符 ID 为 11 位。
扩展格式数据帧(Extended Frame):标识符 ID 为 29 位。
两者都包含帧起始、仲裁字段、控制字段、数据字段、CRC 字段、ACK 字段和帧结束,但仲裁字段和控制字段的具体结构有所不同。
3.1CAN数据帧——标准帧
标准格式数据帧
| SOF | 11位ID | RTR | IDE(0) | r0 | DLC | DATA | CRC | ACK | EOF |
3.1.1 SOF:帧起始位(1 位)
SOF 全称是Start of Frame,用于标识一帧报文的起始
固定为显性位
0。总线上的其他节点通过观察总线状态和位时间进行接收、同步。
示例:
总线空闲: 1 1 1 1 开始发送: 0 总线上连续出现的隐性位不表示报文开始,真正的 SOF 是显性位。
3.1.2 仲裁字段(12位)
CAN标准帧的仲裁字段包含11位ID和1位RTR。
ID(11位)
ID是 CAN 最重要的字段之一,具有两个核心用途:
用途1:标识这条报文的类别或含义。
例如通过不同的ID标识电机的不同参数:
| ID | 约定的含义 |
| 0x100 | 电机状态 |
| 0x101 | 电机目标转速 |
| 0x200 | 温度数据 |
| 0x300 | 故障状态 |
重点:CAN ID 并不自动表示目标设备地址。 总线上的设备都能接收到帧,再通过硬件过滤器或软件判断是否需要处理它。
用途2:在多个设备同时申请发送时,决定哪条报文优先发送。
ID 以最高有效位在前的顺序发送。通常在相同帧格式中,ID 数值越小,仲裁优先级越高。这个结论后面会结合逐位仲裁详细解释。
RTR(1位)
(Remote Transmission Request)即远程传输请求,用于标识当前帧是数据帧还是远程帧。
| RTR | 含义 |
| 0 | 数据帧 |
| 1 | 远程帧 |
数据帧是发送实际数据的帧。远程帧则用于请求其他节点发送具有指定 ID 的数据帧,本身不携带数据字段。
例如,某节点希望请求 ID 为0x200的数据,可以发送对应 ID 的远程帧。但现代 CAN 网络的应用层协议通常使用更明确的请求报文或服务机制,而不是大量依赖远程帧。
有一个与仲裁有关的重要结论:如果相同 ID 的数据帧和远程帧同时竞争总线,数据帧的 RTR 为0,远程帧的 RTR 为1,所以数据帧优先。
CAN FD 不支持经典 CAN 的远程帧格式,因此不要将这个机制照搬到 CAN FD。
3.1.3 控制字段(6位)
控制字段包含1位IDE、1位r0、4位DLC
IDE(1位)
IDE 的英文为 Identifier Extension,用于标识当前帧是标准帧还是拓展帧。
| IDE | 帧类型 |
| 0 | 标准帧(11位ID) |
| 1 | 拓展帧(29位ID) |
r0(1位)
保留位
在经典 CAN 格式下,保留位应按照标准发送为显性位0。
DLC(4位)
DLC 的英文是 Data Length Code,用来指示数据字段的长度。
经典 CAN 每帧最多传输 8 字节数据,因此常用 DLC 与字节数的对应关系如下:
| DLC | 数据长度 |
| 0000 | 0字节 |
| 0001 | 1字节 |
| 0010 | 2字节 |
| 0011 | 3字节 |
| 0100 | 4字节 |
| 0101 | 5字节 |
| 0110 | 6字节 |
| 0111 | 7字节 |
| 1000 | 8字节 |
普通经典 CAN 数据帧应使用 0~8 对应的有效数据长度。不要把 DLC 的 4 位理解成“数据最多 15 字节”:它虽然有 16 种二进制组合,但经典 CAN 的数据字段仍然最多只有 8 字节。CAN FD 则对 DLC 使用了不同的长度映射,后面会介绍。
3.1.4数据字段(0~64位)
Data 字段才是应用真正要传输的数据。例如:
| CAN_ID | 0x100 |
| DLC | 4 |
| DATA | 01 02 03 04 |
这表示 ID 为0x100、数据长度为 4 字节、数据内容为01 02 03 04的报文。
但需要注意,CAN 标准只规定怎样传输这些字节,并不自动规定它们的物理意义。如果数据01 02 03 04代表温度、转速、故障码还是其他数值,必须由上层通信协议或项目自身的数据定义来解释。
数据位也按照协议规定的位顺序发送。多字节变量如何排列、整数采用什么字节序、数据如何缩放,都需要通信双方事先约定。
3.1.5 CRC字段(16位)
CRC 的英文为 Cyclic Redundancy Check,作用是检测报文在传输过程中是否出现了数据错误。
发送端按照协议规定的算法计算 CRC;接收端根据收到的报文重新计算并进行比较。如果结果不匹配,接收端会检测到 CRC 错误,并按照 CAN 错误处理机制采取相应动作。
CRC 序列之后还有一个单独的 CRC 分隔符,在经典 CAN 中该位必须为隐性位1。
3.1.6 ACK字段(2位)
ACK是应答字段包含两个不同的位:
| 字段 | 位数 | 含义 |
| ACK Slot(ACK 槽) | 1位 | 接收节点确认收到有效报文的位置 |
| ACK Delimiter(ACK 分隔符) | 1位 | 固定格式的分隔位,必须为隐性位 |
ACK 槽有一个特别重要的设计:
发送端在 ACK 槽发送隐性位
1。至少一个能够正确接收该帧的节点会在 ACK 槽驱动显性位
0。发送端观察到显性位,便知道至少有节点正确接收了该报文。
但是,ACK 不是应用层的“业务处理成功”回复。它只代表有节点接收成功,不证明特定目标设备已经执行了命令。
3.1.7 EOF(7位)
EOF 的英文为 End of Frame。经典 CAN 帧的 EOF 是连续 7 个隐性位1,用于标明当前帧结束。
EOF 之后通常还需要 3 个隐性位的帧间隔(Intermission),以便总线按协议进入下一帧的发送时机。
帧间隔不属于前一帧的 EOF 本身。
至此,经典 CAN 数据帧的主要字段就完整了:从 SOF 开始,经过 ID、控制、数据和 CRC,再通过 ACK 确认接收情况,最后用 EOF 结束帧。
3.2 CAN数据帧——拓展帧
拓展格式数据帧
| SOF | 11位基础ID | SRR | IDE(1) | 18位拓展ID | RTR | r1 | r0 | DLC | DATA | CRC | ACK | EOF |
数据帧拓展帧与数据帧标准帧的区别在于仲裁字段和控制字段,其他部分都一样。
拓展帧中出现的新标识:SRR和r1
SRR
SRR 的英文为 Substitute Remote Request,用于在扩展帧中的替代原来在标准帧中的远程请求位。
它出现在扩展帧的 ID-A 与 IDE 之间,通常发送隐性位1。它的一个重要作用是保证标准帧和扩展帧在具有相同的 11 位基本标识符时,可以按照协议正确完成仲裁。
r1
在经典 CAN 格式下,r1和r0属于保留位。保留位应按照标准发送为显性位0。
标准帧的控制字段含有
r0。扩展帧的控制字段含有
r1和r0。
在 CAN FD 中,一些原有的保留位位置被赋予了新的协议用途,所以学习经典 CAN 时,应以经典 CAN 的定义为准,不能混用两种格式。
注意:标准格式中IDE属于控制字段;拓展格式中IDE属于仲裁字段。IDE位不仅用于标识帧格式,他在两种帧中的位置也不同。
对于扩展格式数据帧:
SRR =
1(隐性位)IDE =
1(隐性位)(1标识拓展帧)RTR =
0(显性位)(0标识数据帧)
注意:SRR 和 IDE 都不属于 29 位 ID。 它们是独立的协议控制位,只是出现在扩展格式的仲裁字段中。
在经典 CAN 2.0 中:
标准格式数据帧:IDE =
0(显性位),表示 11 位标准标识符。扩展格式数据帧:IDE =
1(隐性位),表示 29 位扩展标识符。标准格式的控制字段有IDE(0)、r0、DLC
拓展格式的控制字段有r1、r0、DLC
因为扩展格式的 IDE 已经放进了仲裁字段,所以控制字段不再包含 IDE。
4. CAN仲裁机制
这是 CAN 协议最有特色的部分,用于解决多个设备同时发送的问题。
假设总线上有三个设备:
节点 A:发送电机转速报文,ID 为
0x100。节点 B:发送温度报文,ID 为
0x200。节点 C:发送故障状态报文,ID 为
0x080。
如果三个节点恰好同时准备发送,谁先发送?
普通串口往往需要通过软件或额外的控制机制协调发送时间。而 CAN 利用标识符的优先级和显性、隐性位的电气特性,直接在总线上完成逐位仲裁。
4.1 仲裁的基本规则
先记住三条规则:
总线空闲时,节点可以申请发送。
节点逐位发送仲裁字段,同时读取总线上的实际电平。
如果某个节点发送隐性位
1,却发现总线上是显性位0,它就输掉本次仲裁,并停止发送自己的报文。
正常的仲裁失败不是通信错误。失败节点不会故意发送错误帧去打断获胜节点,而是退出竞争,转为接收总线上的报文。获胜节点继续发送,不需要因为这次仲裁失败而重新发送整帧。
4.2 用一个真实的 ID 示例理解仲裁
假设节点 A、B 同时发送两个标准数据帧:
| 参数 | 节点A | 节点B |
| CAN ID | 0x123 | 0x124 |
| 11位ID的二进制表示 | 001 0010 0011 | 001 0010 0100 |
两个ID从最高位开始比较:
| 位序列 | ID10 | ID9 | ID8 | ID7 | ID6 | ID5 | ID4 | ID3 | ID2 | ID1 | ID0 |
| 节点A | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 |
| 节点B | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 |
首次出现差异的位置是ID2
前面的位完全相同,两个节点一直同时参与仲裁。当发送到ID2时:
节点 A 发送 0,令总线进入显性状态;
节点 B 发送 1,却读到总线为 0,判断自己失去仲裁,立即退出竞争。
节点 A 获得总线使用权
节点 A 继续把这一帧发送完,节点 B 等待适当时机再发送自己的报文。
这就是无损仲裁。多个节点同时开始发送并不一定造成冲突;只要它们在仲裁字段中能区分出优先级,失败节点就会自动退出,而获胜节点的报文不会被破坏。
4.3 为什么 ID 越小,优先级通常越高?
在同样的经典 CAN 帧格式下,标识符按照最高位优先的顺序发送。
较小的 ID 通常在更靠前的位置出现显性位0。当两个 ID 首次出现差异时,发送0的节点会胜过发送1的节点。因此,较小的数字一般具有更高的仲裁优先级。
这里的“优先级”是总线访问优先级,不是 CPU 中断优先级,也不意味着 ID 数值小的节点永久独占总线。当高优先级报文没有待发送数据时,其他节点仍然可以发送。
需要注意三个细节:
优先级主要由仲裁字段决定,而不是由数据内容决定。
标准帧与扩展帧具有额外的格式区分位,不能在所有格式组合中只看十进制 ID 就判断。
如果两个节点使用相同的仲裁信息,却要发送不同的数据,它们就可能在仲裁结束后出现位错误。这种设计通常应避免。