STM32H7 在 400MHz 下 FDCAN 与 TSN 以太网共存时的中断优先级反转实测与规避
👁 2 阅读 · 2026-08-27 · 嵌入式
在 STM32H7 以 400MHz 运行时,FDCAN 与 TSN 以太网的中断处理存在优先级反转风险,导致实时性下降甚至数据丢失。本文通过实测分析中断嵌套与调度延迟,揭示根因,并提供基于硬件优先级分组、中断服务函数拆分及关键区保护的规避方案,附完整代码示例,帮助开发者构建高可靠混合网络系统。
# 引言
在工业控制与车载网关中,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、避免长临界区,可有效降低延迟抖动,保障实时性。开发者应结合具体应用场景,权衡性能与复杂度,选择合适策略。