2、CAN数据帧结构:帧起始、仲裁场、控制场、数据场、CRC场、应答场、帧结束
CAN总线上的数据,说白了就是靠一帧一帧的报文来传递的。每一帧报文就像一封信,有固定的格式和段落。我刚开始接触CAN时,总觉得这些字段名称很绕,后来拆了几个实际报文,才真正理解每个部分存在的意义。
标准CAN数据帧(Data Frame)一共由7个段组成。咱们按顺序一个一个拆开看。
2.1 帧起始(SOF)
帧起始只有一个bit,固定为显性电平(逻辑0)。
为什么是显性?因为总线空闲时是隐性电平(逻辑1)。SOF用显性电平,相当于在安静的教室里突然拍了一下桌子——所有节点都知道“要开始发消息了”。
2.2 仲裁场(Arbitration Field)
仲裁场包含两部分:标识符(ID) 和 RTR位。
| 组成部分 | 标准帧(11位ID) | 扩展帧(29位ID) |
|---|---|---|
| 标识符 | 11 bit,ID.28 ~ ID.18 | 29 bit,ID.28 ~ ID.0 |
| RTR位 | 1 bit,远程帧请求 | 1 bit,远程帧请求 |
| IDE位 | 显性(0),表示标准帧 | 隐性(1),表示扩展帧 |
仲裁场的核心作用就是解决总线冲突。多个节点同时发送时,谁的ID数值小,谁就优先发。你想想看,ID越小优先级越高,这跟咱们平时“数字越小越靠前”的直觉正好相反。
2.3 控制场(Control Field)
控制场共6个bit,结构如下:
- IDE位(1 bit):标准帧为显性0,扩展帧为隐性1
- 保留位r0(1 bit):显性,留给未来扩展
- DLC(4 bit):数据长度代码,表示数据场有多少个字节
DLC的取值范围是0~8,对应数据场0~8个字节。注意,DLC的值不能超过8,超过8的报文会被视为无效。
2.4 数据场(Data Field)
数据场是报文的“正文”,长度由DLC决定,0~8个字节。数据场的内容完全由用户定义,CAN协议本身不关心里面装的是什么。
举个例子,一个车速报文的数据场可能是这样的:
Byte 0: 车速高字节 (0x00 ~ 0xFF)
Byte 1: 车速低字节 (0x00 ~ 0xFF)
Byte 2: 方向标志 (0x00=前进, 0x01=后退)
Byte 3~7: 保留或填充
我个人习惯把数据场的字节顺序和位顺序画成表格贴在调试台旁边,省得每次都要翻协议文档。
2.5 CRC场(CRC Field)
CRC场包含两部分:
- CRC序列(15 bit):由SOF到数据场的所有位计算得出
- CRC界定符(1 bit):固定为隐性电平
CRC的计算多项式是:x¹⁵ + x¹⁴ + x¹⁰ + x⁸ + x⁷ + x⁴ + x³ + 1。接收节点收到报文后,会用同样的多项式重新计算CRC,如果结果不一致,就说明报文在传输过程中被干扰了。
2.6 应答场(ACK Field)
应答场只有2个bit:
- ACK Slot(1 bit):发送节点发隐性,接收节点如果正确收到报文,就把它拉成显性
- ACK界定符(1 bit):固定为隐性
这个机制很有意思。发送节点只负责发,不负责确认。所有接收节点共同决定是否应答。只要有一个节点正确收到报文,ACK Slot就会被拉成显性。如果没有任何节点应答,发送节点就知道“没人收到我的消息”。
2.7 帧结束(EOF)
帧结束由7个连续的隐性位组成。发送节点发完这7个隐性位后,总线恢复空闲状态,其他节点就可以开始发送了。
为什么是7个?因为CRC界定符、ACK Slot、ACK界定符加起来已经占了3个隐性位,再加上7个EOF,总共10个连续的隐性位。这个长度足够让所有节点检测到总线空闲。
2.8 完整帧结构一览
下面这张SVG图把整个CAN数据帧的结构画清楚了,建议保存下来当参考。
2.9 各字段长度汇总
把标准帧和扩展帧的字段长度放在一起对比,一目了然:
| 字段 | 标准帧(11位ID) | 扩展帧(29位ID) |
|---|---|---|
| 帧起始 | 1 bit | 1 bit |
| 仲裁场 | 12 bit | 32 bit |
| 控制场 | 6 bit | 6 bit |
| 数据场 | 0~64 bit | 0~64 bit |
| CRC场 | 16 bit | 16 bit |
| 应答场 | 2 bit | 2 bit |
| 帧结束 | 7 bit | 7 bit |
| 最小帧长 | 44 bit | 64 bit |
| 最大帧长 | 108 bit | 128 bit |