3. CAN帧结构详解(上):帧起始、仲裁场、控制场

各位好,欢迎来到CAN协议栈的第三讲。今天咱们把CAN帧的头部拆开,看看里面到底藏了什么玄机。

CAN总线上的报文,说白了就是一串高低电平。但这一串电平怎么组织,才能让多个节点既不打架,又能高效传数据?这就得靠帧结构了。

我个人习惯把CAN帧比作一封信:帧起始是信封上的“邮戳”,告诉所有人“我要开始写了”;仲裁场是收件地址和优先级,决定谁先写;控制场是信件格式说明,告诉对方这封信有多长、是什么类型。

今天咱们就重点聊这三个部分。数据场和后面的CRC、ACK,留到下节课再讲。

3.1 帧起始(SOF)—— 总线的“发令枪”

帧起始,英文叫Start of Frame,缩写SOF。它只有一个bit,固定为显性电平(逻辑0)。

你可能会问:为什么非得是显性?

嗯,这里有个关键设计。CAN总线在空闲时,两根线(CAN_H和CAN_L)都是隐性电平(逻辑1)。一旦某个节点想发数据,它就把总线拉成显性。这个从隐性到显性的跳变,就是SOF。

我在项目里遇到过一个问题:有个同事把SOF误写成隐性电平,结果总线上的节点根本检测不到帧开始,整个网络直接“死机”。排查了两天才找到原因——就是这一个bit的事。

SOF核心要点:
  • 长度:1 bit
  • 电平:显性(0)
  • 作用:同步所有节点的时钟,标志帧开始
  • 硬同步:每个节点在SOF的下降沿重新调整自己的采样点

SOF还有一个隐藏功能:硬同步。所有节点在检测到SOF的下降沿时,会强制把自己的时钟对齐到这个边沿。这就是为什么CAN不需要额外的时钟线——SOF本身就是同步信号。

3.2 仲裁场 —— 谁优先级高谁先走

仲裁场是CAN帧里最精彩的部分。它决定了:当多个节点同时发数据时,到底谁说了算。

仲裁场由两部分组成:标识符(ID)和RTR位。

3.2.1 标识符(ID)—— 报文的“身份证号”

ID就是报文的身份标识。在标准帧(CAN 2.0A)里,ID是11位;在扩展帧(CAN 2.0B)里,ID是29位。

ID的作用有两个:

  1. 标识报文内容:比如0x123代表“发动机转速”,0x456代表“车速信号”
  2. 决定优先级:ID值越小,优先级越高

为什么ID越小优先级越高?因为CAN总线是“线与”逻辑:显性(0)会覆盖隐性(1)。ID的高位先发,谁先出现0,谁就赢了。

举个例子:

节点 ID(二进制) 优先级
节点A 0x123 → 0001 0010 0011 高
节点B 0x456 → 0100 0101 0110 低

节点A的ID高位是0,节点B的高位是1。在仲裁时,节点A的0把节点B的1覆盖了,节点B检测到总线电平和自己发的不一致,就知道自己输了,乖乖退出,转为接收模式。

避坑指南: 我曾经在一个项目中,把两个节点的ID设成了0x000和0x001。结果0x000的节点一直占着总线,0x001的节点几乎发不出数据。后来我养成了一个习惯:关键报文用小编号ID,普通报文用大编号ID。比如刹车信号用0x001,车窗信号用0x500。

3.2.2 RTR位 —— 远程帧的“开关”

RTR全称Remote Transmission Request,远程传输请求位。

  • 数据帧:RTR = 0(显性)
  • 远程帧:RTR = 1(隐性)

RTR位紧跟在ID之后。在标准帧里,RTR只有1位。

远程帧是干嘛的?说白了就是“请求数据”。节点A可以发一个远程帧,告诉节点B:“把你的数据发给我。”节点B收到后,再发一个数据帧回应。

这里有个细节:远程帧没有数据场。它的DLC(后面会讲)表示请求的数据长度,但实际不发送数据。

我记得有一次调试,一个传感器节点死活不响应远程帧。查了半天,发现是RTR位写反了——数据帧的RTR写成了1,结果对方以为这是个远程帧,一直在等数据,两边都卡住了。

3.3 控制场 —— 帧格式的“说明书”

控制场在仲裁场之后,由6位组成。它告诉接收方:这个帧是什么格式、数据有多长。

控制场的结构如下:

位域 长度 说明
IDE 1 bit 标识符扩展位
r0 1 bit 保留位(显性)
DLC 4 bits 数据长度码

3.3.1 IDE位 —— 标准帧还是扩展帧?

IDE全称Identifier Extension,标识符扩展位。

  • 标准帧:IDE = 0(显性)
  • 扩展帧:IDE = 1(隐性)

IDE位的位置很关键。在标准帧里,IDE位在控制场;在扩展帧里,IDE位在仲裁场(用来区分ID是11位还是29位)。

你想想看,如果IDE位搞错了,接收方会把11位ID当成29位ID来解析,结果就是乱码。我见过一个案例:某ECU把标准帧的IDE设成了1,结果总线上的其他节点都把它当成扩展帧处理,数据全对不上。

注意: IDE位和SRR位(扩展帧专用)容易混淆。SRR位在扩展帧的仲裁场,替代标准帧的RTR位。IDE位在控制场,决定帧格式。这两个位功能不同,别搞混了。

3.3.2 r0位 —— 保留位

r0是保留位,固定为显性(0)。这是为了兼容未来的扩展。目前所有CAN控制器都必须把r0写成0,接收方也忽略这个位。

不过,有些老旧的CAN控制器会检查r0是否为0,如果不是,可能会报错。所以,千万别自作聪明把r0写成1。

3.3.3 DLC —— 数据长度码

DLC全称Data Length Code,数据长度码。它用4位二进制表示数据场的字节数。

DLC的取值范围是0~8,对应数据场0~8个字节。

DLC(二进制) 数据长度(字节)
0000 0
0001 1
0010 2
... ...
1000 8

注意:DLC的值不能超过8。如果DLC设为9(1001),大多数CAN控制器会把它当成8来处理。但有些控制器会报错。所以,DLC一定要在0~8之间。

我个人的习惯是:DLC尽量用实际数据长度。比如只发2个字节,DLC就设成2。不要为了省事把DLC设成8,然后只填前两个字节——这样浪费总线带宽,也容易让接收方困惑。

3.4 标准帧 vs 扩展帧:一张表说清楚

咱们把标准帧和扩展帧的头部结构对比一下:

字段 标准帧(CAN 2.0A) 扩展帧(CAN 2.0B)
SOF 1 bit 1 bit
ID 11 bits 29 bits(11 + 18)
RTR 1 bit 1 bit(在SRR之后)
SRR 无 1 bit(替代RTR位置)
IDE 1 bit(控制场) 1 bit(仲裁场)
r0 1 bit 1 bit
DLC 4 bits 4 bits

扩展帧的ID是29位,前11位是基本ID,后18位是扩展ID。仲裁时,先比基本ID,再比扩展ID。所以,基本ID的优先级高于扩展ID。

3.5 本章知识结构图

下面这张图把今天讲的内容串起来了:

CAN帧头部结构图(标准帧) CAN标准帧头部结构 SOF 1 bit 仲裁场 ID (11 bits) + RTR (1 bit) 控制场 IDE (1) + r0 (1) + DLC (4) 数据场 0~8字节 关键说明: • SOF:1位显性,同步所有节点时钟 • 仲裁场:ID越小优先级越高,RTR区分数据帧/远程帧 • 控制场:IDE区分标准/扩展帧,DLC指定数据长度(0~8) 帧发送流程 总线空闲? 发送SOF 仲裁(ID+RTR) 发送控制场 仲裁失败则退出,转为接收模式

3.6 避坑总结

最后,我把这些年踩过的坑总结一下:

常见错误清单:
  • SOF写错电平:必须显性,否则帧无法开始
  • ID冲突:两个节点用相同ID,会导致总线错误
  • RTR位搞反:数据帧的RTR=0,远程帧的RTR=1
  • IDE位设错:标准帧IDE=0,扩展帧IDE=1
  • DLC超范围:DLC只能在0~8之间
  • r0写成1:保留位必须为0

好了,今天的内容就到这儿。帧起始、仲裁场、控制场,这三个部分是CAN帧的“骨架”。骨架搭对了,后面的数据才能稳稳地传。下节课咱们接着聊数据场、CRC和ACK——那才是CAN帧的“血肉”。