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 + DLC | IDE + 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 的帧,结果因为相位偏差导致位填充错误,波形直接崩了……后来加了发送延迟补偿才解决。
控制段
控制段由这几个位组成:
- 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~8 | 0~8(与经典 CAN 一致) |
| 9 | 12 |
| 10 | 16 |
| 11 | 20 |
| 12 | 24 |
| 13 | 32 |
| 14 | 48 |
| 15 | 64 |
数据段
从 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 的起点。
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 数据帧的位级组成图。注意这张图是按标准帧画的,扩展帧只是仲裁段长一些,其他段落完全一样。
2.4 实战中容易踩的坑
- BRS 切换时机搞错——BRS 位本身是在控制段里面发的,它之后的第一个数据位才切换波特率。逻辑分析仪如果采样率没跟上,会直接把 BRS 后面的数据段读成一堆乱码。我建议设置分析仪的采样率至少是高速波特率的 5 倍。
- DLC 与实际数据长度不匹配——有些节点在发送时 DLC 填了 8,但实际只发了 4 字节有效数据。收信方不得不从内存里读出 8 字节,其中 4 字节是随机值。这个问题我遇到过,最后在协议栈里加了个校验才抓住。
- CRC 校验失败时不知错在哪——逻辑分析仪通常只会告诉你“CRC 错误”,不告诉你具体哪个 bit 算错了。你可以手动把波形导出,用 CAN FD 的 CRC 多项式重算一遍,对比接收到的 CRC 序列。嗯,这个操作虽然麻烦,但定位问题最快。
- ACK 时隙被噪声误覆盖——如果总线上有噪声脉冲正好落在 ACK 时隙,可能使得隐性位被误判为显性。分析仪会认为 ACK 成功,但实际节点并未应答。这属于偶发故障,需要多次捕获对比。