STM32H7 550MHz 下 FDCAN 与经典 CAN 的时序兼容性陷阱及规避

引言

STM32H7 系列凭借 Cortex-M7 内核,最高运行频率可达 550MHz,其内置的 FDCAN 模块(基于 Bosch M_CAN)支持 CAN 2.0 和 CAN FD 协议。然而,当开发者从经典 CAN(如 bxCAN)迁移到 FDCAN 时,常因时序配置差异导致通信异常。本文聚焦于 550MHz 主频下的位时序计算陷阱,并给出可落地的规避方案。

FDCAN 与经典 CAN 的时序架构差异

经典 CAN 的位时序模型

经典 CAN(如 STM32F1/F4 的 bxCAN)使用简单的位时间分段:

  • 同步段(SYNC_SEG):固定 1 个时间量子(tq)
  • 传播段(PROP_SEG)
  • 相位缓冲段 1(PS1)
  • 相位缓冲段 2(PS2)

位时间 = (1 + PROP + PS1 + PS2) × tq,其中 tq = 1 / (CAN_CLK / Prescaler)。

FDCAN 的位时序扩展

FDCAN 引入了更灵活的位时序,支持数据段(Data Phase)和仲裁段(Arbitration Phase)独立配置,且时间量子长度可动态调整。关键寄存器为 DBTP(Data Bit Timing & Prescaler)和 NBTP(Nominal Bit Timing & Prescaler)。

陷阱 1:默认配置下,FDCAN 的同步段宽度可配置(1-4 tq),而经典 CAN 固定为 1 tq。 若沿用经典 CAN 的配置习惯,将 SYNC 段设为 1,但未调整其他段,可能导致采样点偏移。

陷阱 2:550MHz 主频下,AHB/APB 总线时钟分频不当,导致 FDCAN 的时钟源(FDCAN_CLK)频率过高,超出协议规定的最大位速率(如 1Mbps 仲裁段)。 例如,若 FDCAN_CLK = 275MHz(AHB 分频 2),要得到 1Mbps,预分频器需设为 275,但 FDCAN 预分频器通常只有 8 位(最大 256),直接溢出!

配置步骤:基于 CubeMX 的规避流程

1. 时钟树规划

在 CubeMX 中,确保 FDCAN 时钟源选择 PLL1Q 或 PLL2Q,并计算其频率。推荐将 FDCAN_CLK 设为 20MHz 或 40MHz,便于整数分频。

// 示例:配置 PLL1Q 为 40MHz
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_OscInitStruct.PLL.PLLM = 5;   // HSI=16MHz, /5=3.2MHz
RCC_OscInitStruct.PLL.PLLN = 50;  // x50=160MHz VCO
RCC_OscInitStruct.PLL.PLLP = 2;   // SYSCLK=80MHz (但实际H7可达550MHz,此处为简化)
RCC_OscInitStruct.PLL.PLLQ = 4;   // FDCAN_CLK=160/4=40MHz

2. 位时序计算

假设仲裁段 1Mbps,数据段 5Mbps,FDCAN_CLK=40MHz。

  • 仲裁段:tq = 40MHz / 1Mbps = 40ns → 位时间 = 40 tq。推荐采样点 80%:SYNC=1, PROP=3, PS1=28, PS2=8(总 40)。
  • 数据段:tq = 40MHz / 5Mbps = 8ns → 位时间 = 8 tq。采样点 75%:SYNC=1, PROP=1, PS1=4, PS2=2(总 8)。

注意:FDCAN 的 NBTP 寄存器中,SYNC 段宽度字段为 2 位(0-3),但实际有效值为 1-4 tq,且必须满足 SYNC + PROP + PS1 + PS2 ≥ 8 tq。

3. 代码实现

#include "stm32h7xx_hal.h"

FDCAN_HandleTypeDef hfdcan1;

void FDCAN_Init(void) {
    hfdcan1.Instance = FDCAN1;
    hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1;  // 时钟不分频
    hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS; // 支持 BRS
    hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
    hfdcan1.Init.AutoRetransmission = ENABLE;
    hfdcan1.Init.TransmitPause = DISABLE;
    hfdcan1.Init.ProtocolException = DISABLE;

    // 仲裁段位时序:1Mbps, 采样点80%
    hfdcan1.Init.NominalPrescaler = 1;  // tq = 40MHz/1 = 40ns
    hfdcan1.Init.NominalSyncJumpWidth = 1;
    hfdcan1.Init.NominalTimeSeg1 = 31;  // PROP+PS1 = 3+28 = 31
    hfdcan1.Init.NominalTimeSeg2 = 8;   // PS2 = 8

    // 数据段位时序:5Mbps, 采样点75%
    hfdcan1.Init.DataPrescaler = 1;     // tq = 40MHz/1 = 40ns (但数据段时钟可独立分频)
    hfdcan1.Init.DataSyncJumpWidth = 1;
    hfdcan1.Init.DataTimeSeg1 = 5;      // PROP+PS1 = 1+4 = 5
    hfdcan1.Init.DataTimeSeg2 = 2;      // PS2 = 2

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

// 注意:数据段位时序实际计算时,tq 由 DataPrescaler 决定,但 DataPrescaler 是相对 FDCAN_CLK 的。
// 若 FDCAN_CLK=40MHz,DataPrescaler=1,则数据段 tq=40ns,要得到 5Mbps 需要位时间=8tq,但上面配置总 tq=1+5+2=8,正确。

关键陷阱详解

陷阱 1:预分频器溢出

经典 CAN 的预分频器为 16 位,而 FDCAN 的 NominalPrescaler 和 DataPrescaler 均为 8 位(最大值 256)。在 550MHz 主频下,若直接使用 AHB 时钟(如 275MHz)作为 FDCAN 时钟,要得到 1Mbps,预分频需 275,超出范围。规避:务必通过 PLL 分频降低 FDCAN_CLK 至合理范围(如 20-40MHz)。

陷阱 2:采样点漂移

经典 CAN 的采样点通常配置在 70-80%,但 FDCAN 的默认值可能不同。若沿用经典 CAN 的寄存器值(如 BS1=8, BS2=7),在 FDCAN 中会因 SYNC 段可调导致采样点偏移。规避:使用公式计算并验证采样点:

采样点 = (SYNC + PROP + PS1) / (SYNC + PROP + PS1 + PS2) × 100%

陷阱 3:数据段与仲裁段切换时的同步问题

在 CAN FD 中,BRS(Bit Rate Switch)切换时,若数据段位时序配置不当,会导致相位误差累积。规避:确保 DataSyncJumpWidth 不小于 1,且数据段采样点尽量接近 75%。

完整示例:混合网络通信

以下代码演示如何发送经典 CAN 帧(2.0)和 CAN FD 帧,并处理时序兼容性。

void Send_CAN_Frame(FDCAN_HandleTypeDef *hfdcan, uint32_t id, uint8_t *data, uint8_t len, uint8_t is_fd) {
    FDCAN_TxHeaderTypeDef txHeader = {0};
    txHeader.Identifier = id;
    txHeader.IdType = FDCAN_STANDARD_ID;
    txHeader.TxFrameType = FDCAN_DATA_FRAME;
    txHeader.DataLength = is_fd ? FDCAN_DLC_BYTES_8 : FDCAN_DLC_BYTES_8; // 简化
    txHeader.FDFormat = is_fd ? FDCAN_FD_CAN : FDCAN_CLASSIC_CAN;
    txHeader.BitRateSwitch = is_fd ? FDCAN_BRS_ON : FDCAN_BRS_OFF;
    txHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
    txHeader.BitRateSwitch = FDCAN_BRS_OFF; // 经典 CAN 无 BRS

    if (HAL_FDCAN_AddMessageToTxFifoQ(hfdcan, &txHeader, data) != HAL_OK) {
        // 错误处理
    }
}

// 主循环中发送
uint8_t data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
Send_CAN_Frame(&hfdcan1, 0x123, data, 8, 0); // 经典 CAN
Send_CAN_Frame(&hfdcan1, 0x456, data, 8, 1); // CAN FD

注意事项

  • 时钟源选择:在 CubeMX 中,务必确认 FDCAN 时钟源不是直接来自 AHB,而是来自 PLL 输出。可通过 HAL_RCC_GetFDCANClockFreq() 验证。
  • 位时序验证:使用示波器或 CAN 分析仪实际测量波形,确保采样点位置正确。
  • 兼容性测试:在混合网络中,建议先用经典 CAN 帧测试,再启用 FD 帧,逐步排查。
  • 中断优先级:FDCAN 中断在 550MHz 下响应快,但需注意与实时任务的优先级冲突。

结语

STM32H7 的 FDCAN 在 550MHz 下性能强大,但时序配置需谨慎。通过合理规划时钟、精确计算位时序,并理解与经典 CAN 的差异,可有效规避兼容性陷阱。希望本文的实战经验能助你在嵌入式开发中少走弯路。