# 引言 在工业 4.0 与自动驾驶场景中,TSN 提供确定性以太网传输,而 CAN FD 则广泛存在于现有车载与工控节点。STM32H7 系列(如 STM32H743/750)凭借 480MHz Cortex-M7 内核、双 FDCAN 控制器以及内置 TSN 以太网 MAC(10/100M),成为实现异构时间同步的理想平台。然而,FDCAN 与 TSN 的时钟源不同(FDCAN 基于 APB1,TSN 基于 RCC 的 PTP 时钟),若缺乏硬件级对齐,同步误差可达微秒级。本文提出一种硬件触发对齐策略,利用 STM32H7 的 TIM 与 EXTI 联动,实现 FDCAN 发送/接收事件与 TSN IEEE 802.1AS 时间戳的精确关联。 # 原理剖析 ## 1. 时间同步基础 - **TSN 时间同步**:遵循 IEEE 802.1AS(gPTP),通过 Sync/Follow_Up 报文传递主时钟时间,从时钟基于 PTP 硬件时间戳校正本地时钟。STM32H7 的以太网 MAC 内置 PTP 时间戳单元(TSU),可捕获帧收发时刻,精度达 20ns(在 50MHz PTP 时钟下)。 - **FDCAN 时间戳**:FDCAN 控制器提供 16 位或 32 位时间戳计数器,基于 APB1 时钟(典型 100MHz),但无内置 PTP 同步机制。 ## 2. 对齐难点 - 时钟频率差异:APB1 时钟与 PTP 时钟(通常为 50MHz)不同源,导致 FDCAN 时间戳与 TSN 时间戳无法直接换算。 - 事件触发延迟:软件读取时间戳会引入不确定的延迟(中断响应、总线访问等),破坏同步精度。 ## 3. 硬件触发对齐策略 利用 STM32H7 的 **TIM1/8 高级定时器** 作为共享时基,并配置为外部触发模式: - 将 FDCAN 的 RX/TX 事件映射到 TIM 的输入捕获通道(通过 FDCAN 的 TTTS 或 TTCF 信号)。 - 同时,将 TSN 的 PTP 事件(如 Sync 帧接收)通过 EXTI 触发 TIM 捕获。 - 这样,TIM 捕获寄存器记录的是同一硬件时基下的时刻,从而消除时钟源差异。 更精确的做法:使用 **LPTIM(低功耗定时器)** 或 **HRTIM(高分辨率定时器)**,但 STM32H7 的 HRTIM 可提供 2.08ns 分辨率(480MHz),适合纳秒级对齐。本文以 TIM1 为例,兼顾通用性。 # 配置步骤 ## 1. 时钟配置 - 使用 CubeMX 配置系统时钟:HSE=25MHz,PLL1 输出 480MHz 给 CPU,PLL2 输出 100MHz 给 APB1(FDCAN),PLL3 输出 50MHz 给以太网 PTP。 - 注意:FDCAN 时钟必须来自 APB1,且 APB1 定时器时钟为 200MHz(若 APB1 分频为 1/2,则定时器时钟为 2×100MHz)。 ## 2. FDCAN 配置 - 启用 FDCAN1,模式为正常模式,波特率 1Mbps(数据段 5Mbps)。 - 在 FDCAN 全局配置中,使能 **Timestamp** 功能,并选择时间戳源为 **TIM1**(通过 FDCAN_TTTS 寄存器设置)。 ## 3. TIM1 配置 - 设置 TIM1 为输入捕获模式,通道1 捕获 FDCAN 的 RX 事件(通过 FDCAN 的 TTTS 信号映射到 TIM1_CH1),通道2 捕获 TSN 的 PTP 事件(通过 EXTI 线映射到 TIM1_CH2,需配置 EXTI 与 TIM 的互联)。 - 预分频器设为 0,计数时钟为 200MHz(分辨率 5ns)。 ## 4. TSN 配置 - 启用以太网 MAC,配置 PTP 模式为 802.1AS,使能时间戳捕获。 - 配置 EXTI 线(如 PA0)为上升沿触发,连接到以太网 MAC 的 PTP 事件输出(需通过 GPIO 复用)。 # 代码示例 以下代码基于 STM32H743 和 HAL 库,演示核心配置与中断处理。 ```c // 初始化 TIM1 输入捕获 void TIM1_IC_Init(void) { TIM_IC_InitTypeDef sICConfig; TIM_HandleTypeDef htim1; __HAL_RCC_TIM1_CLK_ENABLE(); htim1.Instance = TIM1; htim1.Init.Prescaler = 0; htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 0xFFFFFFFF; // 32位计数器 htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_IC_Init(&htim1); sICConfig.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING; sICConfig.ICSelection = TIM_ICSELECTION_DIRECTTI; sICConfig.ICPrescaler = TIM_ICPSC_DIV1; sICConfig.ICFilter = 0; HAL_TIM_IC_ConfigChannel(&htim1, &sICConfig, TIM_CHANNEL_1); HAL_TIM_IC_ConfigChannel(&htim1, &sICConfig, TIM_CHANNEL_2); HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_2); } // FDCAN 时间戳源选择 TIM1 void FDCAN_SetTimestampSource(void) { FDCAN_GlobalTypeDef *fdcan = FDCAN1; // 设置 TTTS 寄存器,选择 TIM1 作为时间戳源 fdcan->TTTS = (fdcan->TTTS & ~FDCAN_TTTS_TSS_MASK) | (0x02 << FDCAN_TTTS_TSS_Pos); } // 中断回调:捕获事件 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) { uint32_t fdcan_time = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 记录 FDCAN 事件时刻,与 TSN 时间戳关联 process_fdcan_event(fdcan_time); } else if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2) { uint32_t tsn_time = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); // 记录 TSN PTP 事件时刻 process_tsn_event(tsn_time); } } // 主函数中初始化并启动 int main(void) { HAL_Init(); SystemClock_Config(); TIM1_IC_Init(); FDCAN_SetTimestampSource(); // 配置 FDCAN 和以太网 MAC... while (1) { // 应用逻辑 } } ``` # 注意事项 - **时钟同步**:确保 TIM1 计数时钟与 FDCAN 时间戳时钟同源(都来自 APB1 定时器时钟),否则仍需校准。 - **信号映射**:FDCAN 的 TTTS 信号输出到 TIM 需要查阅参考手册的 GPIO 复用表,通常通过 AF9 或 AF10 映射。 - **中断优先级**:TIM 捕获中断应设为最高优先级,避免被其他中断延迟。 - **溢出处理**:32 位计数器在 200MHz 下约 21.5 秒溢出,需在中断中记录溢出次数,扩展时间范围。 - **调试技巧**:使用逻辑分析仪同时抓取 FDCAN 报文和 TSN Sync 帧,对比 TIM 捕获值,验证对齐精度。 # 结语 通过硬件触发对齐策略,STM32H7 在 480MHz 下实现了 FDCAN 与 TSN 的纳秒级时间同步,避免了软件时间戳的不确定性。该方案适用于工业网关、车载域控制器等场景,为异构实时网络提供了坚实基础。开发者可根据实际需求,将 TIM1 替换为 HRTIM 以获取更高精度,或扩展至多通道同步。