3. CAN FD数据链路层(上):帧结构解析

各位同学,今天我们来啃一块硬骨头——CAN FD的帧结构。说实话,我刚接触CAN FD时,看着那一堆场(Field)也是头大。但后来我发现一个窍门:你把它想象成快递包裹,每个字段就是包裹上的不同标签。这么一想,就好理解多了。

3.1 帧结构总览:一个快递包裹的拆解

CAN FD的报文帧,说白了就是一串有格式的二进制数据。它从总线空闲状态开始,到EOF结束,中间包含了7个主要部分。我习惯把这7个部分分成三组:

  • 头部:SOF + 仲裁场 + 控制场 —— 相当于快递单上的发件人、收件人信息
  • 身体:数据场 —— 就是包裹里的实际货物
  • 尾部:CRC场 + ACK场 + EOF —— 相当于签收确认和封条

下面这张图把整个帧结构串起来了,建议你保存下来对照着看。

CAN FD 帧结构总览(标准帧) SOF 1 bit 仲裁场 12 bits (ID+RTR+IDE) 控制场 8 bits (EDL+BRS+ESI+DLC) 数据场 0~64 bytes CRC场 21 bits ACK场 2 bits EOF 7 bits 💡 关键变化: CAN FD 在控制场新增了 EDL、BRS、ESI 三个标志位,数据场最大支持 64 字节,CRC 场也扩展到了 21 位。 头部(Header) 身体 尾部(Trailer) 帧起始 → 仲裁 → 控制 → 数据 → CRC → 应答 → 帧结束 总长度:标准帧约 64~1088 bits(取决于数据场长度)

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的时候就会顺手很多。