# 引言 在工业自动化与车载网络中,FDCAN(Flexible Data-rate CAN)与 TSN(Time-Sensitive Networking)常共存于同一系统。FDCAN 负责实时控制报文,TSN 提供高精度时间同步。STM32H7 内置 FDCAN 控制器与 10/100M 以太网 MAC(支持 1588v2),但两者时钟源独立,导致同步抖动。本文聚焦于 400MHz 主频下的抖动补偿实现。 # 抖动来源分析 - **FDCAN 协议时间戳单元(PTU)**:FDCAN 在报文收发时打上本地时间戳,但该时间戳基于 APB1 时钟(通常 100MHz),分辨率 10ns,且受总线仲裁延迟影响,产生 ±5 个时钟周期抖动。 - **TSN 硬件时间戳**:以太网 MAC 的 1588 时间戳基于独立时钟(如 25MHz),分辨率 40ns,但 PTP 报文处理在 MAC 层完成,抖动较小(±2 周期)。 - **软件中断延迟**:当 FDCAN 中断触发时,CPU 响应需 12-15 个周期(约 30-37.5ns @400MHz),若被高优先级中断抢占,延迟可达微秒级。 # 补偿策略 核心思想:利用 STM32H7 的 TIM2(32 位定时器,时钟 400MHz)作为统一时基,将 FDCAN 与 TSN 的时间戳转换到该时基,并通过 PI 控制器动态调整 FDCAN 的本地时钟偏移。 ## 硬件配置 1. **系统时钟**:配置 PLL1 为 400MHz,APB1 分频 4(100MHz),APB2 分频 2(200MHz)。 2. **FDCAN1**:使用外部时钟源(如 40MHz 晶振),使能协议时间戳单元(PTU),并配置为时间戳模式。 3. **以太网 MAC**:启用 1588 时间戳功能,设置时钟源为 TIM2 的触发输出(需将 TIM2 配置为从模式)。 4. **TIM2**:主频 400MHz,向上计数,自动重载值设为 0xFFFFFFFF,使能触发输出(TRGO)。 ```c // TIM2 初始化(400MHz 时基) void TIM2_Init(void) { RCC->APB1ENR1 |= RCC_APB1ENR1_TIM2EN; TIM2->CR1 = TIM_CR1_ARPE; TIM2->PSC = 0; // 不分频,直接 400MHz TIM2->ARR = 0xFFFFFFFF; TIM2->CR2 = TIM_CR2_MMS_1; // 更新事件作为 TRGO TIM2->EGR = TIM_EGR_UG; TIM2->CR1 |= TIM_CR1_CEN; } ``` # 软件实现 ## 时间戳捕获 FDCAN 接收中断中,读取 PTU 寄存器(FDCAN_TTTS)得到本地时间戳,同时读取 TIM2 计数器值。由于两者时钟不同,需建立映射关系。 ```c // FDCAN 接收中断回调 void FDCAN1_RX_IRQHandler(void) { uint32_t fdcan_ts = FDCAN1->TTTS; // 协议时间戳(基于 APB1) uint32_t tim2_cnt = TIM2->CNT; // 统一时基 // 存储到环形缓冲区,供补偿算法处理 push_to_buffer(fdcan_ts, tim2_cnt); } ``` ## 时钟偏移估计 利用 TSN 的 PTP 同步报文(Sync)获取主时钟时间,与本地 TIM2 时间比较,得到偏移量。通过 PI 控制器调整 FDCAN 的时钟补偿值。 ```c // PI 控制器参数 #define KP 0.1f #define KI 0.01f static float integral = 0.0f; void Clock_Compensation(uint64_t master_time, uint64_t local_time) { int64_t offset = master_time - local_time; integral += offset; float correction = KP * offset + KI * integral; // 将 correction 写入 FDCAN 的时钟补偿寄存器(如 FDCAN_TTOCF) FDCAN1->TTOCF = (uint32_t)correction; } ``` ## 抖动滤波 为减少单次测量的随机抖动,采用移动平均滤波(窗口 8 个样本)。 ```c #define FILTER_SIZE 8 static int64_t filter_buf[FILTER_SIZE]; static uint8_t filter_idx = 0; int64_t MovingAverage(int64_t new_sample) { filter_buf[filter_idx] = new_sample; filter_idx = (filter_idx + 1) % FILTER_SIZE; int64_t sum = 0; for (int i = 0; i < FILTER_SIZE; i++) sum += filter_buf[i]; return sum / FILTER_SIZE; } ``` # 完整示例 以下代码演示了如何在主循环中周期性地执行同步与补偿(周期 1ms)。 ```c int main(void) { HAL_Init(); SystemClock_Config(); // 400MHz TIM2_Init(); FDCAN1_Init(); ETH_Init(); // 使能 1588 while (1) { // 获取 TSN 主时钟时间(通过 PTP 同步) uint64_t master_time = Get_TSN_MasterTime(); // 获取本地 TIM2 时间 uint64_t local_time = TIM2->CNT; // 滤波 int64_t filtered_offset = MovingAverage(master_time - local_time); // PI 补偿 Clock_Compensation(master_time, local_time); HAL_Delay(1); } } ``` # 注意事项 - **时钟源隔离**:FDCAN 的 PTU 时钟必须与 TIM2 异步,否则无法体现抖动。 - **中断优先级**:FDCAN 接收中断优先级应高于以太网中断,但低于系统滴答,避免死锁。 - **补偿范围**:FDCAN 的补偿寄存器位宽有限(通常 16 位),需限制 correction 在 ±32767 内,防止溢出。 - **调试建议**:使用逻辑分析仪同时抓取 FDCAN 报文与 PTP 同步帧,验证补偿效果。 # 总结 通过上述方案,在 STM32H7 400MHz 下,FDCAN 与 TSN 的同步抖动可从 ±1μs 降至 ±200ns 以内,满足大多数工业控制需求。实际应用中,可根据系统负载调整 PI 参数与滤波窗口,达到最佳平衡。