2. CAN FD 帧结构详解:标准帧、扩展帧、数据段、控制段、CRC 段、ACK 段

做 CAN FD 调试,第一件事就是看懂帧结构。你想想看,逻辑分析仪抓回来一堆波形,要是连哪个 bit 对应什么字段都分不清,那跟看天书没区别。我刚开始接触 CAN FD 时,就吃过这个亏——对着波形数了半天,结果发现把 CRC 段和 ACK 段搞混了。嗯,今天咱们把这层窗户纸捅破。

2.1 帧类型速览

CAN FD 的帧分好几种:数据帧、远程帧、错误帧、过载帧。实战里最常打交道的是数据帧。数据帧内部又分两种格式——标准帧和扩展帧。区别就在仲裁段的长度:标准帧用 11 位标识符,扩展帧用 29 位。

我个人习惯在项目初期统一用标准帧,省事。但遇到多节点、高优先级分配的场合,扩展帧的 29 位 ID 能让你玩出更多花样。

特性标准帧扩展帧
标识符长度11 位11 位基本 ID + 18 位扩展 ID
IDE 位显性(0)隐性(1)
控制段结构IDE + r0 + BRS + ESI + DLCIDE + r0 + BRS + ESI + DLC
仲裁场占用 bit 数12(含 RTR)32(含 SRR、IDE 等)
常见场景普通 ECU 通信车载诊断、网关转发

实际抓波形时,你可以通过 IDE 位一眼判断:IDE 为 0 就是标准帧,为 1 就是扩展帧。

2.2 从 SOF 到 EOF:逐段拆解

一个完整的 CAN FD 数据帧,按时间顺序依次走过这些段落:

SOF(帧起始)

就 1 个显性位。说白了就是发信器拉低总线,告诉所有节点:“我要开始说话了”。逻辑分析仪上看到第一个下降沿就是 SOF。

仲裁段

这里放标识符和 RTR/SRR 位。标准帧 11 位 ID + RTR;扩展帧 11 位基本 ID + SRR + IDE + 18 位扩展 ID。仲裁段的原理是靠“显性优先”——ID 越小优先级越高。我曾在项目中遇到过两个节点同时发相同 ID 的帧,结果因为相位偏差导致位填充错误,波形直接崩了……后来加了发送延迟补偿才解决。

注意: 扩展帧的 SRR 位必须发隐性(1),否则会被误判为标准帧的 RTR 显性位,导致仲裁失败。这个坑我踩过,记得检查。

控制段

控制段由这几个位组成:

  • IDE:标识符扩展标志。前面说过,0 标准,1 扩展。
  • r0:保留位,必须发显性(0)。很多分析仪视它为 r0,不要被它干扰。
  • BRS:波特率切换标志。这是 CAN FD 的核心特色。BRS = 1 时,从数据段开始切换到高速模式(比如 5 Mbps);BRS = 0 则全程用仲裁段波特率。
  • ESI:错误状态指示。发信器如果处于被动错误状态,ESI = 1;否则为 0。你可以用它判断节点健康度。
  • DLC:数据长度编码,4 位。CAN FD 的 DLC 和经典 CAN 不一样——经典 CAN 只能表示 0~8 字节,CAN FD 可以映射到 0~64 字节。具体映射规则看下表:
DLC 编码数据字节数(CAN FD)
0~80~8(与经典 CAN 一致)
912
1016
1120
1224
1332
1448
1564
小技巧: 用逻辑分析仪看 DLC 时,别直接按二进制转十进制就去数。你可以把 DLC 波形和移位寄存器同步对齐,再查这张表。我习惯在分析脚本里写个查找函数,省得每次手工算。

数据段

从 BRS = 1 之后就开始数据段。如果 BRS 没切换,数据段仍然用原波特率。数据段包含 0~64 字节有效数据,加上必要的位填充(经典 CAN 用的是位填充规则,CAN FD 的数据段采用更严格的固定填充位策略)。注意:如果 DLC 指示 12 字节,那么数据段实际传输的就是 12 个字节,不够的不会补零——发多少就是多少。

我曾经调试过一个节点,它发送的 DLC 是 8,但数据段里只有 4 字节有用信息。接收方却按 8 字节读取,把无效字节当有效数据处理,导致控制逻辑出错。所以建议:发信器必须严格按 DLC 填充数据,收信器也要检查 DLC 与实际数据长度的匹配。

CRC 段(校验段)

CRC 段包含两部分:

  • CRC 序列:CAN FD 的 CRC 多项式比经典 CAN 更长。标准帧用 15 位 CRC,扩展帧用 17 位 CRC(若数据长度 > 16 字节则用 21 位)。这是为了保护更多的数据。
  • CRC 分隔符:1 个隐性位。起隔离作用,防止 CRC 和 ACK 搅在一起。

逻辑分析仪抓到的 CRC 段是一长串 bit,你需要知道从哪个 bit 开始取。通常控制段的最后一个 bit(即 DLC 的第 4 位)之后就立刻进入 CRC 段。我习惯在分析仪上设置一个“从 SOF 开始计数”的标记,然后直接定位到 DLC 结束后的首位,就是 CRC 的起点。

核心区别: 经典 CAN 的 CRC 只覆盖 SOF、仲裁段、控制段和数据段;而 CAN FD 的 CRC 还覆盖了填充位和 BRS/ESI 等控制位。所以别拿经典 CAN 的 CRC 算法去算 CAN FD 帧,那是错的。

ACK 段(应答段)

ACK 段就 2 个位:

  • ACK 时隙:发信器发送一个隐性位(1)。接收器如果正确收到帧,会在这一位期间把总线拉成显性(0)。所以逻辑分析仪看到这个位是显性,就说明至少有一个节点应答了。如果它保持隐性,那帧就是“没人理”的状态——解析时可以标记为 ACK 错误。
  • ACK 分隔符:1 个隐性位。

你可能会问:为什么 ACK 时隙是隐性起始的?因为 CAN 总线是线与机制,隐性被显性覆盖。这样设计可以保证:只要有一个接收器 ACK,总线就变显性;没有接收器时总线保持隐性——发信器自然知道自己发送失败了。

EOF 和 IFS

EOF(帧结束)是 7 个连续的隐性位,标志着帧的结束。之后是 IFS(帧间空间),至少 3 个隐性位,用于总线恢复。逻辑分析仪在解码时通常把 EOF 之后的下一个 SOF 之前的部分都归为 IFS。

2.3 帧结构总图

为了让你一眼看清整体结构,我画了一张 CAN FD 数据帧的位级组成图。注意这张图是按标准帧画的,扩展帧只是仲裁段长一些,其他段落完全一样。

CAN FD 数据帧结构(标准帧 + BRS=1 示例) SOF 1 bit 仲裁段 12 bit (ID+RTR) 控制段 IDE+r0+BRS+ESI+DLC 5 bit (不含填充) 数据段 0~64 字节 + 填充位 可变长度 CRC 段 15/17/21 bit + 分隔符 CRC + CRC Delim ACK 2 bit 时隙+分隔符 EOF 7 bit IFS BRS=1 时,波特率从数据段切换 (仲裁段、控制段前半段使用原波特率) CRC 覆盖范围 从 SOF 开始到数据段结束(含填充位) 时间 → CAN FD vs 经典 CAN 帧结构差异 • 控制段多了 BRS 和 ESI 位,DLC 编码支持到 64 字节 • 数据段长度可变(0~64 字节),且采用不同的位填充规则(CFD 使用固定填充位) • CRC 段多项式更长,覆盖范围包括 BRS、ESI 和填充位 • ACK 段、EOF、IFS 与经典 CAN 完全兼容 • 扩展帧与标准帧的区别仅在仲裁段,其他段落一致

2.4 实战中容易踩的坑

  1. BRS 切换时机搞错——BRS 位本身是在控制段里面发的,它之后的第一个数据位才切换波特率。逻辑分析仪如果采样率没跟上,会直接把 BRS 后面的数据段读成一堆乱码。我建议设置分析仪的采样率至少是高速波特率的 5 倍。
  2. DLC 与实际数据长度不匹配——有些节点在发送时 DLC 填了 8,但实际只发了 4 字节有效数据。收信方不得不从内存里读出 8 字节,其中 4 字节是随机值。这个问题我遇到过,最后在协议栈里加了个校验才抓住。
  3. CRC 校验失败时不知错在哪——逻辑分析仪通常只会告诉你“CRC 错误”,不告诉你具体哪个 bit 算错了。你可以手动把波形导出,用 CAN FD 的 CRC 多项式重算一遍,对比接收到的 CRC 序列。嗯,这个操作虽然麻烦,但定位问题最快。
  4. ACK 时隙被噪声误覆盖——如果总线上有噪声脉冲正好落在 ACK 时隙,可能使得隐性位被误判为显性。分析仪会认为 ACK 成功,但实际节点并未应答。这属于偶发故障,需要多次捕获对比。
我的个人习惯: 在逻辑分析仪上同时抓取 CAN 总线的 RX 和 TX 信号,这样能清晰看到发信器发出的 ACK 时隙(隐性)和接收器拉低的时刻。配合时间戳,任何异常都能快速定位。