3. CAN FD数据链路层(上):帧结构解析
各位同学,今天我们来啃一块硬骨头——CAN FD的帧结构。说实话,我刚接触CAN FD时,看着那一堆场(Field)也是头大。但后来我发现一个窍门:你把它想象成快递包裹,每个字段就是包裹上的不同标签。这么一想,就好理解多了。
3.1 帧结构总览:一个快递包裹的拆解
CAN FD的报文帧,说白了就是一串有格式的二进制数据。它从总线空闲状态开始,到EOF结束,中间包含了7个主要部分。我习惯把这7个部分分成三组:
- 头部:SOF + 仲裁场 + 控制场 —— 相当于快递单上的发件人、收件人信息
- 身体:数据场 —— 就是包裹里的实际货物
- 尾部:CRC场 + ACK场 + EOF —— 相当于签收确认和封条
下面这张图把整个帧结构串起来了,建议你保存下来对照着看。
3.2 逐个字段深度解析
3.2.1 SOF(帧起始)
SOF就是一个显性位(逻辑0)。它告诉总线上所有节点:「注意,我要开始发数据了!」。嗯,这里有个细节——SOF的下降沿用来同步所有节点的时钟。我在调试一个多节点系统时,发现有个节点总是丢帧,最后查出来是它的晶振偏差太大,SOF同步没跟上。所以,晶振精度真的很重要。
3.2.2 仲裁场
仲裁场是CAN协议最巧妙的设计之一。它包含:
- 标识符(ID):标准帧11位,扩展帧29位。ID越小,优先级越高。
- RTR位:远程帧请求位。CAN FD中这个位固定为显性(0)。
- IDE位:标识符扩展位。0表示标准帧,1表示扩展帧。
仲裁的原理说白了就是「谁先发0谁赢」。总线是线与逻辑,显性位(0)会覆盖隐性位(1)。两个节点同时发数据时,逐位比较ID,谁先发出0谁就抢到总线。我见过一个新手工程师把两个节点的ID设成一样,结果总线乱成一锅粥——仲裁永远分不出胜负。
避坑指南:我曾经在一个项目中,把节点A的ID设为0x100,节点B的ID设为0x200。结果节点A永远抢不到总线。为什么?因为0x100的二进制是0001 0000 0000,0x200是0010 0000 0000,前三位都是0,但第四位A是1、B是0,所以B赢了。记住:ID数值越小,优先级越高!
3.2.3 控制场
控制场是CAN FD和传统CAN最大的区别所在。它包含:
| 位 | 名称 | 说明 |
|---|---|---|
| bit 0 | EDL | CAN FD 标志位。1=FD帧,0=传统CAN帧 |
| bit 1 | BRS | 比特率切换标志。1=数据段切换到高速率,0=不变 |
| bit 2 | ESI | 错误状态指示。0=主动错误,1=被动错误 |
| bit 3-7 | DLC | 数据长度码。0~8对应0~8字节,9~15对应12/16/20/24/32/48/64字节 |
这里有个容易踩的坑:DLC的编码不是线性的。比如DLC=9表示数据场有12字节,而不是9字节。我第一次用STM32CubeMX配CAN FD时,就因为这个搞错了数据长度,调试了半天才发现。
小技巧:DLC值与实际字节数的对应关系:0-8→0-8,9→12,10→16,11→20,12→24,13→32,14→48,15→64。建议把这个表贴在工位上。
3.2.4 数据场
数据场就是实际要传输的数据。传统CAN最多8字节,CAN FD最多64字节。你想想看,以前发一个64字节的固件升级包,要拆成8帧,现在一帧搞定,效率提升可不是一星半点。
但要注意:数据场越长,CRC校验越重要。我有个项目,数据场用到48字节,结果CRC没过,排查发现是PCB走线太长导致信号质量下降。后来把速率降下来才解决。
3.2.5 CRC场
CRC场包含CRC序列和CRC分隔符。CAN FD的CRC算法和传统CAN不一样:
- 传统CAN:CRC-15,多项式 x¹⁵ + x¹⁴ + x¹⁰ + x⁸ + x⁷ + x⁴ + x³ + 1
- CAN FD:数据场≤16字节用CRC-17,>16字节用CRC-21
为什么要有两种CRC?说白了,数据越长,CRC的检错能力要求越高。CRC-21比CRC-17多4位,能更好地保护64字节的数据场。
3.2.6 ACK场
ACK场只有2位:ACK槽和ACK分隔符。发送节点发隐性位(1),接收节点如果正确收到,就在ACK槽发显性位(0)来应答。如果没人应答,发送节点就知道出问题了。
我记得有一次调试,发现发送节点一直报ACK错误。用示波器一看,总线上确实没有应答信号。查了半天,原来是接收节点的CAN控制器没使能。所以,遇到ACK错误,先检查接收节点是不是在线。
3.2.7 EOF(帧结束)
EOF是7个隐性位(1)。它告诉所有节点:「这帧结束了,总线可以空闲了」。EOF之后还有3位的ITM(帧间隔),然后才能发下一帧。
3.3 标准帧 vs 扩展帧
标准帧和扩展帧的区别,说白了就是ID长度的不同。标准帧11位ID,扩展帧29位ID。但实际使用中,这个区别带来了一系列连锁变化。
| 对比项 | 标准帧 | 扩展帧 |
|---|---|---|
| ID长度 | 11位 | 29位 |
| IDE位 | 0(显性) | 1(隐性) |
| 仲裁场长度 | 12位 | 32位 |
| 最大节点数 | 2048个 | 约5.3亿个 |
| 帧头开销 | 47位 | 67位 |
| 兼容性 | 所有CAN控制器都支持 | 需要支持扩展帧的控制器 |
重要提醒:标准帧和扩展帧可以在同一条总线上共存。但仲裁时,标准帧的优先级永远高于扩展帧。因为标准帧的IDE位是显性(0),而扩展帧的IDE位是隐性(1)。所以,如果你想让某个节点优先响应,给它配标准帧就对了。
3.4 实际项目中的选择建议
我个人习惯这样选:
- 简单系统(≤10个节点):用标准帧就够了。ID分配简单,调试也方便。
- 复杂系统(>10个节点):用扩展帧。29位ID可以按「设备类型-功能-实例号」来编码,管理起来清晰。
- 混合系统:关键节点用标准帧(高优先级),普通节点用扩展帧。但要注意,标准帧的ID范围是0x000~0x7FF,别和扩展帧的ID冲突。
我曾经在一个车载项目中,把ECU都用扩展帧,传感器都用标准帧。结果传感器总是抢在ECU前面发数据,导致ECU的控制指令延迟。后来把ECU改成标准帧才解决。这个教训告诉我:优先级设计一定要从系统层面考虑,不能只看节点数量。
3.5 本章小结
这一章我们拆解了CAN FD的帧结构,从SOF到EOF,每个字段的作用和注意事项都过了一遍。标准帧和扩展帧的区别也讲清楚了。下一章我们会继续深入数据链路层,重点讲位填充、错误检测和帧过滤——这些都是实际开发中绕不开的内容。
嗯,最后送大家一句话:CAN FD的帧结构就像乐高积木,每个字段都有它的位置和作用。你把它拆明白了,后面配置STM32CubeMX的时候就会顺手很多。