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的事。
- 长度: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的作用有两个:
- 标识报文内容:比如0x123代表“发动机转速”,0x456代表“车速信号”
- 决定优先级: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检测到总线电平和自己发的不一致,就知道自己输了,乖乖退出,转为接收模式。
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,结果总线上的其他节点都把它当成扩展帧处理,数据全对不上。
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 本章知识结构图
下面这张图把今天讲的内容串起来了:
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帧的“血肉”。