STM32H7 在 400MHz 下 FDCAN 发送超时与仲裁丢失的排查方法
👁 3 阅读 · 2026-08-27 · 嵌入式
STM32H7 在 400MHz 主频下运行 FDCAN 时,开发者常遇到发送超时或仲裁丢失问题,尤其在总线负载较高或配置不当的情况下。本文从时钟树、FDCAN 位时序、滤波器配置和中断优先级四个维度,系统分析根因,并给出可落地的排查步骤与代码示例,帮助开发者快速定位并解决此类问题。
# 引言
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 生成配置,并利用调试器实时监控寄存器,以快速收敛问题。