1. CAN FD 协议基础:从诞生到实战

大家好,我是你们这趟 OBD-II 移植之旅的向导。今天咱们先聊聊 CAN FD。说实话,我第一次接触 CAN FD 是在一个车载网关项目上,当时客户要求把诊断速率提到 2 Mbps,我第一反应是「传统 CAN 2.0 最高才 1 Mbps,这怎么搞?」后来才发现,CAN FD 就是为这种场景准备的。

这一章,我会从 CAN FD 的诞生背景讲起,带你搞清楚它和传统 CAN 2.0 到底差在哪,再深入数据帧结构和位速率切换机制。嗯,这些都是后面移植协议栈的基石,咱们得打牢了。

1.1 CAN FD 的诞生背景

传统 CAN 2.0 从 1980 年代用到现在,可靠性没得说。但到了 2010 年以后,汽车电子系统越来越复杂,问题就暴露出来了:

  • 带宽瓶颈:传统 CAN 最高 1 Mbps,刷写一个 2 MB 的 ECU 固件,光传输就要 16 秒以上。你想想看,产线上每台车刷十几个 ECU,时间成本根本扛不住。
  • 有效载荷太小:一帧最多 8 字节数据。传输诊断描述符或标定数据时,需要大量帧拆分和重组,协议栈开销很大。
  • 实时性要求:高级驾驶辅助系统(ADAS)和线控底盘需要更高带宽和更低延迟,传统 CAN 已经力不从心。

所以,Bosch 在 2012 年推出了 CAN FD(CAN with Flexible Data-Rate)。说白了,就是在保留 CAN 物理层和总线仲裁机制的基础上,把数据段速率提上去,把有效载荷翻倍。我当年在产线上看到 CAN FD 刷写速度从 1 Mbps 跳到 5 Mbps 时,心里只有一个字:爽。

核心驱动力:CAN FD 不是取代 CAN 2.0,而是向后兼容的升级方案。同一总线上,CAN 2.0 节点和 CAN FD 节点可以共存。

1.2 CAN FD 与传统 CAN 2.0 的区别

咱们直接上表格,对比最关键的几个维度。我个人习惯把这种对比表贴在实验室墙上,写代码时瞄一眼就能避坑。

对比项 传统 CAN 2.0 CAN FD
最大数据场长度 8 字节 64 字节
最大位速率(数据段) 1 Mbps 8 Mbps(典型 2-5 Mbps)
位速率切换 不支持 支持(仲裁段 + 数据段双速率)
帧格式 标准帧 / 扩展帧 标准帧 / 扩展帧 + FD 标志
CRC 校验 15 位 / 17 位 17 位 / 21 位(含填充位保护)
向后兼容 — 与 CAN 2.0 节点共存

这里有个关键点:仲裁段速率不变。为什么?因为总线仲裁需要所有节点在同一速率下竞争总线。CAN FD 只在数据段提速,仲裁段仍然用传统速率(比如 500 kbps)。这样,CAN 2.0 节点看到 FD 帧的仲裁段时,能正常参与仲裁,不会误判。

我曾经踩过的坑:在混合总线上,如果 CAN FD 节点的仲裁段速率和 CAN 2.0 节点不一致,总线会直接报错。所以,仲裁段速率必须统一配置。

1.3 CAN FD 数据帧结构详解

CAN FD 的数据帧结构,和传统 CAN 2.0 很像,但多了几个关键字段。咱们从帧头到帧尾,一个一个拆开看。

1.3.1 帧结构对比

先看一张我手绘的结构对比图。嗯,画图时我特意把 FD 新增的字段标红了,这样更直观。

CAN 2.0 vs CAN FD 数据帧结构对比 CAN 2.0 帧: SOF 仲裁段 控制段 数据段 0-8 字节 CRC ACK EOF CAN FD 帧: SOF 仲裁段 控制段(FD) 数据段(FD) 0-64 字节 CRC(FD) ACK EOF 图例说明: 与 CAN 2.0 相同的字段 CAN FD 新增/修改的字段 数据段(速率可切换)

1.3.2 关键字段解读

咱们重点看 CAN FD 新增和修改的字段:

  • EDL(Extended Data Length):位于控制段,用于区分 CAN 2.0 帧和 CAN FD 帧。EDL = 1 表示 FD 帧,EDL = 0 表示传统帧。
  • BRS(Bit Rate Switch):位速率切换标志。BRS = 1 时,数据段切换到高速率;BRS = 0 时,数据段保持仲裁段速率。
  • ESI(Error State Indicator):发送节点的错误状态。ESI = 1 表示节点处于被动错误状态,接收方可以据此调整行为。
  • DLC(Data Length Code):虽然名字没变,但编码方式变了。CAN FD 支持 0-64 字节,DLC 编码表如下:
DLC 值 CAN 2.0 数据长度(字节) CAN FD 数据长度(字节)
0-8 0-8 0-8
9 8 12
10 8 16
11 8 20
12 8 24
13 8 32
14 8 48
15 8 64

小技巧:写 CAN FD 驱动时,DLC 解码不能直接用 CAN 2.0 的查表法。我习惯用一个 16 元素的数组做映射,DLC 值直接索引,省去 if-else 嵌套。

1.4 位速率切换机制

这是 CAN FD 最核心的机制,也是很多工程师容易搞混的地方。咱们用一张流程图来理清逻辑。

CAN FD 位速率切换流程 仲裁段(低速) 速率:500 kbps(典型) 控制段:BRS = 1? (位速率切换标志) BRS = 0 数据段保持低速 BRS = 1 数据段切换到高速 CRC 段 + ACK + EOF 速率与数据段一致 注意:CRC 段、ACK 段和 EOF 段的速率与数据段保持一致,仲裁段始终为低速。

1.4.1 切换时机

位速率切换发生在控制段之后、数据段开始之前。具体来说:

  1. 仲裁段:所有节点以低速(如 500 kbps)参与总线仲裁,发送 SOF、标识符、控制段。
  2. BRS 位:发送节点在控制段中设置 BRS 位。接收节点在 BRS 位采样点之后,立即切换到高速时钟。
  3. 数据段:以高速(如 2 Mbps)传输 0-64 字节数据。
  4. CRC 段:以高速传输 CRC 校验码。
  5. ACK 段 + EOF:在 CRC 段结束后,切换回低速,完成 ACK 和帧结束。

我曾经踩过的坑:切换时机非常敏感。如果接收节点的 PLL 锁定时间太长,或者采样点配置不当,数据段的第一位就可能采样错误。我建议在配置 CAN FD 控制器时,把数据段的采样点设在 70%-75% 位置,留足余量。

1.4.2 速率配置实战

在实际项目中,仲裁段和数据段的速率配置是分开的。以 STM32H7 的 FDCAN 外设为例,配置代码大致如下:

/* 仲裁段:500 kbps,数据段:2 Mbps */
FDCAN_InitTypeDef hfdcan;

/* 仲裁段时序 */
hfdcan.NominalPrescaler = 2;      // 预分频
hfdcan.NominalSyncJumpWidth = 1;  // 同步跳转宽度
hfdcan.NominalTimeSeg1 = 13;      // TSEG1
hfdcan.NominalTimeSeg2 = 2;       // TSEG2

/* 数据段时序 */
hfdcan.DataPrescaler = 1;         // 预分频
hfdcan.DataSyncJumpWidth = 1;    // 同步跳转宽度
hfdcan.DataTimeSeg1 = 9;         // TSEG1
hfdcan.DataTimeSeg2 = 2;         // TSEG2

/* 使能位速率切换 */
hfdcan.FrameFormat = FDCAN_FRAME_FD_BRS;

这里有个细节:数据段的预分频通常比仲裁段小,因为数据段需要更高的位时钟。我习惯先算好仲裁段的时序,再根据目标数据速率反推数据段参数。

1.5 本章小结

这一章咱们把 CAN FD 的基础打了一遍。从诞生背景到帧结构,到位速率切换,每个点都跟后面的 OBD-II 协议栈移植直接相关。特别是位速率切换机制,你在写 CAN FD 驱动时一定会反复用到。