引言

在工业自动化与车载域控中,TSN(如 802.1AS)提供纳秒级时钟同步,而 CAN FD 仍是主流总线。当两者共存于同一系统(如 TSN 交换机连接多个 FDCAN 节点)时,需要将 FDCAN 的发送/接收时刻对齐到 TSN 全局时间。STM32H7 的 FDCAN 支持硬件触发(Trigger)功能,可基于定时器或外部信号启动传输,配合 480MHz 主频和 HRTIM 的高分辨率,能实现精确对齐。

原理剖析

1. STM32H7 的 FDCAN 硬件触发机制

FDCAN 外设(兼容 Bosch M_CAN)包含一个 Trigger 单元,允许通过以下方式触发帧传输:

  • 定时器比较匹配(如 TIM1/8 或 HRTIM)
  • 外部事件(如 EXTI 线)
  • 软件触发(立即发送)

触发信号通过 FDCAN_TTTR 寄存器配置,并映射到特定 GPIO 或内部信号。当触发发生时,FDCAN 自动从 TX 邮箱加载帧并发送,无需 CPU 干预。

2. TSN 时间同步的硬件对齐策略

TSN 节点通常有本地时钟(如 802.1AS 的 gPTP),通过 PHY 或 MAC 层时间戳获取全局时间。STM32H7 可通过以下方式对齐:

  • 将 TSN 时钟(如 ETH MAC 的 PTP 时间)映射到定时器输入捕获,生成周期脉冲。
  • 利用该脉冲作为 FDCAN 的硬件触发源,使 FDCAN 帧的起始位与 TSN 的同步帧(如 PTP Sync)对齐。

关键点:触发信号必须与 FDCAN 的位时序同步,避免因触发抖动导致偏差。STM32H7 的 FDCAN 支持在触发后延迟若干位时间(通过 TTOCN 寄存器)来微调,实现亚微秒级对齐。

配置步骤

1. 硬件准备

  • STM32H743 或 H750 开发板(主频 480MHz)
  • TSN 以太网 PHY(如 KSZ8463)
  • CAN 收发器(如 TJA1044)
  • 示波器或逻辑分析仪验证时序

2. 时钟与 GPIO 配置

使用 CubeMX 配置:

  • 系统时钟:480MHz(HSE + PLL)
  • FDCAN1:波特率 1Mbps,使用外部触发
  • TIM1:作为触发源,输出比较模式
  • GPIO:FDCAN1_TX (PB9), FDCAN1_RX (PB8),TIM1_CH1 (PA8) 用于触发输入(内部连接)

3. FDCAN 触发配置

在 FDCAN 初始化中,设置 TTOCN(Triggered Transmit Operation Control)寄存器:

  • 使能触发发送模式
  • 选择触发源为 TIM1 的 TRGO
  • 设置触发后延迟(如 0 位时间)

4. 定时器配置

TIM1 配置为 PWM 模式,频率与 TSN 同步周期一致(如 1kHz),占空比 50%。将 TRGO 连接到 FDCAN 触发输入(通过内部信号,无需外部连线)。

完整代码示例

以下代码基于 HAL 库,演示如何配置 FDCAN 硬件触发与 TIM1 同步。

// 初始化 FDCAN1 并启用硬件触发
void FDCAN_Trigger_Init(void)
{
    FDCAN_HandleTypeDef hfdcan1;
    hfdcan1.Instance = FDCAN1;
    hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1;
    hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS;
    hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
    hfdcan1.Init.AutoRetransmission = DISABLE;
    hfdcan1.Init.TransmitPause = DISABLE;
    hfdcan1.Init.ProtocolException = DISABLE;
    hfdcan1.Init.NominalPrescaler = 10;  // 480MHz / 10 = 48MHz
    hfdcan1.Init.NominalSyncJumpWidth = 1;
    hfdcan1.Init.NominalTimeSeg1 = 13;   // 采样点 75%
    hfdcan1.Init.NominalTimeSeg2 = 2;
    hfdcan1.Init.DataPrescaler = 5;
    hfdcan1.Init.DataSyncJumpWidth = 1;
    hfdcan1.Init.DataTimeSeg1 = 13;
    hfdcan1.Init.DataTimeSeg2 = 2;
    hfdcan1.Init.StdFiltersNbr = 1;
    hfdcan1.Init.ExtFiltersNbr = 0;
    hfdcan1.Init.TxEventsNbr = 1;
    hfdcan1.Init.RxFifo0ElmtsNbr = 1;
    hfdcan1.Init.RxFifo0ElmtsSize = FDCAN_DATA_BYTES_8;
    hfdcan1.Init.RxFifo1ElmtsNbr = 0;
    hfdcan1.Init.RxFifo1ElmtsSize = FDCAN_DATA_BYTES_8;
    hfdcan1.Init.TxEventsNbr = 1;
    hfdcan1.Init.TxBuffersNbr = 1;
    HAL_FDCAN_Init(&hfdcan1);

    // 配置触发发送
    FDCAN_TriggerConfigTypeDef triggerConfig;
    triggerConfig.TriggerMode = FDCAN_TRIGGER_MODE_TIMER;
    triggerConfig.TriggerSource = FDCAN_TRIGGER_SOURCE_TIM1_TRGO;
    triggerConfig.TriggerDelay = 0;  // 无延迟
    triggerConfig.TriggerPolarity = FDCAN_TRIGGER_POLARITY_RISING;
    HAL_FDCAN_ConfigTrigger(&hfdcan1, &triggerConfig);

    // 使能触发发送
    HAL_FDCAN_ActivateTrigger(&hfdcan1, FDCAN_TRIGGER_TX);

    // 配置 TX 邮箱(示例:发送 ID 0x123)
    FDCAN_TxHeaderTypeDef txHeader;
    txHeader.Identifier = 0x123;
    txHeader.IdType = FDCAN_STANDARD_ID;
    txHeader.TxFrameType = FDCAN_DATA_FRAME;
    txHeader.DataLength = FDCAN_DLC_BYTES_8;
    txHeader.BitRateSwitch = FDCAN_BRS_ON;
    txHeader.FDFormat = FDCAN_FD_CAN;
    txHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
    txHeader.MessageMarker = 0;
    uint8_t data[8] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};
    HAL_FDCAN_AddMessageToTxBuffer(&hfdcan1, &txHeader, data, 0);
}

// 初始化 TIM1 作为触发源
void TIM1_Trigger_Init(void)
{
    TIM_HandleTypeDef htim1;
    htim1.Instance = TIM1;
    htim1.Init.Prescaler = 480 - 1;  // 480MHz / 480 = 1MHz
    htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim1.Init.Period = 1000 - 1;    // 1kHz 频率
    htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim1.Init.RepetitionCounter = 0;
    htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
    HAL_TIM_Base_Init(&htim1);

    TIM_OC_InitTypeDef ocConfig;
    ocConfig.OCMode = TIM_OCMODE_PWM1;
    ocConfig.Pulse = 500;  // 50% 占空比
    ocConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
    ocConfig.OCFastMode = TIM_OCFAST_DISABLE;
    HAL_TIM_PWM_ConfigChannel(&htim1, &ocConfig, TIM_CHANNEL_1);

    // 配置 TRGO 为更新事件(或比较匹配)
    TIM_MasterConfigTypeDef masterConfig;
    masterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
    masterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    HAL_TIMEx_MasterConfigSynchronization(&htim1, &masterConfig);

    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
}

// 主函数中调用
int main(void)
{
    HAL_Init();
    SystemClock_Config();  // 配置 480MHz
    GPIO_Init();          // 配置 FDCAN 和 TIM 引脚
    FDCAN_Trigger_Init();
    TIM1_Trigger_Init();

    while (1)
    {
        // 主循环,无需干预发送,由硬件触发
    }
}

注意事项

  • 触发源选择:确保 TIM1 的 TRGO 信号已正确映射到 FDCAN 的触发输入。在 STM32H7 中,这通常通过 SYSCFG 或内部互连矩阵配置,CubeMX 可自动处理。
  • 位时间对齐:触发延迟(TriggerDelay)的单位是 FDCAN 位时间,需根据实际采样点计算。例如,若采样点在 75%,延迟 0 位意味着帧起始位与触发沿对齐,但可能需微调以补偿内部延迟。
  • 时钟同步:TSN 的 PTP 时钟与 TIM1 的同步至关重要。建议使用 ETH 的 PTP 事件触发 TIM1 的捕获,或通过外部 PLL 锁定,避免漂移。
  • 中断优先级:若使用触发中断,需设置高优先级,避免因其他中断延迟导致触发抖动。
  • 调试验证:使用示波器测量 FDCAN_TX 引脚与 TSN 同步信号,观察相位差。若偏差大于 1 位时间,调整 TriggerDelay 或 TIM1 的相位。
  • 功耗与温度:480MHz 下注意散热,且 FDCAN 触发功能在低功耗模式下可能不可用,需保持设备在运行模式。

总结

通过 STM32H7 的硬件触发机制,FDCAN 可以与 TSN 时间同步实现硬件级对齐,无需 CPU 干预,精度可达微秒级。本文提供的配置和代码可直接应用于工业网关或车载域控制器,为混合网络提供可靠的时间一致性。实际项目中,还需结合具体 TSN 协议栈(如 gPTP)进行时钟校准,以进一步优化同步性能。