# 引言 STM32H7 系列凭借 Cortex-M7 内核和 480MHz 主频,成为高性能嵌入式应用的首选。其内置的 FDCAN 模块支持 CAN 2.0 和 CAN FD 协议,但在实际项目中,不少开发者发现:当系统时钟配置为 480MHz 时,FDCAN 与经典 CAN(如 STM32F1 的 bxCAN)节点通信时,出现间歇性错误帧、总线关闭甚至完全无法同步。问题根源并非协议不兼容,而是 FDCAN 位时间配置在高速内核时钟下的时序精度陷阱。本文将逐一拆解这些陷阱,并给出可落地的解决方案。 # 一、FDCAN 位时间基础与主频耦合 FDCAN 的位时间由四个段组成:同步段(SYNC_SEG)、传播段(PROP_SEG)、相位缓冲段 1(PHASE_SEG1)和相位缓冲段 2(PHASE_SEG2)。每个段的时长以时间量子(tq)为单位,而 tq 由 CAN 时钟(CAN_CLK)分频得到。 在 STM32H7 中,CAN_CLK 通常来自 FDCAN 内核时钟,该时钟可独立配置,但默认情况下与系统时钟(SYSCLK)存在分频关系。当 SYSCLK=480MHz 时,若直接使用默认的 /2 分频,CAN_CLK=240MHz,此时一个 tq 仅约 4.17ns。对于 500kbps 的经典 CAN 波特率,位时间为 2μs,即需要 480 个 tq。这远超 FDCAN 位时间寄存器(最多 256 tq)的限制,导致配置失败。 **陷阱 1:tq 数量溢出**。经典 CAN 的位时间通常为 8~25 tq,但高速时钟下,若分频系数过小,位时间 tq 数会超出寄存器范围。 **陷阱 2:采样点精度下降**。即使 tq 数在范围内,由于 tq 粒度变小,采样点位置(通常要求 75%~80%)的量化误差增大,导致与经典 CAN 节点的相位裕量不足。 # 二、配置步骤与陷阱规避 ## 2.1 合理选择 CAN_CLK 分频 首先,明确目标波特率(如 500kbps)和采样点(如 80%)。计算所需位时间 tq 数: - 位时间 = 1 / 波特率 = 2μs - 若 CAN_CLK = 40MHz(即 480MHz / 12),则 tq = 25ns,位时间 tq 数 = 80。 **规避策略**:将 FDCAN 内核时钟分频系数设为 12,使 CAN_CLK 降至 40MHz,确保 tq 数在 8~25 范围内(此处 80 仍偏大,可进一步分频至 20MHz,tq=50ns,位时间 tq=40,仍超限)。实际推荐 CAN_CLK 在 20~40MHz 之间,并调整位时间 tq 数至 16~25。例如,CAN_CLK=20MHz,tq=50ns,位时间=40 tq,仍超限。因此,对于 500kbps,最佳 CAN_CLK 为 10MHz(tq=100ns,位时间=20 tq)。 **结论**:优先将 CAN_CLK 配置为 10MHz(480MHz / 48),这样位时间 tq=20,符合经典 CAN 的典型配置。 ## 2.2 位时间寄存器配置 以 CAN_CLK=10MHz、波特率 500kbps、采样点 80% 为例: - 位时间 = 20 tq - 同步段固定 1 tq - 传播段 + 相位缓冲段1 = 15 tq(采样点 = (1+15)/20 = 80%) - 相位缓冲段2 = 4 tq(20-1-15) 配置 FDCAN 的 NBTP 寄存器(Nominal Bit Timing & Prescaler): - NSJW = 4(同步跳转宽度,取 min(4, PHASE_SEG1)) - NTSEG1 = 14(传播段+相位段1-1,因为寄存器值从0开始) - NTSEG2 = 3(相位段2-1) - NBRP = 0(预分频器,CAN_CLK 已分频) ## 2.3 代码示例(基于 STM32H743 HAL 库) ```c #include "stm32h7xx_hal.h" FDCAN_HandleTypeDef hfdcan1; void FDCAN_Init(void) { hfdcan1.Instance = FDCAN1; hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV48; // 480MHz / 48 = 10MHz hfdcan1.Init.FrameFormat = FDCAN_FRAME_CLASSIC; // 经典 CAN 模式 hfdcan1.Init.Mode = FDCAN_MODE_NORMAL; hfdcan1.Init.AutoRetransmission = ENABLE; hfdcan1.Init.TransmitPause = DISABLE; hfdcan1.Init.ProtocolException = DISABLE; // 标称位时间配置:20 tq,采样点 80% hfdcan1.Init.NominalPrescaler = 1; // 实际分频已在 ClockDivider 完成 hfdcan1.Init.NominalSyncJumpWidth = 4; hfdcan1.Init.NominalTimeSeg1 = 14; // 传播段+相位段1 = 15 tq hfdcan1.Init.NominalTimeSeg2 = 3; // 相位段2 = 4 tq if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK) { Error_Handler(); } // 配置滤波器(可选) FDCAN_FilterTypeDef sFilterConfig; sFilterConfig.IdType = FDCAN_STANDARD_ID; sFilterConfig.FilterIndex = 0; sFilterConfig.FilterType = FDCAN_FILTER_MASK; sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; sFilterConfig.FilterID1 = 0x123; sFilterConfig.FilterID2 = 0x7FF; HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig); // 启动 FDCAN HAL_FDCAN_Start(&hfdcan1); HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0); } // 发送经典 CAN 帧 void FDCAN_SendClassic(uint32_t id, uint8_t *data, uint8_t len) { FDCAN_TxHeaderTypeDef txHeader; txHeader.Identifier = id; txHeader.IdType = FDCAN_STANDARD_ID; txHeader.TxFrameType = FDCAN_DATA_FRAME; txHeader.DataLength = FDCAN_DLC_BYTES_8; txHeader.FDFormat = FDCAN_CLASSIC_CAN; // 经典 CAN 帧格式 txHeader.BitRateSwitch = DISABLE; txHeader.TxEventFifoControl = DISABLE; HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, data); } ``` # 三、其他陷阱与注意事项 ## 3.1 数据阶段位时间(CAN FD) 若启用 CAN FD,数据阶段波特率通常更高(如 2Mbps),此时 CAN_CLK 需重新计算。例如,数据阶段位时间 tq 数可能为 10,采样点要求 70%。务必分别配置数据位时间寄存器(DBTP),不可沿用标称位时间。 ## 3.2 时钟树配置顺序 在 CubeMX 中,应先配置 RCC 时钟树,确保 FDCAN 内核时钟源正确。若使用 PLL2 作为 FDCAN 时钟,需注意 PLL2 的 VCO 频率范围(192~836MHz),并合理设置分频系数。 ## 3.3 硬件同步与重同步 经典 CAN 节点对同步段要求严格。FDCAN 的重同步跳转宽度(SJW)应设为不小于 1 tq,且建议与相位缓冲段1 相等,以容忍时钟漂移。在 480MHz 主频下,内核时钟抖动可能影响 CAN 时钟,因此 SJW 不宜过小。 ## 3.4 实测验证 配置完成后,使用示波器或 CAN 分析仪测量 TX 引脚波形,确认位时间宽度和采样点位置。推荐使用 CAN 一致性测试工具(如 CANoe)进行干扰测试。 # 四、总结 STM32H7 在 480MHz 下使用 FDCAN 与经典 CAN 通信时,核心陷阱在于 CAN_CLK 分频不当导致 tq 数溢出或采样点精度下降。通过将 CAN_CLK 降至 10MHz 左右,并严格按位时间公式配置 NBTP 寄存器,可确保与经典 CAN 节点的时序兼容。实际项目中,还需关注 CAN FD 数据阶段配置和时钟树细节。掌握这些要点,即可在高主频下稳定运行 CAN 通信。