第2章:CAN数据帧结构——标准帧与扩展帧
CAN总线上的数据,说白了就是靠一个个“帧”来传递的。我刚开始接触CAN协议栈时,总觉得帧结构就是一堆位段的堆砌,背下来就行了。直到有一次调试一个电机控制器,发现报文老是丢,查了三天才发现是ACK场理解不到位。嗯,从那以后我再也不敢小看这些“位段”了。
这一章,咱们就把CAN数据帧的每一块掰开揉碎,看看它到底长什么样,每个场在干什么活。
2.1 标准帧 vs 扩展帧:差在哪?
CAN 2.0规范定义了两种帧格式:标准帧(11位ID)和扩展帧(29位ID)。你想想看,为什么要有两种?说白了就是早期11位ID够用,后来节点多了,11位不够分,才搞出了29位。
| 对比项 | 标准帧 | 扩展帧 |
|---|---|---|
| 标识符长度 | 11位 | 29位 |
| IDE位 | 显性(0) | 隐性(1) |
| 仲裁场长度 | 12位 | 32位 |
| 最大节点数 | 约2000个 | 约5亿个 |
| 兼容性 | 所有控制器支持 | 需扩展帧控制器 |
2.2 数据帧的七段结构
一个完整的数据帧,从开始到结束,一共7个场。我习惯把它想象成一封信:
- SOF —— 信封上的“开始”戳
- 仲裁场 —— 收件人地址(谁优先级高谁先发)
- 控制场 —— 信里装了几页纸
- 数据场 —— 信的内容(0~8字节)
- CRC场 —— 防伪码
- ACK场 —— 收件人回执
- EOF —— 信封封口
2.3 逐场拆解
2.3.1 SOF(帧起始)
SOF只有1位,必须是显性(逻辑0)。为什么?因为总线空闲时是隐性(逻辑1),SOF的显性跳变就是告诉所有节点:“我要开始发了!”
2.3.2 仲裁场
这是CAN协议最精彩的部分。标准帧的仲裁场包含:
- 11位标识符:ID越小,优先级越高
- RTR位:远程帧请求位(数据帧为显性,远程帧为隐性)
- IDE位:标识符扩展位(标准帧为显性)
扩展帧的仲裁场更复杂:
- 29位标识符:分基本ID(11位)和扩展ID(18位)
- SRR位:替代远程请求位(总是隐性)
- IDE位:隐性(表示后面还有18位扩展ID)
- RTR位:同标准帧
仲裁的原理很简单:多个节点同时发送时,谁先发隐性(1)谁就退出。我习惯把ID理解成“数字越小,嗓门越大”。
2.3.3 控制场
控制场共6位:
- r0、r1:保留位(必须为显性)
- DLC[3:0]:数据长度码,表示数据场有多少字节
DLC的取值范围是0~8,对应数据场0~8字节。注意:DLC如果大于8,很多控制器会当作8处理,但有些会报错。我建议你严格遵守0~8。
| DLC值 | 数据场长度(字节) | 常见用途 |
|---|---|---|
| 0 | 0 | 远程帧、心跳包 |
| 1~4 | 1~4 | 传感器数据、状态字 |
| 5~8 | 5~8 | 诊断数据、固件升级块 |
2.3.4 数据场
数据场就是你要传的实际数据,最多8字节。为什么是8?这是上世纪80年代的设计,当时觉得8字节够用了。现在CAN FD已经把这个限制扩展到了64字节,但经典CAN还是8字节。
2.3.5 CRC场
CRC场包含15位CRC校验码和1位CRC分隔符(隐性)。CRC的生成多项式是固定的:
// CRC-15 生成多项式
// x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1
// 对应二进制:1 1000 1001 1001 1001
#define CRC15_POLY 0x4599
CRC的计算范围是从SOF到数据场结束。接收节点会重新计算CRC,如果和发送的不一致,就丢弃这个帧。
2.3.6 ACK场
ACK场只有2位:
- ACK槽:发送节点发隐性(1),接收节点如果正确接收,就拉成显性(0)
- ACK分隔符:必须是隐性(1)
这里有个关键点:只要有一个节点正确接收,ACK槽就是显性。如果总线上没有任何节点响应(比如终端电阻没接好),发送节点就收不到显性ACK,会触发错误帧。
2.3.7 EOF(帧结束)
EOF是7位隐性位。为什么是7位?因为位填充机制最多连续5个相同位,7位隐性足够让所有节点确认帧结束了。EOF之后还有3位ITM(帧间隔),然后才能发下一帧。
2.4 位填充机制
CAN协议有个特殊规则:连续5个相同电平后,必须插入一个相反电平。这叫位填充。目的是保证时钟同步。
举个例子:
原始数据:11111 00000 11111
填充后: 11111 0 00000 1 11111 0
^ ^ ^
填充位 填充位 填充位
注意:SOF到CRC场之间才做位填充,ACK场和EOF不做。为什么?因为ACK场需要多个节点同时驱动,位填充会干扰仲裁。
2.5 错误帧与过载帧
除了数据帧和远程帧,CAN总线上还有两种特殊帧:
- 错误帧:6个显性位 + 8个隐性位。任何节点检测到错误,就发错误帧中断当前通信。
- 过载帧:6个显性位 + 8个隐性位。接收节点来不及处理时,可以发过载帧要求发送方等一下。
这两种帧的优先级高于数据帧。所以如果你的总线上错误帧太多,正常数据帧就发不出去。我见过一个案例,总线错误率超过30%,整个系统直接瘫痪。
本章核心要点:
- 标准帧11位ID,扩展帧29位ID,IDE位决定格式
- 仲裁场决定优先级,ID越小优先级越高
- 数据场最多8字节,DLC必须≤8
- CRC场15位校验,覆盖SOF到数据场
- ACK场需要至少一个节点响应,否则触发错误
- EOF为7位隐性,之后有3位帧间隔
- 位填充每5个相同位插入1个相反位