引言

在工业自动化与车载网络中,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)。
// 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 计数器值。由于两者时钟不同,需建立映射关系。

// 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 的时钟补偿值。

// 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 个样本)。

#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)。

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 参数与滤波窗口,达到最佳平衡。