STM32H7 480MHz 下 FDCAN 与普通 CAN 的时序兼容性陷阱与配置对策

引言

STM32H7 系列凭借 Cortex-M7 内核,主频可达 480MHz,其内置的 FDCAN 控制器(基于 Bosch M_CAN)支持 CAN FD 协议,同时兼容经典 CAN 2.0B。然而,当 FDCAN 配置在 480MHz 系统时钟下时,由于外设时钟分频、位时间量化(TQ)计算以及采样点设置不当,常常导致与普通 CAN 节点通信时出现错误帧、总线关闭甚至完全无法通信。本文将从底层原理出发,揭示这些时序陷阱,并提供经过验证的配置对策。

1. FDCAN 与经典 CAN 的时序基础

1.1 位时间结构

CAN 协议的每一位由四个段组成:同步段(SYNC_SEG)、传播段(PROP_SEG)、相位缓冲段 1(PHASE_SEG1)和相位缓冲段 2(PHASE_SEG2)。每个段由整数个时间量子(TQ)组成,TQ 是 CAN 控制器的最小时间单位。

  • 同步段:固定为 1 TQ,用于同步总线上的各个节点。
  • 传播段:补偿物理延迟(总线长度、收发器延迟等)。
  • 相位缓冲段 1/2:用于补偿相位误差,配合重同步机制。

采样点位于 PHASE_SEG1 和 PHASE_SEG2 之间,其位置(采样点百分比)对通信可靠性至关重要。

1.2 FDCAN 的速率灵活性

FDCAN 支持两种速率:仲裁段速率(用于发送帧起始、仲裁场、控制场等)和数据段速率(用于数据场)。经典 CAN 只使用一种速率。因此,FDCAN 在接收经典 CAN 帧时,必须将数据段速率配置为与仲裁段速率相同,否则无法正确解析。

2. 480MHz 主频下的时序陷阱

2.1 外设时钟分频陷阱

STM32H7 的 FDCAN 外设时钟(FDCAN_KER_CK)来自 PLL1Q 或 PLL2Q,通常设置为 20MHz 或 40MHz。当系统主频为 480MHz 时,若直接使用系统时钟分频,可能导致 FDCAN 时钟频率过高,超出其最大工作频率(通常为 100MHz)。例如,若将 FDCAN_KER_CK 设为 80MHz,则 TQ 时间 = 12.5ns,对于 1Mbps 的经典 CAN,每位需要 80 TQ,这超出了 FDCAN 位时间寄存器(最多 32 TQ)的限制。

对策:必须将 FDCAN_KER_CK 配置在合理范围内(如 20MHz),以保证每个位时间内的 TQ 数量足够。在 STM32CubeMX 中,通过调整 PLL 分频系数,使 FDCAN 时钟为 20MHz 或 40MHz。

2.2 采样点偏移陷阱

经典 CAN 的标准采样点通常为 75%~80%(如 1Mbps 时采样点 75%)。但 FDCAN 在高速模式下,默认配置可能将采样点设置得较晚(如 85% 以上),这会导致在长总线或高负载下,采样点接近位结束,容易受到信号反射和振铃的影响,从而产生位错误。

对策:在配置 FDCAN 位时间时,必须根据总线长度和波特率计算采样点,并确保与总线上其他经典 CAN 节点一致。例如,对于 500kbps 经典 CAN,推荐采样点 87.5%(常见于汽车应用),但若与默认 75% 的节点混用,则需调整。

2.3 同步段宽度不足

FDCAN 的同步段固定为 1 TQ,但在 480MHz 主频下,若 TQ 时间过短(如 25ns),同步段仅 25ns,而总线收发器(如 TJA1050)的环路延迟可能超过 100ns。这导致同步段无法覆盖收发器延迟,造成同步失败。

对策:增加传播段(PROP_SEG)的长度,以补偿物理延迟。通常,传播段应至少为 2 倍的物理延迟时间。在配置时,通过增大 TQ 数量或降低 FDCAN 时钟频率来增加 TQ 时间。

3. 配置步骤与代码示例

3.1 使用 STM32CubeMX 配置 FDCAN

  1. 在 Clock Configuration 中,设置 PLL1Q 输出 20MHz 作为 FDCAN 内核时钟。
  2. 在 FDCAN1 配置中,启用 FDCAN,选择经典 CAN 模式(FD 关闭)。
  3. 设置仲裁段波特率为 500kbps,数据段波特率相同。
  4. 手动设置位时间参数:
    • 预分频器(Prescaler)= 1(因为 FDCAN 时钟 20MHz,500kbps 需要 40 TQ,但 FDCAN 最大 32 TQ,所以预分频器设为 2,得到 10MHz TQ 时钟,每位 20 TQ)。
    • 同步段 = 1 TQ
    • 传播段 = 2 TQ(补偿延迟)
    • 相位缓冲段1 = 9 TQ
    • 相位缓冲段2 = 8 TQ
    • 采样点 = (1+2+9)/20 = 60%?不对,需要重新计算。

实际上,对于 500kbps,FDCAN 时钟 20MHz,若预分频器=1,则 TQ=50ns,每位需要 40 TQ,超出范围。因此预分频器必须设为 2,TQ=100ns,每位 20 TQ。此时采样点可设为 75%,即同步段+传播段+相位缓冲段1 = 15 TQ,则相位缓冲段2 = 5 TQ。配置:同步段=1,传播段=2,相位缓冲段1=12,相位缓冲段2=5(总20 TQ,采样点=15/20=75%)。

3.2 HAL 库代码示例

#include "fdcan.h"

FDCAN_HandleTypeDef hfdcan1;

void FDCAN1_Init(void)
{
    hfdcan1.Instance = FDCAN1;
    hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1; // 时钟不分频,但内核时钟已配置为20MHz
    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;

    // 仲裁段位时间配置(500kbps,20MHz时钟,预分频2,20TQ/位)
    hfdcan1.Init.NominalPrescaler = 2; // TQ = 2/20MHz = 100ns
    hfdcan1.Init.NominalSyncJumpWidth = 1; // SJW = 1 TQ
    hfdcan1.Init.NominalTimeSeg1 = 14; // 传播段+相位缓冲段1 = 2+12 = 14 TQ
    hfdcan1.Init.NominalTimeSeg2 = 5;  // 相位缓冲段2 = 5 TQ

    // 数据段位时间(经典CAN中与仲裁段相同)
    hfdcan1.Init.DataPrescaler = 2;
    hfdcan1.Init.DataSyncJumpWidth = 1;
    hfdcan1.Init.DataTimeSeg1 = 14;
    hfdcan1.Init.DataTimeSeg2 = 5;

    hfdcan1.Init.StdFiltersNbr = 1;
    hfdcan1.Init.ExtFiltersNbr = 1;
    hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;

    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 = 0x000;
    sFilterConfig.FilterID2 = 0x000; // 掩码全0,接收所有
    HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig);

    // 启动FDCAN
    HAL_FDCAN_Start(&hfdcan1);
    HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);
}

// 发送经典CAN帧
void CAN_SendFrame(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 = len << 16; // 数据长度编码,经典CAN支持0-8字节
    txHeader.FDFormat = FDCAN_CLASSIC_CAN; // 经典CAN帧
    txHeader.BitRateSwitch = FDCAN_BRS_OFF;
    txHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;

    if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, data) != HAL_OK)
    {
        Error_Handler();
    }
}

4. 注意事项

  • 时钟源选择:确保 FDCAN 内核时钟来自 PLL1Q 或 PLL2Q,不要直接使用系统时钟(480MHz),否则超出 FDCAN 最大频率。
  • 位时间计算:使用公式:波特率 = FDCAN_KER_CK / (Prescaler * (1 + TimeSeg1 + TimeSeg2))。务必验证 TQ 总数不超过 32。
  • 采样点一致性:与总线上其他经典 CAN 节点通信时,采样点差异应小于 5%,否则建议调整。可使用 CAN 分析仪测量实际采样点。
  • 同步跳转宽度(SJW):SJW 应至少为 1 TQ,但不要大于 PHASE_SEG1,否则可能导致过冲。
  • 总线长度:若总线长度超过 10m,应增加传播段(TimeSeg1 中的传播部分),以补偿延迟。
  • 调试技巧:使用逻辑分析仪抓取波形,检查位时间是否正常。若出现错误帧,可尝试增大 SJW 或调整采样点。

5. 总结

STM32H7 在 480MHz 下配置 FDCAN 与经典 CAN 通信,关键在于合理选择外设时钟频率,并精确计算位时间参数。通过上述配置对策,可避免采样点偏移、同步失败等陷阱,实现稳定可靠的通信。实际项目中,建议根据具体总线拓扑和节点特性,使用 CAN 一致性测试工具验证时序。