STM32H7 480MHz 下 FDCAN 与经典 CAN 的时序兼容性陷阱及解法
1. 问题背景
STM32H7 系列最高运行在 480MHz,其内置的 FDCAN 模块(基于 Bosch M_CAN)支持 CAN 2.0 和 CAN FD 协议。当 FDCAN 节点与经典 CAN 节点(如 SJA1000、MCP2515)混合组网时,开发者常遇到以下现象:
- 总线偶尔出现错误帧,但重试后恢复;
- 特定波特率(如 500kbps)下通信完全失败;
- 不同板卡间时序表现不一致。
这些问题的根源在于 FDCAN 位时序配置在高速外设时钟下的量化误差,以及经典 CAN 协议对位时间结构的隐性要求。
2. 位时序基础与 FDCAN 差异
2.1 经典 CAN 位时间结构
经典 CAN(ISO 11898-1)将一位时间划分为四个段:
- 同步段 (SYNC_SEG):固定 1 TQ(Time Quantum),用于同步总线上的各个节点;
- 传播段 (PROP_SEG):补偿物理延迟(总线长度、收发器延迟等),可编程 1-8 TQ;
- 相位缓冲段 1 (PHASE_SEG1):用于补偿上升沿相位误差,可编程 1-8 TQ;
- 相位缓冲段 2 (PHASE_SEG2):用于补偿下降沿相位误差,可编程 1-8 TQ;
- 同步跳转宽度 (SJW):最大可调 1-4 TQ,用于重同步。
经典 CAN 要求:PROP_SEG + PHASE_SEG1 >= 2 TQ(通常建议 >= 2 TQ),且 PHASE_SEG2 > SJW。
2.2 FDCAN 的位时间寄存器
FDCAN 使用 NBTP(Nominal Bit Timing & Prescaler)寄存器配置经典 CAN 部分,其字段包括:
-
BRP(Baud Rate Prescaler):分频系数,范围 1-512; -
TSEG1:时间段 1(包含 PROP_SEG + PHASE_SEG1),范围 2-256 TQ; -
TSEG2:时间段 2(即 PHASE_SEG2),范围 2-128 TQ; -
SJW:同步跳转宽度,范围 1-128 TQ。
注意:FDCAN 将 SYNC_SEG 固定为 1 TQ,且 TSEG1 至少为 2 TQ(隐含 PROP_SEG 至少 1 TQ)。这与经典 CAN 的段划分在概念上一致,但寄存器映射不同。
3. 480MHz 时钟下的量化误差陷阱
3.1 误差来源
STM32H7 的 FDCAN 外设时钟(fdcan_ker_ck)通常由 PLL2 或 PLL3 生成,典型值为 80MHz 或 100MHz。若直接使用 480MHz 作为外设时钟(某些配置可能),则位时间计算如下:
- 目标波特率:500kbps → 位时间 = 1/500k = 2000ns;
- 若外设时钟 = 480MHz,则 TQ = 1/480MHz ≈ 2.083ns;
- 需要的 TQ 数 = 2000ns / 2.083ns ≈ 960.4,非整数!
实际配置时,必须取整,导致实际波特率偏离目标。例如取 960 TQ,则实际波特率 = 480MHz / 960 = 500.0kbps(巧合),但若取 961 TQ,则变为 499.48kbps,误差 0.1%。
3.2 误差影响
经典 CAN 协议要求所有节点波特率误差不超过 ±0.5%(通常更严格为 ±0.3%),且位时间各段比例需满足重同步条件。当 FDCAN 节点因量化误差导致位时间偏短或偏长,而经典 CAN 节点(如 MCP2515 使用 16MHz 晶振,误差极小)无法容忍时,就会产生错误帧。
更隐蔽的是:即使总波特率误差在范围内,若 TSEG1 和 TSEG2 的比例不合理(例如 TSEG1 过小),会导致采样点位置偏移,在总线负载高时出现位错误。
4. 配置步骤:以 STM32H743 为例
4.1 硬件环境
- MCU:STM32H743ZI(480MHz)
- FDCAN1 外设时钟:PLL2Q,配置为 80MHz(推荐)
- 收发器:TJA1050(经典 CAN)
- 目标波特率:500kbps,采样点 75%
4.2 使用 STM32CubeMX 配置
-
时钟树:将
FDCAN1 kernel clock设置为 80MHz(通过 PLL2,分频系数确保精确)。 -
FDCAN1 参数:
- 模式:Normal(经典 CAN)
- 波特率:500 kBit/s
- 采样点:75%
- 自动计算:CubeMX 会生成
NBTP值。
-
检查生成的时序:
- 位时间 = 80MHz / 500k = 160 TQ(整数,理想)
- 采样点 75% → 采样点位置 = 0.75 * 160 = 120 TQ
- 通常
SYNC_SEG=1,TSEG1=119,TSEG2=40(但 TSEG2 最大 128,可行) - 但注意:经典 CAN 要求
PROP_SEG + PHASE_SEG1 >= 2 TQ,这里 TSEG1=119 满足,但实际采样点可能偏移。
4.3 手动优化配置
CubeMX 自动计算可能不满足经典 CAN 的段比例要求。建议手动设置:
- 保持
BRP=1(80MHz / 1 = 80MHz TQ) - 位时间 = 160 TQ
- 设置
TSEG1=118(包含 PROP_SEG + PHASE_SEG1),TSEG2=41(PHASE_SEG2),SJW=20(最大 128,但建议 16) - 采样点 = (1 + 118) / 160 = 74.375% ≈ 75%
这样 PROP_SEG 隐含为 1 TQ(因为 TSEG1 最小 2,但实际可设更大),但为了补偿总线延迟,建议 PROP_SEG 至少 2 TQ。因此可设 TSEG1=117,TSEG2=42,采样点 = 118/160 = 73.75%。
5. 代码示例:使用 HAL 库配置 FDCAN
以下代码演示如何手动设置 NBTP 寄存器,确保与经典 CAN 兼容。
#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_CLASSIC;
hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
hfdcan1.Init.AutoRetransmission = ENABLE;
// 手动配置位时序,外设时钟 80MHz,目标 500kbps
// 位时间 = 80MHz / 500k = 160 TQ
// 设置 TSEG1=117, TSEG2=42, SJW=16 (经典 CAN 要求 SJW <= PHASE_SEG2)
hfdcan1.Init.NominalPrescaler = 1; // BRP
hfdcan1.Init.NominalSyncJumpWidth = 16; // SJW
hfdcan1.Init.NominalTimeSeg1 = 117; // TSEG1 (包含 PROP+PHASE1)
hfdcan1.Init.NominalTimeSeg2 = 42; // TSEG2 (PHASE2)
// 数据段时序(仅 FD 模式使用,此处忽略)
hfdcan1.Init.DataPrescaler = 1;
hfdcan1.Init.DataSyncJumpWidth = 1;
hfdcan1.Init.DataTimeSeg1 = 1;
hfdcan1.Init.DataTimeSeg2 = 1;
hfdcan1.Init.MessageRAMOffset = 0;
hfdcan1.Init.StdFiltersNbr = 1;
hfdcan1.Init.ExtFiltersNbr = 0;
hfdcan1.Init.RxFifo0ElmtsNbr = 8;
hfdcan1.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_MAX;
hfdcan1.Init.TxEventsNbr = 0;
hfdcan1.Init.TxBuffersNbr = 0;
if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK)
{
Error_Handler();
}
// 配置过滤器,接收所有经典 CAN 帧
FDCAN_FilterTypeDef sFilterConfig;
sFilterConfig.IdType = FDCAN_STANDARD_ID;
sFilterConfig.FilterIndex = 0;
sFilterConfig.FilterType = FDCAN_FILTER_MASK;
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
sFilterConfig.FilterID1 = 0x000;
sFilterConfig.FilterID2 = 0x7FF; // 掩码,接收所有
HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig);
// 启动 FDCAN
HAL_FDCAN_Start(&hfdcan1);
HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);
}
// 发送经典 CAN 帧示例
void CAN_SendMessage(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) | FDCAN_DLC_BYTES_8; // 经典 CAN 固定 8 字节
txHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
txHeader.BitRateSwitch = FDCAN_BRS_OFF;
txHeader.FDFormat = FDCAN_CLASSIC_CAN;
txHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, data) != HAL_OK)
{
// 处理错误
}
}
6. 注意事项与调试技巧
- 外设时钟选择:优先使用 80MHz 或 100MHz,避免 480MHz 直接分频导致 TQ 数非整数。若必须用 480MHz,则需仔细计算并接受微小误差。
-
采样点位置:经典 CAN 推荐采样点位于 70%-80% 之间。对于 500kbps,75% 是常见选择。若总线较长(>10m),应增大
PROP_SEG(即增大TSEG1),但保持采样点不变。 -
SJW 设置:经典 CAN 中 SJW 应小于等于
PHASE_SEG2,且建议为 1-4 TQ。FDCAN 允许更大值,但过大可能导致振荡。 -
使用逻辑分析仪:抓取总线波形,测量实际位时间,验证配置。若发现位时间偏差 >0.5%,需调整
BRP或TSEG。 - 兼容性测试:与不同品牌经典 CAN 控制器(如 SJA1000、MCP2515)联调,确保在极端温度下仍稳定。
- 错误处理:启用 FDCAN 错误中断,记录错误码(如 ACK 错误、位错误),帮助定位时序问题。
7. 总结
STM32H7 在 480MHz 下运行 FDCAN 时,位时序的量化误差是导致与经典 CAN 不兼容的主要原因。通过合理选择外设时钟、手动配置 NBTP 参数,并遵循经典 CAN 的段比例要求,可以完全避免该陷阱。建议开发者使用 CubeMX 生成初始配置后,再根据实际总线环境微调,并用示波器验证。
掌握这些细节,你的嵌入式系统将能在混合 CAN 网络中稳定运行,发挥 STM32H7 的高性能优势。