4、CAN数据帧结构(上):SOF、仲裁场(ID与RTR)、控制场(IDE、DLC)
各位同学,今天我们来啃一块硬骨头——CAN数据帧的结构。说实话,很多工程师干了几年,报文能收能发,但你要问他帧里每个bit是干嘛的,他可能就含糊了。我当年刚入行时也是这样,直到有一次在实车上排查一个偶发性的通信故障,被一个叫RTR的位坑得死去活来……从那以后,我就把帧结构刻在脑子里了。
CAN总线上一共跑着四种帧:数据帧、远程帧、错误帧、过载帧。其中数据帧是我们最常用的,用来传输实际数据。一个标准的数据帧,从开头到结尾,可以拆成七个段。今天我们先讲前三个:SOF、仲裁场、控制场。
标准CAN数据帧的七个段(今天讲前三个):
- SOF(帧起始)—— 1 bit,同步信号
- 仲裁场 —— 12 bits(标准帧),包含ID和RTR
- 控制场 —— 6 bits,包含IDE和DLC
- 数据场 —— 0~8 bytes,真正的数据
- CRC场 —— 15 bits + 1 bit 界定符,校验用
- ACK场 —— 2 bits,应答
- EOF(帧结束)—— 7 bits,收尾
下面这张图,把今天要讲的三个段在帧里的位置标出来了。你把它存下来,后面几节课都会用到。
4.1 SOF——帧起始,总线的发令枪
SOF全称Start of Frame,就1个bit,显性电平(逻辑0)。它的作用很简单:告诉总线上所有节点——"我要开始发消息了,大家注意同步!"
你想想看,CAN总线是异步通信,没有单独的时钟线。节点之间怎么对齐时钟?就是靠SOF这个下降沿。总线空闲时是隐性电平(逻辑1),SOF一来,电平从1跳到0,所有节点检测到这个跳变沿,立刻把自己的时钟同步过来。
我的经验: 在测试CAN通信时,如果你用示波器抓波形,第一个要找的就是SOF的下降沿。只要这个沿干净、陡峭,后面的数据基本不会跑偏。我曾经遇到一个案例,某款MCU的CAN控制器在低温下SOF边沿变缓,导致总线同步失败,整条总线上的节点都收不到数据。排查了整整两天才找到根因。
4.2 仲裁场——ID + RTR,决定谁能说话
仲裁场是CAN帧里最核心的段之一。它由两部分组成:
- ID(标识符):标准帧11位,扩展帧29位。本节课先讲11位标准ID。
- RTR(远程发送请求位):1位。显性(0)表示数据帧,隐性(1)表示远程帧。
4.2.1 ID——不只是地址,更是优先级
很多初学者会把CAN的ID理解成"地址",就像I2C设备地址那样。其实不对。CAN的ID本质上是优先级。数值越小,优先级越高。
为什么会这样?因为CAN总线是"线与"机制。多个节点同时发送时,显性电平(0)会覆盖隐性电平(1)。ID数值小的帧,高位先出现0,就能把其他节点"仲裁"掉,自己继续发。
ID优先级规则:
- ID = 0x000(全0):最高优先级
- ID = 0x7FF(全1):最低优先级
- 数值越小,越早出现显性位,仲裁胜出
我建议你在设计CAN网络时,把关键的控制报文(比如刹车、转向)分配小ID,把诊断、配置等非实时报文分配大ID。这个习惯能帮你避免很多优先级反转的问题。
4.2.2 RTR——数据帧 vs 远程帧
RTR位紧跟在ID的最后一位之后。它区分两种帧:
| 帧类型 | RTR值 | 说明 |
|---|---|---|
| 数据帧 | 0(显性) | 发送数据给其他节点 |
| 远程帧 | 1(隐性) | 请求其他节点发送数据 |
远程帧本身不带数据场,它的作用是"请求帧"。比如节点A发一个远程帧,ID = 0x123,RTR = 1,那么总线上所有收到这个帧的节点,如果发现自己能提供ID=0x123的数据,就会在下一个总线空闲时发送对应的数据帧。
⚠️ 我曾经踩过的坑: 有一次在项目中,我用远程帧来请求传感器数据。结果发现传感器节点总是延迟响应,甚至不响应。排查后发现,传感器节点的CAN控制器在收到远程帧后,需要先处理中断再组包发送,这个过程耗时不确定。后来我改成传感器节点主动周期发送数据帧,问题就解决了。
教训: 远程帧虽然好用,但实时性不可控。在时间敏感的场景下,尽量用数据帧主动发送,别依赖远程帧去"要"数据。
4.3 控制场——IDE + r0 + DLC
控制场共6位,结构如下:
- IDE(标识符扩展位):1位。0表示标准帧(11位ID),1表示扩展帧(29位ID)。
- r0(保留位):1位。显性(0),留给未来扩展用。目前必须为0。
- DLC(数据长度码):4位。表示数据场有多少个字节,范围0~8。
4.3.1 IDE——标准帧还是扩展帧?
IDE位决定了仲裁场的长度。标准帧的仲裁场是12位(11位ID + 1位RTR),扩展帧的仲裁场是32位(29位ID + 1位SRR + 1位IDE + 1位RTR)。
IDE位在标准帧中为0,在扩展帧中为1。注意,IDE位在标准帧中属于控制场,在扩展帧中属于仲裁场——这个细节很多教材都没讲清楚,我当年也是翻了半天协议才搞明白。
我的建议: 如果你的网络节点不多(比如少于20个),优先用标准帧。11位ID足够用了,而且帧短,总线利用率高。只有在大规模网络(比如整车所有ECU都在一条总线上)才需要考虑扩展帧。我个人习惯,能用标准帧绝不用扩展帧,省带宽、省时间。
4.3.2 DLC——数据长度,别搞错了
DLC占4位,理论上可以表示0~15,但CAN协议规定数据场最多8个字节。所以DLC的有效范围是0~8。如果DLC的值大于8,接收节点怎么处理?
嗯,这里要注意:不同厂商的CAN控制器行为不一样。有些会截断到8字节,有些会报错。我建议你严格遵守协议,DLC不要超过8。
| DLC值(二进制) | 数据场字节数 | 备注 |
|---|---|---|
| 0000 | 0 | 无数据,常用于远程帧 |
| 0001 | 1 | 单字节信号 |
| 0010 | 2 | 常用 |
| 0011 | 3 | 较少见 |
| 0100 | 4 | 常用,一个int32 |
| 0101 | 5 | 较少见 |
| 0110 | 6 | 较少见 |
| 0111 | 7 | 较少见 |
| 1000 | 8 | 最常用,满载 |
| 1001 ~ 1111 | 8(部分控制器)或报错 | ⚠️ 非法值,不要用 |
实用技巧: 在解析CAN报文时,DLC是第一个要检查的字段。如果DLC为0,说明这个帧没有数据场,你直接跳过数据解析就行。很多诊断协议(如UDS)会用到DLC=0的报文作为请求确认。
4.4 实战:手撕一个CAN数据帧
光说不练假把式。我们拿一个真实的CAN报文来拆解一下。假设你用CAN工具抓到这样一帧数据:
ID = 0x321, DLC = 3, Data = 0x55 0xAA 0x0F
我们把它展开成二进制,看看SOF、仲裁场、控制场分别是什么:
- SOF:1位,显性0。
- 仲裁场:ID = 0x321 = 011 0010 0001(11位),RTR = 0(数据帧)。所以仲裁场 = 01100100001 0。
- 控制场:IDE = 0(标准帧),r0 = 0,DLC = 3 = 0011。所以控制场 = 0 0 0011。
把这几个段拼起来,帧开头就是:
0 | 01100100001 0 | 0 0 0011 | ...(后面是数据场、CRC等)
你想想看,如果总线上同时有两个节点要发送,一个ID=0x321,另一个ID=0x320。谁的优先级高?
0x320 = 011 0010 0000,0x321 = 011 0010 0001。从高位开始逐位比较,前10位都一样,第11位(最低位)0x320是0,0x321是1。0是显性,所以0x320胜出。这就是仲裁的过程。
我的习惯: 在纸上手算一遍ID的二进制,能帮你真正理解优先级。别偷懒,拿几个你项目里的ID算一算,保证你对CAN的理解上一个台阶。
4.5 小结
今天的内容就到这里。我们讲了数据帧的前三个段:
- SOF:1位显性,同步信号
- 仲裁场:11位ID + 1位RTR,决定优先级和帧类型
- 控制场:1位IDE + 1位r0 + 4位DLC,标识帧格式和数据长度