# 引言 STM32H7 系列以 400MHz 主频和双 FDCAN 控制器著称,但在高速运行下,FDCAN 的发送超时(TX Timeout)和仲裁丢失(Arbitration Lost)问题频发。这些问题往往源于时钟配置错误、位时序参数不匹配、滤波器误过滤或中断优先级不当。本文基于实际调试经验,提供一套完整的排查方法论。 # 1. 问题现象与根因分析 ## 1.1 发送超时 发送超时指 FDCAN 在预定时间内未能完成报文发送,通常表现为 HAL_FDCAN_AddMessageToTxMailbox 返回 HAL_BUSY,或 TX Event FIFO 中无对应事件。根因包括: - **时钟源错误**:FDCAN 内核时钟(FDCAN_KER_CK)未正确分频,导致位时间计算偏差。 - **位时序过紧**:采样点设置不当,尤其在 400MHz 下,APB1 时钟(通常 100MHz)与 FDCAN 时钟(通常 20MHz)分频不匹配。 - **邮箱占用**:三个发送邮箱全部被低优先级报文占满。 ## 1.2 仲裁丢失 仲裁丢失发生在多个节点同时发送时,ID 较小的报文赢得仲裁。若本节点频繁丢失,可能是: - **位时序错误**:导致位采样偏移,误判总线电平。 - **滤波器配置**:接收滤波器误过滤了本应接收的报文,但发送侧无直接影响。 - **总线竞争**:总线负载过高,本节点发送优先级低。 # 2. 排查步骤 ## 2.1 检查时钟树配置 在 STM32H7 中,FDCAN 内核时钟源可选 PLL1Q、PLL2Q 或 PLL3Q,最大 80MHz。确保: - 使用 CubeMX 或 HAL 配置时,FDCAN_KER_CK 不超过 80MHz。 - 检查 RCC 时钟配置: ```c // 示例:配置 FDCAN1 时钟为 PLL1Q(20MHz) RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FDCAN; PeriphClkInit.Fdcan1ClockSelection = RCC_FDCAN1CLKSOURCE_PLL1Q; HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit); ``` 验证:在调试器中读取 `RCC->D2CCIP2R` 的 FDCANSEL 位,确认时钟源。 ## 2.2 位时序参数计算 FDCAN 位时间 = 同步段 + 传播段 + 相位缓冲段1 + 相位缓冲段2。在 400MHz 下,APB1 时钟通常为 100MHz,FDCAN 时钟为 20MHz,则 1 个时间量子(tq)= 50ns。若目标波特率 1Mbps,则位时间 = 1000ns = 20 tq。 推荐采样点:75%~80%。配置示例: ```c FDCAN_InitTypeDef FdcanInit = {0}; FdcanInit.ClockDivider = FDCAN_CLOCK_DIV1; FdcanInit.NominalPrescaler = 1; // 20MHz / 1 = 20MHz FdcanInit.NominalSyncJumpWidth = 1; FdcanInit.NominalTimeSeg1 = 14; // 15 tq (含同步段) FdcanInit.NominalTimeSeg2 = 5; // 6 tq // 采样点 = (1+15)/(1+15+6) = 72.7% ``` 注意:STM32H7 的 FDCAN 位时序寄存器中,TimeSeg1 和 TimeSeg2 的值需加 1 才是实际 tq 数。 ## 2.3 检查滤波器配置 滤波器误配置可能导致本节点无法接收应答帧(ACK),但发送超时通常与滤波器无关。然而,若使用了硬件过滤,确保标准 ID 和扩展 ID 的掩码正确: ```c FDCAN_FilterTypeDef FilterConfig = {0}; FilterConfig.IdType = FDCAN_STANDARD_ID; FilterConfig.FilterIndex = 0; FilterConfig.FilterType = FDCAN_FILTER_MASK; FilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; FilterConfig.FilterID1 = 0x123; // 期望 ID FilterConfig.FilterID2 = 0x7FF; // 掩码,0x7FF 表示精确匹配 HAL_FDCAN_ConfigFilter(&hfdcan1, &FilterConfig); ``` ## 2.4 中断优先级与邮箱管理 高优先级中断(如 CAN 发送中断)若被其他中断阻塞,可能导致发送超时。确保: - 使用 `HAL_FDCAN_ActivateNotification` 使能发送中断。 - 在中断回调中及时释放邮箱。 ```c void HAL_FDCAN_TxMailbox0CompleteCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t BufferIndexes) { // 处理发送完成,释放邮箱 HAL_FDCAN_AbortTxRequest(hfdcan, FDCAN_TX_MAILBOX0); } ``` # 3. 完整代码示例 以下代码演示了 FDCAN1 在 20MHz 内核时钟下,1Mbps 波特率的初始化与发送: ```c #include "stm32h7xx_hal.h" FDCAN_HandleTypeDef hfdcan1; void FDCAN1_Init(void) { hfdcan1.Instance = FDCAN1; hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1; hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS; 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 = 1; hfdcan1.Init.NominalTimeSeg1 = 14; hfdcan1.Init.NominalTimeSeg2 = 5; hfdcan1.Init.DataPrescaler = 1; hfdcan1.Init.DataSyncJumpWidth = 1; hfdcan1.Init.DataTimeSeg1 = 4; hfdcan1.Init.DataTimeSeg2 = 3; hfdcan1.Init.StdFiltersNbr = 1; hfdcan1.Init.ExtFiltersNbr = 0; hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; HAL_FDCAN_Init(&hfdcan1); // 配置滤波器 FDCAN_FilterTypeDef FilterConfig = {0}; FilterConfig.IdType = FDCAN_STANDARD_ID; FilterConfig.FilterIndex = 0; FilterConfig.FilterType = FDCAN_FILTER_MASK; FilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; FilterConfig.FilterID1 = 0x123; FilterConfig.FilterID2 = 0x7FF; HAL_FDCAN_ConfigFilter(&hfdcan1, &FilterConfig); // 启动 FDCAN HAL_FDCAN_Start(&hfdcan1); HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_TX_MAILBOX_EMPTY, 0); } void FDCAN1_Send(uint32_t id, uint8_t *data, uint8_t len) { FDCAN_TxHeaderTypeDef TxHeader = {0}; TxHeader.Identifier = id; TxHeader.IdType = FDCAN_STANDARD_ID; TxHeader.TxFrameType = FDCAN_DATA_FRAME; TxHeader.DataLength = len << 16; // 长度编码 TxHeader.FDFormat = FDCAN_FD_CAN; TxHeader.BitRateSwitch = DISABLE; TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS; uint32_t mailbox; if (HAL_FDCAN_AddMessageToTxMailbox(&hfdcan1, &TxHeader, data, &mailbox) != HAL_OK) { Error_Handler(); } } ``` # 4. 调试技巧与注意事项 - **使用逻辑分析仪**:抓取 CAN_H/CAN_L 波形,验证位时间是否与配置一致。 - **查看错误寄存器**:`HAL_FDCAN_GetError` 可获取仲裁丢失计数(LEC 字段)。 - **避免使用自动重传**:在调试阶段关闭自动重传,便于观察超时现象。 - **注意时钟分频**:STM32H7 的 FDCAN 时钟源若选择 PLL3Q,需确保 PLL3 配置正确。 - **中断优先级**:FDCAN 中断优先级应高于其他非关键中断,但低于系统滴答。 # 总结 解决 STM32H7 FDCAN 发送超时与仲裁丢失,核心在于时钟配置、位时序和中断管理。通过系统化排查,90% 的问题可定位到位时序参数错误或时钟源分频不当。建议在项目初期用 CubeMX 生成配置,并利用调试器实时监控寄存器,以快速收敛问题。