CAN FD
经典 CAN 的 8 字节、1Mbps 天花板如何被打破:CAN FD 的可变数据段速率与 64 字节负载,以及 STM32 G4/H7 上的 FDCAN 外设编程。
经典 CAN 的 8 字节、1Mbps 天花板如何被打破:CAN FD 的可变数据段速率与 64 字节负载,以及 STM32 G4/H7 上的 FDCAN 外设编程。
| 特性 | 经典 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) |
下图标注了 BRS 位前后的速率切换点——仲裁段慢、数据段快,CRC 界定符后切回:
仲裁阶段可能有多个节点同时发送并竞争总线,靠“显性覆盖隐性”实现无损仲裁——这要求信号能在一位时间内传遍整条总线并被所有节点采样。总线越长、节点越多,能支持的速率越低。而数据段只有仲裁获胜的那一个节点在发,不再需要全总线同步比较,因此可以安全提速。这就是 CAN FD “仲裁慢、数据快”双速率设计的物理根源。
| DLC | 0~8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|
| 字节数 | 0~8 | 12 | 16 | 20 | 24 | 32 | 48 | 64 |
写 FDCAN 代码时不要手填 DLC 数字,用 HAL 提供的宏更安全:FDCAN_DLC_BYTES_8、FDCAN_DLC_BYTES_12 …… FDCAN_DLC_BYTES_64。它们已按上表编码好,避免记错非线性映射。数据缓冲区也要开到对应大小(如发 64 字节要 uint8_t data[64])。
以 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_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 节点和 CAN FD 节点,而 FD 节点发出了真正的 FD 帧(FDF=1),经典节点无法识别,会把它当成格式错误并发送错误帧,破坏整条总线的通信。解决方案:要么全网升级为支持 FD 的控制器,要么让 FD 节点配置为只发经典帧(FrameFormat = FDCAN_FRAME_CLASSIC)。规划网络时必须提前确认所有节点的 FD 支持能力。
CAN FD 数据段跑到 2~8 Mbps 时,普通经典 CAN 收发器(如老款 TJA1050)的信号边沿速率可能跟不上,导致高速段位错误。使用 CAN FD 必须选用标注支持 FD 的收发器,如 TJA1044、TJA1051(部分型号)、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_CAN、BitRateSwitch=BRS_ON;接收回调 HAL_FDCAN_RxFifo0Callback 带 RxFifo0ITs 参数。兼容性:FD 控制器能收发经典帧,但经典节点收不了 FD 帧,混网时须让 FD 节点降为经典模式,且收发器也必须支持 FD 高速。