引言
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 时钟配置:
// 示例:配置 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%。配置示例:
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 的掩码正确:
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使能发送中断。 - 在中断回调中及时释放邮箱。
void HAL_FDCAN_TxMailbox0CompleteCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t BufferIndexes) {
// 处理发送完成,释放邮箱
HAL_FDCAN_AbortTxRequest(hfdcan, FDCAN_TX_MAILBOX0);
}
3. 完整代码示例
以下代码演示了 FDCAN1 在 20MHz 内核时钟下,1Mbps 波特率的初始化与发送:
#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 生成配置,并利用调试器实时监控寄存器,以快速收敛问题。