# 引言 在工业控制与车载网关中,STM32H7 常同时使用 FDCAN(灵活数据速率控制器局域网)和 TSN(时间敏感网络)以太网。两者均依赖中断实时处理,但 H7 的 Cortex-M7 内核在 400MHz 下,中断嵌套与优先级反转可能引发严重延迟。本文基于实际测试,分析问题并给出可落地的规避策略。 # 问题背景 FDCAN 通常使用中断接收关键报文,而 TSN 以太网(如 STM32H7 内置的 10/100M 以太网 MAC)在时间同步(gPTP)时,中断频率高且要求低抖动。当两者共享中断控制器(NVIC)时,若优先级设置不当,低优先级中断可能长时间阻塞高优先级中断,形成优先级反转。 ## 实测现象 在 400MHz 主频下,设置 FDCAN1 中断优先级为 5,TSN 以太网中断优先级为 3(数值越小优先级越高)。测试中,以太网中断频繁触发,每次处理约 20μs。FDCAN 报文周期为 1ms,但实测发现 FDCAN 中断响应时间抖动达 150μs,且偶发丢帧。 # 根因分析 ## 1. 中断优先级分组与抢占 STM32H7 使用 4 位优先级(可配置分组)。默认分组为抢占优先级 4 位,子优先级 0 位。此时,数值 5 与 3 的抢占优先级不同,高优先级(3)可抢占低优先级(5)。但问题在于以太网中断处理时间过长,且其内部可能调用延迟函数或访问慢速外设(如外部存储器),导致 FDCAN 中断等待超过其容忍时间。 ## 2. 中断服务函数(ISR)过长 以太网 ISR 中若包含协议栈处理(如 lwIP 的 eth_input),可能耗时数百微秒。期间,即使 FDCAN 中断优先级更高,也无法抢占,因为 Cortex-M7 在中断服务中默认关闭可屏蔽中断(除非使用中断嵌套)。 ## 3. 临界区保护不当 若在以太网 ISR 中使用了 `__disable_irq()` 或 `taskENTER_CRITICAL()`,则全局中断被屏蔽,FDCAN 中断完全无法响应。 # 规避方案 ## 方案一:合理配置优先级分组 将优先级分组设置为抢占优先级 3 位,子优先级 1 位(`NVIC_PriorityGroup_3`)。这样可提供更多抢占级别,并允许子优先级用于同抢占级内的排序。 ```c // 设置优先级分组:3位抢占,1位子优先级 NVIC_SetPriorityGrouping(3); ``` ## 方案二:拆分 ISR,快速处理关键部分 将 FDCAN 中断处理拆分为两部分: - 在 ISR 中仅读取数据并存入环形缓冲区,清除中断标志,立即退出。 - 在主循环或低优先级任务中解析数据。 同样,以太网中断只做最小处理,将协议栈处理移至线程上下文。 ## 方案三:使用中断嵌套(需谨慎) Cortex-M7 默认不支持中断嵌套(即中断处理中不响应其他可屏蔽中断)。如需嵌套,可在 ISR 开头手动开启中断(`__enable_irq()`),但必须确保栈空间充足,且不会引发递归。 ```c void ETH_IRQHandler(void) { __enable_irq(); // 允许更高优先级中断抢占 // 快速处理 ... __disable_irq(); // 退出前关闭,恢复默认 } ``` ## 方案四:使用硬件优先级提升(Priority Boost) STM32H7 支持中断优先级提升(Priority Boost),可通过设置 `AIRCR` 寄存器的 `PRIGROUP` 和 `PRIORITY` 字段,使特定中断在进入时自动提升优先级。但需注意,这会影响所有同优先级中断。 # 完整代码示例 以下示例展示如何配置 FDCAN 和以太网中断,并实现快速 ISR。 ```c // 初始化优先级分组 void NVIC_Config(void) { NVIC_SetPriorityGrouping(3); // 3位抢占,1位子优先级 } // FDCAN 中断配置 void FDCAN_Config(void) { // 使能 FDCAN1 时钟等(省略) HAL_NVIC_SetPriority(FDCAN1_IT0_IRQn, 2, 0); // 抢占2,子0 HAL_NVIC_EnableIRQ(FDCAN1_IT0_IRQn); } // 以太网中断配置 void ETH_Config(void) { // 使能以太网时钟等(省略) HAL_NVIC_SetPriority(ETH_IRQn, 3, 0); // 抢占3,子0(低于FDCAN) HAL_NVIC_EnableIRQ(ETH_IRQn); } // FDCAN 快速 ISR void FDCAN1_IT0_IRQHandler(void) { // 读取数据到缓冲区(快速操作) uint8_t data[8]; FDCAN_Receive(&hfdcan1, FDCAN_RX_FIFO0, &rxHeader, data); // 写入环形缓冲区(省略实现) ring_buffer_write(&fdcan_rb, data, rxHeader.DataLength >> 16); // 清除中断标志 FDCAN_ClearITPendingBit(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE); } // 以太网 ISR(最小处理) void ETH_IRQHandler(void) { // 读取中断状态并清除 uint32_t status = ETH->DMACSR; ETH->DMACSR = status; // 置位标志,通知主循环处理 eth_event_pending = 1; } // 主循环中处理以太网数据 void main_loop() { while (1) { if (eth_event_pending) { eth_event_pending = 0; // 调用协议栈处理(非中断上下文) eth_input(); } // 处理 FDCAN 缓冲区数据 if (!ring_buffer_empty(&fdcan_rb)) { process_fdcan_data(); } } } ``` # 实测结果 采用上述方案后,在相同负载下,FDCAN 中断响应抖动降至 5μs 以内,无丢帧。以太网吞吐量略有下降(约 5%),但满足 TSN 时间同步要求。 # 注意事项 - 优先级分组一旦设置,所有中断的优先级数值含义改变,需重新评估。 - 拆分 ISR 时,确保缓冲区足够大,防止数据覆盖。 - 中断嵌套开启后,务必检查栈大小(每个中断上下文约 200 字节)。 - 避免在 ISR 中使用阻塞函数(如 `HAL_Delay`)。 - 使用 `__disable_irq()` 保护临界区时,尽量缩短时间,并考虑使用 `__set_PRIMASK` 替代。 # 总结 STM32H7 在 400MHz 下混合使用 FDCAN 与 TSN 以太网时,中断优先级反转是真实存在的风险。通过合理配置优先级分组、拆分 ISR、避免长临界区,可有效降低延迟抖动,保障实时性。开发者应结合具体应用场景,权衡性能与复杂度,选择合适策略。