Chapter 08 · CAN 总线 / 汽车电子

CAN FD

经典 CAN 的 8 字节、1Mbps 天花板如何被打破:CAN FD 的可变数据段速率与 64 字节负载,以及 STM32 G4/H7 上的 FDCAN 外设编程。

课程进度50%

关键概念

CAN FD (Flexible Data-Rate)
Bosch 于 2012 年推出的 CAN 升级版,向下兼容经典 CAN 的仲裁机制。两大改进:更大负载(数据段最多 64 字节,经典 CAN 只有 8)和更快数据速率(数据段可切到 2~8 Mbps,仲裁段仍保持 ≤1 Mbps 以兼容总线拓扑)。规范定为 ISO 11898-1:2015。
FDF 位 (FD Format)
帧格式标志位,位于控制场,取代经典 CAN 的保留位 r0。FDF=1 表示这是一帧 CAN FD 报文,接收方据此按 FD 规则解析后续字段;FDF=0 则是经典帧。它是区分“经典 CAN”与“CAN FD”帧的关键位,旧规范里也叫 EDL(Extended Data Length)。
BRS 位 (Bit Rate Switch)
位速率切换位。BRS=1 时,从 BRS 位之后(数据段和 CRC 段)起,总线切换到更高的“数据段波特率”,直到 CRC 界定符后切回仲裁段波特率。BRS=0 则整帧都用仲裁速率。正是 BRS 实现了“仲裁慢、数据快”的双速率机制,大幅提升有效吞吐。
ESI 位 (Error State Indicator)
错误状态指示位,由发送节点填写:ESI=0 表示发送方处于“错误主动”状态(健康),ESI=1 表示处于“错误被动”状态(累积错误较多)。它让网络其他节点无需额外报文即可感知某节点的健康度,便于诊断。经典 CAN 没有此位。
DLC 与实际长度的非线性映射
经典 CAN 里 DLC 直接等于字节数(0~8)。CAN FD 为了用 4 位 DLC 表达更大长度,采用非线性编码:DLC 9~15 分别映射到 12、16、20、24、32、48、64 字节。因此 FD 帧的数据长度不能超过 8 后就随意取值,只能是 {0..8,12,16,20,24,32,48,64} 这几个离散值。
CRC17 / CRC21
经典 CAN 用 15 位 CRC。CAN FD 由于帧更长、位错误概率上升,改用更强的 CRC:数据 ≤16 字节用 CRC17(17 位多项式),>16 字节用 CRC21(21 位多项式),保证长帧的检错能力。此外 FD 帧在 CRC 场加入了固定填充位以提高同步鲁棒性。
FDCAN 外设 (STM32 G4/H7)
STM32F4 的 bxCAN 不支持 FD。真正支持 CAN FD 的是较新型号(STM32G0/G4/H5/H7 等)内置的 FDCAN 控制器,基于 Bosch M_CAN IP。它用一块 Message RAM 存放过滤器、Rx FIFO、Tx Buffer,配置方式与 bxCAN 差别很大:需分别设置仲裁段(Nominal)和数据段(Data)两套位时序。
仲裁段速率 vs 数据段速率
CAN FD 有两个独立波特率。仲裁段(Nominal Bit Timing)决定 ID 竞争阶段的速率,受总线长度与节点数限制,通常仍是 500k/1M。数据段(Data Bit Timing)只在 BRS=1 的数据/CRC 阶段生效,可达 2~8M。因为此时总线上只有一个节点在发(仲裁已结束),可以放心提速。

经典 CAN vs CAN FD 对比

特性经典 CAN (2.0)CAN FD
最大数据长度8 字节64 字节
仲裁段速率最高 1 Mbps最高 1 Mbps(兼容)
数据段速率与仲裁段相同可切到 2~8 Mbps
CRC 长度15 位17 位 / 21 位
关键新增位FDF、BRS、ESI
DLC 编码线性 0~8非线性(9~15 → 12~64)
典型控制器bxCAN (STM32F4)FDCAN (STM32G4/H7)

CAN FD 帧结构与双速率

下图标注了 BRS 位前后的速率切换点——仲裁段慢、数据段快,CRC 界定符后切回:

CAN FD 数据帧(BRS=1 时的双速率) ═══════════════════════════════════════════════════════════════ │◀──── 仲裁段速率(500k/1M) ────▶│◀── 数据段速率(2~8M) ──▶│◀ 仲裁速率 ▶│ ┌───┬───────────┬─────┬─────┬─────┬─────┬──────────┬────────┬───┬─────┬─────┐ │SOF│ 仲裁场 │ IDE │FDF │ res │BRSESI │ DLC │数据 0~64BCRC │ ... │ │ 1 │ ID 11/29 │ │ =1 │ │ =1 │ 0=健康 │ 4 位 │高速传输 17/21│ACK.. │ └───┴───────────┴─────┴─────┴─────┴──┬──┴──────────┴────────┴────────┴──┬──┴─────┘ ▲ ▲ 此处开始提速 CRC 界定符后切回 (BRS=1 → 数据段速率) (回到仲裁段速率) 仲裁段用低速:保证长总线、多节点仍能可靠仲裁 数据段用高速:仲裁已结束、只剩一个发送者,可放心提速 → 吞吐大增
为什么不能整帧都用高速?

仲裁阶段可能有多个节点同时发送并竞争总线,靠“显性覆盖隐性”实现无损仲裁——这要求信号能在一位时间内传遍整条总线并被所有节点采样。总线越长、节点越多,能支持的速率越低。而数据段只有仲裁获胜的那一个节点在发,不再需要全总线同步比较,因此可以安全提速。这就是 CAN FD “仲裁慢、数据快”双速率设计的物理根源。

DLC 到字节数的映射表

DLC0~89101112131415
字节数0~812162024324864
HAL 用宏而非裸 DLC

写 FDCAN 代码时不要手填 DLC 数字,用 HAL 提供的宏更安全:FDCAN_DLC_BYTES_8FDCAN_DLC_BYTES_12 …… FDCAN_DLC_BYTES_64。它们已按上表编码好,避免记错非线性映射。数据缓冲区也要开到对应大小(如发 64 字节要 uint8_t data[64])。

STM32 FDCAN 初始化

以 STM32G4 为例(PLLQ 时钟 → FDCAN 内核时钟)。仲裁段设 500k、数据段设 2M,需分别配置两套时序。注意 FDCAN 需要先分配 Message RAM。

/* fdcan.c — STM32G4 FDCAN 初始化,仲裁 500k / 数据 2M */
#include "stm32g4xx_hal.h"

FDCAN_HandleTypeDef hfdcan1;

void MX_FDCAN1_Init(void) {
  hfdcan1.Instance = FDCAN1;
  hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1;
  hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS;  /* FD + 位速率切换 */
  hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;

  /* ── 仲裁段位时序(假设内核时钟 20MHz)── */
  hfdcan1.Init.NominalPrescaler   = 2;   /* 20M/2 = 10M Tq 时钟 */
  hfdcan1.Init.NominalSyncJumpWidth = 4;
  hfdcan1.Init.NominalTimeSeg1    = 15;  /* 1+15+4 = 20 Tq → 10M/20=500k */
  hfdcan1.Init.NominalTimeSeg2    = 4;

  /* ── 数据段位时序(更快)── */
  hfdcan1.Init.DataPrescaler    = 1;    /* 20M Tq 时钟 */
  hfdcan1.Init.DataSyncJumpWidth = 2;
  hfdcan1.Init.DataTimeSeg1     = 7;    /* 1+7+2 = 10 Tq → 20M/10=2M */
  hfdcan1.Init.DataTimeSeg2     = 2;

  hfdcan1.Init.AutoRetransmission = ENABLE;
  hfdcan1.Init.TransmitPause      = DISABLE;
  hfdcan1.Init.ProtocolException  = DISABLE;

  /* ── Message RAM 分配 ── */
  hfdcan1.Init.StdFiltersNbr = 1;
  hfdcan1.Init.ExtFiltersNbr = 0;
  hfdcan1.Init.RxFifo0ElmtsNbr = 8;
  hfdcan1.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_64;
  hfdcan1.Init.TxFifoQueueElmtsNbr = 8;
  hfdcan1.Init.TxElmtSize = FDCAN_DATA_BYTES_64;
  hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;

  if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK) Error_Handler();
}

void HAL_FDCAN_MspInit(FDCAN_HandleTypeDef *h) {
  GPIO_InitTypeDef g = {0};
  if (h->Instance == FDCAN1) {
    __HAL_RCC_FDCAN_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /* PB8=FDCAN1_RX, PB9=FDCAN1_TX,G4 上复用为 AF9 */
    g.Pin = GPIO_PIN_8 | GPIO_PIN_9;
    g.Mode = GPIO_MODE_AF_PP;
    g.Pull = GPIO_NOPULL;
    g.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    g.Alternate = GPIO_AF9_FDCAN1;
    HAL_GPIO_Init(GPIOB, &g);
  }
}

FDCAN 发送与接收

FDCAN 用 FDCAN_TxHeaderTypeDef 描述发送帧,注意要把 FDFormat 设为 FD、BitRateSwitch 设为 ON 才启用双速率:

/* 发送一帧 64 字节的 CAN FD 报文(BRS 开)*/
void FDCAN_Send64(uint16_t id, uint8_t *data) {
  FDCAN_TxHeaderTypeDef tx = {0};
  tx.Identifier          = id;
  tx.IdType              = FDCAN_STANDARD_ID;
  tx.TxFrameType         = FDCAN_DATA_FRAME;
  tx.DataLength          = FDCAN_DLC_BYTES_64;   /* 64 字节 */
  tx.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
  tx.BitRateSwitch       = FDCAN_BRS_ON;        /* 数据段提速 */
  tx.FDFormat            = FDCAN_FD_CAN;         /* FD 帧 */
  tx.TxEventFifoControl  = FDCAN_NO_TX_EVENTS;
  tx.MessageMarker       = 0;

  HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &tx, data);
}

/* 启动 + 使能接收中断 */
void FDCAN_StartRx(void) {
  HAL_FDCAN_Start(&hfdcan1);
  HAL_FDCAN_ActivateNotification(&hfdcan1,
      FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);
}

/* 接收回调:G4 FDCAN 的回调带 RxFifo0ITs 参数 */
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *h,
                               uint32_t RxFifo0ITs) {
  if (RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) {
    FDCAN_RxHeaderTypeDef rx;
    uint8_t buf[64];
    if (HAL_FDCAN_GetRxMessage(h, FDCAN_RX_FIFO0, &rx, buf) == HAL_OK) {
      /* rx.Identifier = ID, rx.DataLength = DLC 宏 */
      /* 处理 buf(最多 64 字节)*/
    }
  }
}

经典 CAN 与 FD 的兼容性

可以兼容的情况

  • FD 控制器可发/收经典帧(FDF=0)
  • 同一总线可混跑经典帧与 FD 帧
  • 物理层(收发器)多数向下兼容
  • 仲裁段速率一致即可共存

不兼容的情况

  • 纯经典控制器收不了 FD 帧
  • 经典节点见到 FDF=1 会当作错误帧
  • 老收发器可能不支持数据段高速
  • 混网时须让 FD 节点只发经典帧
混合网络的陷阱

如果总线上同时挂着经典 CAN 节点和 CAN FD 节点,而 FD 节点发出了真正的 FD 帧(FDF=1),经典节点无法识别,会把它当成格式错误并发送错误帧,破坏整条总线的通信。解决方案:要么全网升级为支持 FD 的控制器,要么让 FD 节点配置为只发经典帧(FrameFormat = FDCAN_FRAME_CLASSIC)。规划网络时必须提前确认所有节点的 FD 支持能力。

收发器也要支持高速

CAN FD 数据段跑到 2~8 Mbps 时,普通经典 CAN 收发器(如老款 TJA1050)的信号边沿速率可能跟不上,导致高速段位错误。使用 CAN FD 必须选用标注支持 FD 的收发器,如 TJA1044TJA1051(部分型号)、MCP2562FD 等。控制器、收发器、总线拓扑三者的速率能力必须匹配,任何一环拖后腿都会在高速段出错。

本章小结

CAN FD 在保持经典 CAN 仲裁机制的前提下带来两大提升:数据段最大 64 字节(DLC 9~15 非线性映射到 12~64),以及数据段速率可切到 2~8 Mbps。关键新增位:FDF(标识 FD 帧)、BRS(位速率切换,=1 时数据段提速)、ESI(发送方错误状态)。检错用更强的 CRC17(≤16B)/ CRC21(>16B)。“仲裁慢、数据快”的物理根源是仲裁阶段需全总线同步比较、数据阶段只有单节点发送。STM32F4 的 bxCAN 不支持 FD,需用 G4/H7 的 FDCAN 外设(基于 M_CAN IP,配 Message RAM,分设 Nominal/Data 两套位时序)。发送用 FDCAN_TxHeaderTypeDef 并设 FDFormat=FD_CANBitRateSwitch=BRS_ON;接收回调 HAL_FDCAN_RxFifo0CallbackRxFifo0ITs 参数。兼容性:FD 控制器能收发经典帧,但经典节点收不了 FD 帧,混网时须让 FD 节点降为经典模式,且收发器也必须支持 FD 高速。

⚒️ 配套开发者工具箱 · JSON / 正则 / 时间戳 / Cron 等 17 款免费在线工具