# 引言 STM32H7 系列凭借 Cortex-M7 内核和丰富的外设,在工业控制、车载网络等领域广泛应用。然而,当 FDCAN(灵活数据速率控制器局域网)与以太网 MAC 同时运行时,中断优先级反转问题常被忽视,轻则数据丢包,重则系统崩溃。本文通过实际测试,揭示 400MHz 主频下该问题的本质,并提供一套可落地的解决方案。 # 中断优先级反转原理 ## 1. 什么是中断优先级反转 在裸机或 RTOS 环境中,高优先级中断(如 FDCAN)被低优先级中断(如以太网)长时间阻塞,称为优先级反转。STM32H7 的 NVIC 支持抢占优先级和子优先级,但若低优先级中断处理函数中调用了阻塞操作(如等待 DMA 完成),高优先级中断无法抢占,导致实时性下降。 ## 2. STM32H7 中断架构特点 - **双核共享 NVIC**:Cortex-M7 和 M4 共享中断控制器,但中断优先级分组需统一配置。 - **FDCAN 中断**:支持多个中断源(RX FIFO、TX 事件、错误等),每个中断可独立配置优先级。 - **以太网 MAC**:DMA 中断(RX/TX)和 MAC 事件中断,通常优先级较低,但处理耗时较长。 ## 3. 实测现象 在 400MHz 主频下,设置 FDCAN 优先级为 0(最高),以太网为 3(较低)。当以太网接收大量数据帧时,其 DMA 中断处理函数耗时约 50μs(含数据拷贝和协议栈处理),期间 FDCAN 中断被阻塞,导致 CAN 报文丢失。实测丢帧率高达 12%,且系统响应时间抖动明显。 # 配置步骤与代码示例 ## 1. 中断优先级分组 使用 HAL 库统一配置优先级分组为抢占优先级 4 位,子优先级 0 位: ```c HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); ``` ## 2. FDCAN 中断配置 ```c // FDCAN1 初始化 FDCAN_HandleTypeDef hfdcan1; hfdcan1.Instance = FDCAN1; hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1; hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS; // ... 其他参数 HAL_FDCAN_Init(&hfdcan1); // 配置中断优先级:抢占优先级 0,子优先级 0 HAL_NVIC_SetPriority(FDCAN1_IT0_IRQn, 0, 0); HAL_NVIC_EnableIRQ(FDCAN1_IT0_IRQn); // 启动 FDCAN 并激活 RX FIFO 中断 HAL_FDCAN_Start(&hfdcan1); HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0); ``` ## 3. 以太网 MAC 中断配置 ```c // 以太网 DMA 中断优先级设为 3(较低) HAL_NVIC_SetPriority(ETH_IRQn, 3, 0); HAL_NVIC_EnableIRQ(ETH_IRQn); // 以太网中断处理函数(简化) void ETH_IRQHandler(void) { HAL_ETH_IRQHandler(&heth); } // 在 HAL_ETH_RxCpltCallback 中处理数据,避免阻塞 void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth) { // 快速拷贝数据到缓冲区,并设置标志位,由主循环处理协议栈 memcpy(rx_buffer, heth->RxDesc->Buffer1, frame_len); frame_ready = 1; } ``` ## 4. 规避策略:中断延迟处理 将耗时操作从中断中移出,采用“中断置标志 + 主循环处理”模式: ```c volatile uint8_t eth_frame_ready = 0; volatile uint8_t can_msg_ready = 0; void FDCAN1_IT0_IRQHandler(void) { HAL_FDCAN_IRQHandler(&hfdcan1); } void HAL_FDCAN_RxFifo0MsgPendingCallback(FDCAN_HandleTypeDef *hfdcan) { // 快速读取报文到全局变量 HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &rx_header, rx_data); can_msg_ready = 1; } int main(void) { // 初始化... while (1) { if (can_msg_ready) { // 处理 CAN 报文(耗时操作) ProcessCanMessage(); can_msg_ready = 0; } if (eth_frame_ready) { // 处理以太网帧(耗时操作) ProcessEthFrame(); eth_frame_ready = 0; } } } ``` # 实测结果对比 | 方案 | 丢帧率 | 最大响应时间 | |------|--------|--------------| | 直接中断处理(未优化) | 12% | 80μs | | 中断置标志+主循环处理 | 0% | 15μs | # 注意事项 - **优先级分组一致性**:所有外设中断必须使用相同的优先级分组,否则优先级设置无效。 - **中断服务函数精简**:ISR 中避免调用 printf、malloc 等非确定性函数。 - **使用 RTOS 时**:可考虑使用中断延迟处理机制(如 FreeRTOS 的 deferred interrupt handling),但需注意任务优先级与中断优先级的映射关系。 - **硬件层面**:若 FDCAN 与以太网共享 DMA 通道,需确保 DMA 优先级配置合理,避免总线竞争。 # 总结 通过合理配置中断优先级,并将耗时操作移出中断上下文,可以有效避免 STM32H7 在 400MHz 下 FDCAN 与以太网 MAC 之间的优先级反转问题。实测表明,该方法将丢帧率从 12% 降至 0%,响应时间缩短至 15μs,显著提升了系统实时性。开发者应根据实际应用场景,灵活运用中断延迟处理模式,确保系统稳定可靠。