4、CAN数据帧结构(上):SOF、仲裁场(ID与RTR)、控制场(IDE、DLC)

各位同学,今天我们来啃一块硬骨头——CAN数据帧的结构。说实话,很多工程师干了几年,报文能收能发,但你要问他帧里每个bit是干嘛的,他可能就含糊了。我当年刚入行时也是这样,直到有一次在实车上排查一个偶发性的通信故障,被一个叫RTR的位坑得死去活来……从那以后,我就把帧结构刻在脑子里了。

CAN总线上一共跑着四种帧:数据帧、远程帧、错误帧、过载帧。其中数据帧是我们最常用的,用来传输实际数据。一个标准的数据帧,从开头到结尾,可以拆成七个段。今天我们先讲前三个:SOF、仲裁场、控制场。

标准CAN数据帧的七个段(今天讲前三个):

  1. SOF(帧起始)—— 1 bit,同步信号
  2. 仲裁场 —— 12 bits(标准帧),包含ID和RTR
  3. 控制场 —— 6 bits,包含IDE和DLC
  4. 数据场 —— 0~8 bytes,真正的数据
  5. CRC场 —— 15 bits + 1 bit 界定符,校验用
  6. ACK场 —— 2 bits,应答
  7. EOF(帧结束)—— 7 bits,收尾

下面这张图,把今天要讲的三个段在帧里的位置标出来了。你把它存下来,后面几节课都会用到。

标准CAN数据帧结构(前三个段) 完整数据帧(108 ~ 128 bits) SOF 1 bit 仲裁场 ID (11 bits) + RTR (1 bit) 12 bits 控制场 IDE (1 bit) + r0 (1 bit) + DLC (4 bits) 6 bits 数据场 + CRC + ACK + EOF 后续章节讲解 💡 说明: 标准帧(11位ID)仲裁场12 bits;扩展帧(29位ID)仲裁场32 bits,IDE位为1。 本节课以标准帧为例讲解,扩展帧后续章节单独展开。

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、仲裁场、控制场分别是什么:

  1. SOF:1位,显性0。
  2. 仲裁场:ID = 0x321 = 011 0010 0001(11位),RTR = 0(数据帧)。所以仲裁场 = 01100100001 0。
  3. 控制场: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,标识帧格式和数据长度