引言

STM32H7 系列凭借 400MHz 主频和双 FDCAN 控制器,成为车载和工业控制的首选。然而,在高速主频下,FDCAN 多节点总线仲裁失效问题频发,轻则数据错乱,重则总线关闭。本文结合实战案例,剖析失效机理,并提供可复现的解决步骤。

1. 问题现象与初步定位

1.1 典型故障表现

  • 节点 A(优先级高)发送周期帧,节点 B(优先级低)持续发送,导致 A 的帧延迟或丢失。
  • 总线错误计数器(TEC/REC)快速增加,最终进入 Bus-Off 状态。
  • 逻辑分析仪抓取波形,发现显性位被隐性位覆盖,出现位错误。

1.2 初步排查方向

  • 检查波特率配置是否一致(如 1Mbps)。
  • 检查终端电阻(120Ω)是否匹配。
  • 检查 CAN 收发器型号与供电。

但上述常规检查均正常,问题依旧,需深入协议层。

2. 根因分析:400MHz 下的时序陷阱

2.1 FDCAN 位时序与主频关系

FDCAN 的位时间由 SYNC_SEG、PROP_SEG、PHASE_SEG1、PHASE_SEG2 组成,每个段由若干个时间量子(tq)构成。tq 由 CAN 时钟(通常是 APB1 时钟)分频得到。STM32H7 的 APB1 最高可达 100MHz(当主频 400MHz 时,APB1 分频系数为 4)。

若配置不当,例如将 CAN 时钟设为 100MHz,而位时间设为 10 tq,则位时间 = 10 × (1/100MHz) = 100ns,对应 1Mbps。但此时采样点位置(通常设为 75%)对时钟漂移和总线延迟非常敏感。

2.2 仲裁失效的协议层原因

CAN 总线仲裁基于“显性位覆盖隐性位”的机制。当两个节点同时发送帧时,每个节点逐位比较发送位与总线电平。若发送隐性位但读到显性位,则失去仲裁,转为接收。

在 400MHz 主频下,若 FDCAN 外设时钟与 CPU 时钟不同步(例如使用 PLL1Q 作为 CAN 时钟,而 PLL1Q 存在抖动),或者位时序中的同步跳转宽度(SJW)设置过小,会导致采样点偏移,使得节点在仲裁场(ID 位)采样错误,误判总线电平,从而出现低优先级帧“赢得”仲裁。

2.3 硬件层面的隐患

  • PCB 走线过长导致信号反射,在高速位率下加剧位错误。
  • 收发器(如 TJA1044)的环路延迟与 STM32H7 的输入比较器延迟叠加,超出 SJW 补偿范围。

3. 解决方案:从配置到代码

3.1 重新计算位时序(CubeMX 配置)

以 1Mbps 为例,推荐使用 CAN 时钟 = 20MHz(由 PLL1Q 分频得到),位时间 = 20 tq,采样点 = 75%。这样每个 tq 为 50ns,SJW 可设为 4 tq,留有足够余量。

在 CubeMX 中:

  • 设置 FDCAN 时钟源为 PLL1Q,并确保 PLL1Q 输出 20MHz(需调整 PLL 配置)。
  • 位时序参数:
    • Prescaler = 1(因为 CAN 时钟已为 20MHz)
    • Sync Jump Width = 4 tq
    • Time Seg1 = 13 tq(传播段+相位段1)
    • Time Seg2 = 6 tq(相位段2)
    • 采样点 = (1+13)/(1+13+6) = 70%,接近 75%,可微调。

3.2 代码实现(HAL 库)

// 初始化 FDCAN 句柄
FDCAN_HandleTypeDef hfdcan1;

void FDCAN1_Init(void)
{
    hfdcan1.Instance = FDCAN1;
    hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1;  // 时钟不分频
    hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS; // 或经典 CAN
    hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
    hfdcan1.Init.AutoRetransmission = ENABLE;
    hfdcan1.Init.TransmitPause = DISABLE;
    hfdcan1.Init.ProtocolException = DISABLE;

    // 关键:位时序参数
    hfdcan1.Init.NominalPrescaler = 1;   // 预分频
    hfdcan1.Init.NominalSyncJumpWidth = 4;
    hfdcan1.Init.NominalTimeSeg1 = 13;
    hfdcan1.Init.NominalTimeSeg2 = 6;
    hfdcan1.Init.NominalBitRate = 1000000; // 1Mbps

    // 数据段时序(若使用 FD 格式)
    hfdcan1.Init.DataPrescaler = 1;
    hfdcan1.Init.DataSyncJumpWidth = 4;
    hfdcan1.Init.DataTimeSeg1 = 13;
    hfdcan1.Init.DataTimeSeg2 = 6;
    hfdcan1.Init.DataBitRate = 2000000; // 2Mbps 数据段

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

// 发送消息(带优先级)
void FDCAN1_SendMsg(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; // 长度编码
    txHeader.BitRateSwitch = DISABLE; // 若 FD 则 ENABLE
    txHeader.FDFormat = FDCAN_CLASSIC_CAN;
    txHeader.TxEventFifoControl = DISABLE;

    // 发送并等待完成
    if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, data) != HAL_OK)
    {
        // 处理发送失败
    }
}

3.3 增强鲁棒性的额外措施

  • 启用 FDCAN 自动重传(AutoRetransmission),避免因瞬时错误丢失帧。
  • 使用硬件过滤器,减少无关中断。
  • 在 PCB 布局中,CAN 差分线对尽量短且等长,并加共模电感。

4. 验证与测试

  • 使用 CAN 分析仪(如 PCAN)监控总线,观察 ID 优先级是否严格遵循。
  • 压力测试:三个节点同时以不同周期发送,持续 24 小时,检查错误帧计数。
  • 用示波器测量 CAN_H/CAN_L 差分波形,确认位时间稳定,无毛刺。

5. 注意事项

  • 不要直接使用默认的 40MHz CAN 时钟,除非位时间足够长(如 500kbps 以下)。
  • 修改 PLL 配置后,务必重新计算所有外设时钟,避免影响其他功能。
  • 若使用 FDCAN 的 FD 模式,数据段位时序需单独配置,且收发器必须支持 FD。
  • 在中断服务函数中避免长时间阻塞,防止 FDCAN 缓冲区溢出。

结语

STM32H7 在 400MHz 下的 FDCAN 仲裁失效,本质是位时序配置与高速主频不匹配导致的采样点漂移。通过精确计算位时序、合理设置 SJW,并配合硬件优化,可彻底解决。希望本文能帮助开发者少走弯路,构建稳定可靠的 CAN 网络。