3、CANFD数据链路层详解:CANFD帧结构

好,咱们进入正题。数据链路层,说白了就是CANFD协议最核心的「骨架」。你想想看,所有的数据在总线上飞来飞去,总得有个规矩吧?这个规矩就是帧结构。我刚开始接触CANFD时,总觉得帧结构就是一堆位的排列组合,背下来就行了。后来在项目里踩了坑才明白——不理解帧结构的设计意图,你根本调不好通信。

今天这一章,我带你彻底吃透CANFD的帧结构。咱们从最基础的帧类型开始,再到BRS位、ESI位这些「新玩意儿」,最后把DLC编码规则和数据场长度掰开揉碎了讲清楚。

3.1 帧类型:标准帧 vs 扩展帧,数据帧 vs 远程帧

CANFD的帧类型,其实和传统CAN一样,分两大类:

  • 按ID长度分:标准帧(11位ID)和扩展帧(29位ID)
  • 按用途分:数据帧(带数据)和远程帧(不带数据,只请求)

组合起来就是四种:标准数据帧、标准远程帧、扩展数据帧、扩展远程帧。嗯,这里要注意——CANFD只支持数据帧,远程帧在CANFD里其实是被废弃的。为什么?因为CANFD的设计初衷是提升数据传输效率,远程帧这种「只问不答」的玩法,在高速场景下反而容易造成总线混乱。我个人习惯,在新项目中直接禁用远程帧,省心。

咱们重点看数据帧。标准帧和扩展帧的区别,说白了就是仲裁场里ID的长度不同。标准帧的ID是11位,扩展帧是29位。你可能会问:「29位ID有什么用?」我在一个多节点车载网络中遇到过——11位ID根本不够分配,节点一多,ID冲突就来了。换成29位扩展帧后,每个节点都能分到唯一的ID,问题迎刃而解。

3.2 BRS位与ESI位的深度解析

这两个位,是CANFD帧里最「灵魂」的东西。咱们一个一个说。

3.4.1 BRS位:比特率切换的开关

BRS,全称是Bit Rate Switch。它的作用很简单——告诉接收方:「我要加速了!」

在CANFD帧中,从BRS位之后的数据场开始,可以切换到更高的比特率(比如2Mbps、5Mbps),而仲裁场和控制场的前半部分仍然使用标准CAN的比特率(比如500kbps)。为什么要这么设计?因为仲裁过程需要所有节点同步,用低速更可靠;而数据传输追求效率,用高速更划算。

我举个例子:你开车在市区(仲裁场)得慢点,上了高速(数据场)就可以踩油门。BRS位就是那个「踩油门」的信号。

重要提示: BRS位为隐性(1)时,表示启用比特率切换;为显性(0)时,表示不切换,全程使用标准比特率。

我在项目中遇到过一个问题:某个节点发送的帧BRS位总是隐性,但接收节点却无法正确解析高速数据。排查了半天,发现是接收节点的时钟精度不够,导致高速采样点偏移。嗯,这里要注意——启用BRS位后,所有参与通信的节点都必须支持CANFD,并且时钟精度要达标

3.4.2 ESI位:错误状态的「告警灯」

ESI,全称是Error State Indicator。它表示发送节点的当前错误状态。

  • ESI = 0(显性):发送节点处于「错误主动」状态,一切正常。
  • ESI = 1(隐性):发送节点处于「错误被动」状态,它已经出过错了,正在「小心行事」。

你想想看,这个位有什么用?说白了,就是一个「健康指示灯」。接收节点看到ESI位为1,就知道对方可能不太靠谱,可以采取一些措施,比如降低通信频率、请求重发等。

我曾经在一个工业控制项目中,发现某个节点偶尔会丢帧。抓了波形一看,它的ESI位经常是1。进一步排查,发现是它的CAN收发器供电不稳,导致发送错误。修复供电后,ESI位恢复正常,丢帧问题也消失了。所以,ESI位是诊断节点健康状态的一个「快照」,调试时多留意它,能省不少事。

3.3 DLC编码规则与数据场长度(0-64字节)

DLC,Data Length Code,数据长度码。它位于控制场,用4个位来表示数据场的字节数。

传统CAN的DLC编码很简单:0-8对应0-8字节。但CANFD支持0-64字节,4个位最多只能表示0-15,怎么映射到64呢?这里就有讲究了。

CANFD的DLC编码规则如下:

DLC值(二进制) 传统CAN数据长度(字节) CANFD数据长度(字节)
0000 - 1000 0 - 8 0 - 8
1001 (无效) 12
1010 (无效) 16
1011 (无效) 20
1100 (无效) 24
1101 (无效) 32
1110 (无效) 48
1111 (无效) 64

看到了吗?DLC值从9到15,分别映射到12、16、20、24、32、48、64字节。这种「跳跃式」映射,是为了在有限的4位空间里,覆盖最常用的数据长度。我个人习惯,如果数据长度不是这些标准值,就向上取整到下一个映射值,比如9字节的数据,DLC就填1001(对应12字节),多余字节填充0。

小技巧: 在代码中,可以用一个查找表来实现DLC到字节数的转换,效率很高。比如:
const uint8_t dlc_to_len[] = {0,1,2,3,4,5,6,7,8,12,16,20,24,32,48,64};
警告: 千万不要在CANFD帧中使用DLC值9-15但数据场长度小于8字节!这会导致接收方解析错误,甚至触发错误帧。我曾经见过一个新手工程师,为了「省带宽」把DLC设为9但只发8字节数据,结果整个网络都乱了套。

数据场长度从0到64字节,意味着你可以一次传输更多的数据。比如,一个ECU的固件升级包,传统CAN要分几十次发送,CANFD一次就能搞定。但要注意,数据场越长,占用总线的时间也越长,如果网络中有实时性要求高的节点,建议把数据场控制在8-16字节以内。

好了,这一章的内容就到这里。帧结构是CANFD的基石,理解了它,后面的性能优化才能有的放矢。下一章,咱们聊聊CANFD的仲裁机制和位填充规则——这两个东西,才是决定总线效率的关键。