# 引言 在工业自动化与车载域控中,TSN(如 802.1AS)提供纳秒级时钟同步,而 CAN FD 仍是主流总线。当两者共存于同一系统(如 TSN 交换机连接多个 FDCAN 节点)时,需要将 FDCAN 的发送/接收时刻对齐到 TSN 全局时间。STM32H7 的 FDCAN 支持硬件触发(Trigger)功能,可基于定时器或外部信号启动传输,配合 480MHz 主频和 HRTIM 的高分辨率,能实现精确对齐。 # 原理剖析 ## 1. STM32H7 的 FDCAN 硬件触发机制 FDCAN 外设(兼容 Bosch M_CAN)包含一个 Trigger 单元,允许通过以下方式触发帧传输: - 定时器比较匹配(如 TIM1/8 或 HRTIM) - 外部事件(如 EXTI 线) - 软件触发(立即发送) 触发信号通过 `FDCAN_TTTR` 寄存器配置,并映射到特定 GPIO 或内部信号。当触发发生时,FDCAN 自动从 TX 邮箱加载帧并发送,无需 CPU 干预。 ## 2. TSN 时间同步的硬件对齐策略 TSN 节点通常有本地时钟(如 802.1AS 的 gPTP),通过 PHY 或 MAC 层时间戳获取全局时间。STM32H7 可通过以下方式对齐: - 将 TSN 时钟(如 ETH MAC 的 PTP 时间)映射到定时器输入捕获,生成周期脉冲。 - 利用该脉冲作为 FDCAN 的硬件触发源,使 FDCAN 帧的起始位与 TSN 的同步帧(如 PTP Sync)对齐。 关键点:触发信号必须与 FDCAN 的位时序同步,避免因触发抖动导致偏差。STM32H7 的 FDCAN 支持在触发后延迟若干位时间(通过 `TTOCN` 寄存器)来微调,实现亚微秒级对齐。 # 配置步骤 ## 1. 硬件准备 - STM32H743 或 H750 开发板(主频 480MHz) - TSN 以太网 PHY(如 KSZ8463) - CAN 收发器(如 TJA1044) - 示波器或逻辑分析仪验证时序 ## 2. 时钟与 GPIO 配置 使用 CubeMX 配置: - 系统时钟:480MHz(HSE + PLL) - FDCAN1:波特率 1Mbps,使用外部触发 - TIM1:作为触发源,输出比较模式 - GPIO:FDCAN1_TX (PB9), FDCAN1_RX (PB8),TIM1_CH1 (PA8) 用于触发输入(内部连接) ## 3. FDCAN 触发配置 在 FDCAN 初始化中,设置 `TTOCN`(Triggered Transmit Operation Control)寄存器: - 使能触发发送模式 - 选择触发源为 TIM1 的 TRGO - 设置触发后延迟(如 0 位时间) ## 4. 定时器配置 TIM1 配置为 PWM 模式,频率与 TSN 同步周期一致(如 1kHz),占空比 50%。将 TRGO 连接到 FDCAN 触发输入(通过内部信号,无需外部连线)。 # 完整代码示例 以下代码基于 HAL 库,演示如何配置 FDCAN 硬件触发与 TIM1 同步。 ```c // 初始化 FDCAN1 并启用硬件触发 void FDCAN_Trigger_Init(void) { FDCAN_HandleTypeDef hfdcan1; 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 = DISABLE; hfdcan1.Init.TransmitPause = DISABLE; hfdcan1.Init.ProtocolException = DISABLE; hfdcan1.Init.NominalPrescaler = 10; // 480MHz / 10 = 48MHz hfdcan1.Init.NominalSyncJumpWidth = 1; hfdcan1.Init.NominalTimeSeg1 = 13; // 采样点 75% hfdcan1.Init.NominalTimeSeg2 = 2; hfdcan1.Init.DataPrescaler = 5; hfdcan1.Init.DataSyncJumpWidth = 1; hfdcan1.Init.DataTimeSeg1 = 13; hfdcan1.Init.DataTimeSeg2 = 2; hfdcan1.Init.StdFiltersNbr = 1; hfdcan1.Init.ExtFiltersNbr = 0; hfdcan1.Init.TxEventsNbr = 1; hfdcan1.Init.RxFifo0ElmtsNbr = 1; hfdcan1.Init.RxFifo0ElmtsSize = FDCAN_DATA_BYTES_8; hfdcan1.Init.RxFifo1ElmtsNbr = 0; hfdcan1.Init.RxFifo1ElmtsSize = FDCAN_DATA_BYTES_8; hfdcan1.Init.TxEventsNbr = 1; hfdcan1.Init.TxBuffersNbr = 1; HAL_FDCAN_Init(&hfdcan1); // 配置触发发送 FDCAN_TriggerConfigTypeDef triggerConfig; triggerConfig.TriggerMode = FDCAN_TRIGGER_MODE_TIMER; triggerConfig.TriggerSource = FDCAN_TRIGGER_SOURCE_TIM1_TRGO; triggerConfig.TriggerDelay = 0; // 无延迟 triggerConfig.TriggerPolarity = FDCAN_TRIGGER_POLARITY_RISING; HAL_FDCAN_ConfigTrigger(&hfdcan1, &triggerConfig); // 使能触发发送 HAL_FDCAN_ActivateTrigger(&hfdcan1, FDCAN_TRIGGER_TX); // 配置 TX 邮箱(示例:发送 ID 0x123) FDCAN_TxHeaderTypeDef txHeader; txHeader.Identifier = 0x123; txHeader.IdType = FDCAN_STANDARD_ID; txHeader.TxFrameType = FDCAN_DATA_FRAME; txHeader.DataLength = FDCAN_DLC_BYTES_8; txHeader.BitRateSwitch = FDCAN_BRS_ON; txHeader.FDFormat = FDCAN_FD_CAN; txHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS; txHeader.MessageMarker = 0; uint8_t data[8] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}; HAL_FDCAN_AddMessageToTxBuffer(&hfdcan1, &txHeader, data, 0); } // 初始化 TIM1 作为触发源 void TIM1_Trigger_Init(void) { TIM_HandleTypeDef htim1; htim1.Instance = TIM1; htim1.Init.Prescaler = 480 - 1; // 480MHz / 480 = 1MHz htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 1000 - 1; // 1kHz 频率 htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter = 0; htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_Base_Init(&htim1); TIM_OC_InitTypeDef ocConfig; ocConfig.OCMode = TIM_OCMODE_PWM1; ocConfig.Pulse = 500; // 50% 占空比 ocConfig.OCPolarity = TIM_OCPOLARITY_HIGH; ocConfig.OCFastMode = TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(&htim1, &ocConfig, TIM_CHANNEL_1); // 配置 TRGO 为更新事件(或比较匹配) TIM_MasterConfigTypeDef masterConfig; masterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; masterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; HAL_TIMEx_MasterConfigSynchronization(&htim1, &masterConfig); HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); } // 主函数中调用 int main(void) { HAL_Init(); SystemClock_Config(); // 配置 480MHz GPIO_Init(); // 配置 FDCAN 和 TIM 引脚 FDCAN_Trigger_Init(); TIM1_Trigger_Init(); while (1) { // 主循环,无需干预发送,由硬件触发 } } ``` # 注意事项 - **触发源选择**:确保 TIM1 的 TRGO 信号已正确映射到 FDCAN 的触发输入。在 STM32H7 中,这通常通过 `SYSCFG` 或内部互连矩阵配置,CubeMX 可自动处理。 - **位时间对齐**:触发延迟(`TriggerDelay`)的单位是 FDCAN 位时间,需根据实际采样点计算。例如,若采样点在 75%,延迟 0 位意味着帧起始位与触发沿对齐,但可能需微调以补偿内部延迟。 - **时钟同步**:TSN 的 PTP 时钟与 TIM1 的同步至关重要。建议使用 ETH 的 PTP 事件触发 TIM1 的捕获,或通过外部 PLL 锁定,避免漂移。 - **中断优先级**:若使用触发中断,需设置高优先级,避免因其他中断延迟导致触发抖动。 - **调试验证**:使用示波器测量 FDCAN_TX 引脚与 TSN 同步信号,观察相位差。若偏差大于 1 位时间,调整 `TriggerDelay` 或 TIM1 的相位。 - **功耗与温度**:480MHz 下注意散热,且 FDCAN 触发功能在低功耗模式下可能不可用,需保持设备在运行模式。 # 总结 通过 STM32H7 的硬件触发机制,FDCAN 可以与 TSN 时间同步实现硬件级对齐,无需 CPU 干预,精度可达微秒级。本文提供的配置和代码可直接应用于工业网关或车载域控制器,为混合网络提供可靠的时间一致性。实际项目中,还需结合具体 TSN 协议栈(如 gPTP)进行时钟校准,以进一步优化同步性能。