# 引言 在工业自动化与智能驾驶场景中,TSN(IEEE 802.1AS)提供亚微秒级网络同步,而 CAN FD 仍是车身与底盘控制的核心总线。当两者共存于同一网关时,必须实现时间基准对齐,否则数据融合将产生不可接受的延迟误差。STM32H7 系列凭借 400MHz Cortex-M7 内核、双 FDCAN 控制器以及灵活的定时器/外部信号接口,为硬件级时间同步提供了可能。 # 1. 硬件时序对齐原理 ## 1.1 时间同步的核心挑战 软件同步(如读取系统 tick 后打时间戳)会引入中断延迟、缓存等待等不确定性,通常误差在微秒级。而 TSN 要求同步精度优于 1μs,FDCAN 的硬件时间戳分辨率可达 20ns(50MHz 时钟)。因此,必须利用硬件外设直接捕获/生成时间事件。 ## 1.2 STM32H7 的硬件资源 - **FDCAN 控制器**:每个 FDCAN 内核内置 16 位/32 位时间戳计数器(TSCC),可配置为基于内部定时器或外部时钟源递增。 - **定时器(TIM2/TIM5)**:32 位高级定时器,支持外部时钟模式,可作为全局时间基准(如 10ns 分辨率)。 - **外部中断/事件控制器(EXTI)**:可将 TSN 的同步脉冲(如 PPS)映射到定时器触发输入。 - **DMA**:用于自动传输时间戳数据,减少 CPU 干预。 ## 1.3 对齐策略 采用**主-从时间同步**:TSN 主时钟通过 PPS 信号(每秒一个脉冲)输出到 STM32H7 的定时器输入捕获通道,定时器记录本地时间并校准偏移;同时,FDCAN 的硬件时间戳计数器与定时器共享同一时钟源(如 PLL2 输出 100MHz),从而保证 FDCAN 时间戳与 TSN 时间基准严格对齐。 # 2. 配置步骤 ## 2.1 时钟树设计 - 使用 HSE(25MHz)经 PLL1 生成 400MHz 系统时钟。 - 配置 PLL2 输出 100MHz 作为 FDCAN 内核时钟(FDCANCLK),同时作为 TIM2 的时钟源(通过 MUX 选择)。 - 确保 FDCAN 时间戳时钟(TSCC)选择为 FDCANCLK 分频,例如 50MHz(分频 2)。 ## 2.2 FDCAN 硬件时间戳配置 1. 使能 FDCAN 时钟(RCC->APB1ENR1 |= RCC_APB1ENR1_FDCAN1EN)。 2. 配置 FDCAN 位时序(数据段 2Mbps,仲裁段 500kbps)。 3. 设置时间戳单元: - 选择内部时钟源(FDCANCLK)。 - 设置分频系数(例如 2,得到 50MHz 计数频率)。 - 使能时间戳捕获(在接收/发送帧时自动打戳)。 ## 2.3 TSN 同步脉冲接入 - 将 TSN 的 PPS 信号连接到 TIM2_CH1(PA0)。 - 配置 TIM2 为输入捕获模式,上升沿触发,并记录捕获值。 - 通过比较捕获值与本地时间基准,计算偏移量并写入 FDCAN 时间戳偏移寄存器(TSOFF)。 ## 2.4 中断与 DMA - 使用 TIM2 捕获中断,在中断中更新偏移量(注意中断延迟,但仅需微秒级,可接受)。 - 对于 FDCAN 消息,使用 FIFO 的硬件时间戳,通过 DMA 读取到内存,避免 CPU 读取延迟。 # 3. 完整代码示例 以下基于 STM32H743 和 HAL 库,演示核心配置(省略 GPIO 初始化)。 ```c // 1. 初始化 FDCAN 时间戳 void FDCAN_TS_Init(FDCAN_HandleTypeDef *hfdcan) { hfdcan->Init.ClockDivider = FDCAN_CLOCK_DIV2; // 100MHz -> 50MHz hfdcan->Init.FrameFormat = FDCAN_FRAME_FD_BRS; hfdcan->Init.Mode = FDCAN_MODE_NORMAL; hfdcan->Init.AutoRetransmission = DISABLE; hfdcan->Init.TransmitPause = DISABLE; hfdcan->Init.ProtocolException = DISABLE; hfdcan->Init.NominalPrescaler = 2; // 500kbps hfdcan->Init.NominalSyncJumpWidth = 1; hfdcan->Init.NominalTimeSeg1 = 13; hfdcan->Init.NominalTimeSeg2 = 2; hfdcan->Init.DataPrescaler = 1; // 2Mbps hfdcan->Init.DataSyncJumpWidth = 1; hfdcan->Init.DataTimeSeg1 = 13; hfdcan->Init.DataTimeSeg2 = 2; hfdcan->Init.StdFiltersNbr = 1; hfdcan->Init.ExtFiltersNbr = 0; hfdcan->Init.TxEventsNbr = 0; hfdcan->Init.RxFifo0ElmtsNbr = 8; hfdcan->Init.RxFifo0ElmtsSize = FDCAN_DATA_BYTES_8; hfdcan->Init.RxFifo1ElmtsNbr = 0; hfdcan->Init.RxFifo1ElmtsSize = FDCAN_DATA_BYTES_8; hfdcan->Init.TxEventsNbr = 0; hfdcan->Init.TxBuffersNbr = 0; HAL_FDCAN_Init(hfdcan); // 配置时间戳单元 FDCAN_TimestampConfigTypeDef tsConfig; tsConfig.TimestampPrescaler = 1; // 50MHz/1 = 50MHz tsConfig.TimestampGlobal = FDCAN_TIMESTAMP_GLOBAL; // 全局时间戳 tsConfig.TimestampInternal = FDCAN_TIMESTAMP_INTERNAL; // 使用内部时钟 HAL_FDCAN_ConfigTimestamp(hfdcan, &tsConfig); // 使能接收时间戳捕获 HAL_FDCAN_ActivateTimestamp(hfdcan, FDCAN_TIMESTAMP_RX); } // 2. TIM2 输入捕获初始化(PPS 信号) void TIM2_PPS_Init(void) { __HAL_RCC_TIM2_CLK_ENABLE(); TIM_HandleTypeDef htim2 = {0}; htim2.Instance = TIM2; htim2.Init.Prescaler = 0; // 100MHz 计数 htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 0xFFFFFFFF; // 32 位自由运行 htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_IC_Init(&htim2); TIM_IC_InitTypeDef sConfigIC = {0}; sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING; sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; sConfigIC.ICFilter = 0; HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); } // 3. 捕获中断回调(更新 FDCAN 偏移) void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM2) { uint32_t pps_capture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 假设 TSN 主时钟的 PPS 对应全局时间 T_global(例如 0 或已知值) // 计算偏移:offset = T_global - pps_capture(需考虑溢出) uint32_t offset = 0; // 根据实际同步协议计算 // 写入 FDCAN 时间戳偏移寄存器(通过 HAL 或直接寄存器操作) FDCAN1->TSOFF = offset; } } // 4. 接收消息时读取时间戳(示例) void FDCAN_RxCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t fifo) { FDCAN_RxHeaderTypeDef rxHeader; uint8_t data[8]; HAL_FDCAN_GetRxMessage(hfdcan, fifo, &rxHeader, data); uint32_t timestamp = rxHeader.Timestamp; // 硬件时间戳,已与 TSN 对齐 // 进一步处理... } ``` # 4. 注意事项 - **时钟同步精度**:确保 FDCAN 时间戳时钟与 TIM2 时钟同源(如均来自 PLL2),否则存在相位漂移。建议使用 PLL2 的专用输出,并避免分频不一致。 - **溢出处理**:32 位时间戳在 50MHz 下约 85.9 秒回绕,需在应用层实现扩展(如记录秒数)。 - **中断优先级**:TIM2 捕获中断应设为最高优先级,但中断处理需极短(仅计算偏移),避免影响 FDCAN 接收中断。 - **硬件连接**:PPS 信号应使用高速数字隔离器(如 ISO7721)以降低噪声,且走线尽量短。 - **调试技巧**:使用逻辑分析仪同时抓取 PPS 和 FDCAN 发送的 SYNC 帧,验证对齐误差是否在 ±100ns 内。 # 5. 总结 通过 STM32H7 的硬件时间戳单元和外部同步接口,可以实现 FDCAN 与 TSN 的亚微秒级时间对齐,无需软件干预。本文提供的配置方法和代码可直接应用于网关设计。实际项目中,还需结合具体 TSN 协议栈(如 gPTP)进行偏移计算,但硬件基础已就绪。