# 引言 STM32H7 系列(如 STM32H743/750)凭借 Cortex-M7 内核的 480MHz 主频,以及内置的 FDCAN 控制器和 TSN 以太网 MAC,成为工业 IoT 和车载网关的理想平台。然而,当 FDCAN(CAN FD)与 TSN(时间敏感网络)以太网需要协同工作时,实时调度成为关键挑战:如何确保不同协议的数据在共享 CPU 资源下满足各自的截止时间?本文将从硬件机制、时间同步和软件调度三个层面展开。 # 硬件资源与实时性基础 ## 1. FDCAN 特性 - **数据速率**:最高 8Mbps(数据段),支持 64 字节数据场。 - **硬件 FIFO**:每个 FDCAN 实例有 3 个 TX 邮箱和 2 个 RX FIFO,减少 CPU 中断负担。 - **时间戳**:内置 16 位定时器,可配合外部时间同步。 ## 2. TSN 以太网特性 - **IEEE 802.1Qbv**:时间感知整形器(TAS),通过门控列表控制队列发送。 - **IEEE 802.1AS**:gPTP 精确时间协议,实现微秒级同步。 - **多队列**:STM32H7 的 MAC 支持 8 个 TX 队列,可映射不同优先级。 ## 3. 协同难点 - **资源竞争**:CPU 需同时处理 FDCAN 中断和以太网 DMA 中断。 - **时间基准**:FDCAN 和 TSN 需要统一的时间源,否则调度失序。 - **优先级反转**:低优先级任务可能阻塞高优先级实时帧。 # 时间同步机制 ## 1. 统一时间基准 利用 STM32H7 的 TIM2 作为全局时间主时钟(64 位),通过以下方式同步: - **FDCAN**:将 TIM2 映射到 FDCAN 的 Timestamp Counter(需配置为外部时钟模式)。 - **TSN**:使用 802.1AS 的 gPTP 同步,将 TIM2 作为本地时钟源。 配置示例(HAL 库): ```c // 初始化 TIM2 为 1MHz 计数 TIM_HandleTypeDef htim2; htim2.Instance = TIM2; htim2.Init.Prescaler = 480-1; // 480MHz/480 = 1MHz htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 0xFFFFFFFF; // 32 位 HAL_TIM_Base_Init(&htim2); HAL_TIM_Base_Start(&htim2); // FDCAN 时间戳配置(使用外部时钟) FDCAN_HandleTypeDef hfdcan1; hfdcan1.Init.TimestampConfig = FDCAN_TIMESTAMP_INTERNAL; // 改为外部 hfdcan1.Init.TimestampPrescaler = 1; // 注意:需将 TIM2 输出连接到 FDCAN 的 Timestamp 引脚(如 PA8) ``` ## 2. gPTP 同步流程 - 启用以太网 MAC 的 IEEE 1588 功能,配置 PTP 时钟源为 TIM2。 - 通过 gPTP 协议从主时钟获取偏移,并调整 TIM2 的计数值(使用 HAL_TIM_OC_ConfigChannel 产生校正脉冲)。 # 实时调度策略 ## 1. 优先级映射 - **FDCAN 高优先级帧**(如紧急控制消息)映射到 TSN 的队列 7(最高优先级)。 - **FDCAN 普通帧**映射到队列 3。 - **TSN 音视频流**映射到队列 0-2,利用 Qbv 门控。 ## 2. 中断与轮询结合 - **FDCAN 中断**:仅对高优先级消息启用 FIFO 满中断,普通消息使用轮询(在空闲时隙)。 - **以太网中断**:利用 DMA 完成中断,但将处理函数放入低优先级任务。 ## 3. 时间片轮转 在 RTOS(如 FreeRTOS)中,为 FDCAN 和 TSN 分配独立任务,并设置优先级: - FDCAN_Task:优先级 5,周期 1ms。 - TSN_Task:优先级 3,周期 5ms。 - 使用消息队列传递数据,避免共享内存冲突。 # 完整代码示例 以下示例展示如何配置 FDCAN 发送高优先级消息,并通过 TSN 以太网发送同步帧(基于 STM32H743 和 HAL 库): ```c // 初始化 FDCAN1(速率 5Mbps,数据段 8Mbps) void FDCAN1_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 = ENABLE; hfdcan1.Init.TransmitPause = DISABLE; hfdcan1.Init.ProtocolException = DISABLE; hfdcan1.Init.NominalPrescaler = 4; // 80MHz/4/2 = 10MHz 仲裁段 hfdcan1.Init.NominalSyncJumpWidth = 1; hfdcan1.Init.NominalTimeSeg1 = 13; hfdcan1.Init.NominalTimeSeg2 = 2; hfdcan1.Init.DataPrescaler = 2; // 数据段 8Mbps hfdcan1.Init.DataSyncJumpWidth = 1; hfdcan1.Init.DataTimeSeg1 = 9; hfdcan1.Init.DataTimeSeg2 = 2; hfdcan1.Init.TimestampConfig = FDCAN_TIMESTAMP_EXTERNAL; hfdcan1.Init.TimestampPrescaler = 1; HAL_FDCAN_Init(&hfdcan1); // 配置过滤器(接收所有) FDCAN_FilterTypeDef sFilterConfig; sFilterConfig.IdType = FDCAN_STANDARD_ID; sFilterConfig.FilterIndex = 0; sFilterConfig.FilterType = FDCAN_FILTER_MASK; sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; sFilterConfig.FilterID1 = 0; sFilterConfig.FilterID2 = 0; HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig); } // 发送 FDCAN 高优先级消息 void FDCAN_SendHighPriority(uint32_t id, uint8_t* data, uint8_t len) { FDCAN_TxHeaderTypeDef txHeader; txHeader.Identifier = id; txHeader.IdType = FDCAN_STANDARD_ID; txHeader.TxFrameType = FDCAN_DATA_FRAME; txHeader.DataLength = len << 16; // 转换为 DLC txHeader.FDFormat = FDCAN_FD_CAN; txHeader.BitRateSwitch = ENABLE; txHeader.TxEventFifoControl = DISABLE; txHeader.MessageMarker = 0; // 使用 TX 邮箱 0(最高优先级) HAL_FDCAN_AddMessageToTxBuffer(&hfdcan1, &txHeader, data, FDCAN_TX_MAILBOX0); } // TSN 以太网发送(使用队列 7) void TSN_SendFrame(uint8_t* frame, uint16_t len) { ETH_TxPacketConfig txConfig; txConfig.Attributes = ETH_TX_PACKETS_FEATURES_TSO | ETH_TX_PACKETS_FEATURES_VLAN; txConfig.VlanTag = 0x8100; // 802.1Q VLAN 标签,PCP=7 映射到队列 7 txConfig.Payload = frame; txConfig.Length = len; txConfig.Timestamp = 0; // 使用硬件时间戳 HAL_ETH_Transmit(&heth, &txConfig, 1); } // 主循环调度 void RealTimeScheduler(void) { uint32_t tick = HAL_GetTick(); if (tick % 1 == 0) { // 每 1ms 处理 FDCAN FDCAN_SendHighPriority(0x100, (uint8_t*)"Hi", 2); } if (tick % 5 == 0) { // 每 5ms 发送 TSN 同步帧 uint8_t frame[64]; // 填充帧内容(略) TSN_SendFrame(frame, 64); } } ``` # 注意事项 - **时钟配置**:确保 FDCAN 和以太网使用不同的 PLL 输出,避免频率耦合。 - **内存屏障**:在共享数据(如全局时间戳)访问时,使用 `__DMB()` 指令。 - **中断优先级**:将 FDCAN 中断优先级设为高于以太网,但低于系统节拍。 - **调试技巧**:使用逻辑分析仪同时抓取 FDCAN 和以太网信号,验证时间同步精度。 - **功耗考虑**:480MHz 下全速运行功耗较高,可在空闲时降低 CPU 频率(通过 PLL 切换)。 # 总结 通过统一时间基准、优先级映射和 RTOS 任务调度,STM32H7 能够有效协同 FDCAN 与 TSN 以太网,满足实时性要求。实际项目中,需根据具体业务调整时间片和队列配置,并利用硬件特性减少 CPU 负载。希望本文能为您的嵌入式实时系统设计提供参考。