引言
在工业自动化与车载网络中,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 的本地时钟偏移。
硬件配置
- 系统时钟:配置 PLL1 为 400MHz,APB1 分频 4(100MHz),APB2 分频 2(200MHz)。
- FDCAN1:使用外部时钟源(如 40MHz 晶振),使能协议时间戳单元(PTU),并配置为时间戳模式。
- 以太网 MAC:启用 1588 时间戳功能,设置时钟源为 TIM2 的触发输出(需将 TIM2 配置为从模式)。
- 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 参数与滤波窗口,达到最佳平衡。