# 引言 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 库) ```c // 初始化 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 网络。