You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

低优先级EXTI15_10中断死循环时高优先级EXTI4无法抢占问题

STM32F446RE 高优先级中断无法抢占低优先级中断问题排查

问题描述

参考《Mastering STM32》中的中断优先级示例代码,将EXTI15_10(对应GPIO_PIN_13)配置为抢占优先级1的低优先级中断,其ISR回调函数包含死循环;理论上抢占优先级0的高优先级EXTI4(对应GPIO_PIN_4)中断应能抢占该低优先级中断,但在NUCLEO-F446RE开发板上多次测试均未实现预期效果。

相关代码如下:

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : B1_Pin */
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_4;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC,&GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_8;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  HAL_NVIC_SetPriority(EXTI15_10_IRQn,1,0);
  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

  HAL_NVIC_SetPriority(EXTI4_IRQn,0,0);
  HAL_NVIC_EnableIRQ(EXTI4_IRQn);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

void EXTI15_10_IRQHandler(void){
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
}

void EXTI4_IRQHandler(void){
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}
/* USER CODE BEGIN 4 */

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    if (GPIO_Pin == GPIO_PIN_13) {
        blink = 1;
        while (blink) {
            HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8);
            for (volatile int i = 0; i < 100000; i++);  // Delay
        }
    }
    else{
        blink=0;
    }
}

问题排查与解决方案

1. 显式配置中断优先级分组

STM32的中断优先级依赖优先级分组配置,分组决定了抢占优先级和子优先级的位数分配。若未显式设置,HAL库默认分组可能存在不确定性,导致抢占优先级设置未生效。

在MX_GPIO_Init的USER CODE BEGIN MX_GPIO_Init_1区域添加分组配置:

HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

该配置为4位抢占优先级,0位子优先级,此时抢占优先级范围为0-15,数值越小优先级越高,完全匹配你设置的EXTI4(0)和EXTI15_10(1)的优先级规则。

2. 验证EXTI4中断触发有效性

单独测试EXTI4中断是否能正常触发:修改HAL_GPIO_EXTI_Callback,在EXTI4分支添加LED闪烁或串口输出,确认按键按下时该分支能被执行,排除硬件连接或触发边沿配置错误。

示例修改:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    if (GPIO_Pin == GPIO_PIN_13) {
        blink = 1;
        while (blink) {
            HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8);
            for (volatile int i = 0; i < 100000; i++);
        }
    }
    else if(GPIO_Pin == GPIO_PIN_4){
        blink=0;
        // 添加测试代码确认中断触发
        HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
    }
}

3. 确认中断标志位清除逻辑

虽然HAL_GPIO_EXTI_IRQHandler会自动清除中断标志位,可手动在EXTI4的IRQHandler中补充清除操作,避免因HAL库逻辑遗漏导致中断无法触发:

void EXTI4_IRQHandler(void){
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4);
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}

4. 检查全局中断状态

确保代码中未调用__disable_irq()关闭全局中断,死循环中的忙等逻辑不会影响中断响应,只要全局中断处于开启状态(默认HAL_Init会开启),高优先级中断就能抢占当前低优先级中断。

原理说明

STM32的NVIC中断抢占规则:

  • 仅当新中断的抢占优先级高于当前执行的中断时,才会触发抢占。
  • 抢占优先级数值越小,优先级越高(0为最高优先级)。
  • 优先级分组必须正确配置,否则抢占优先级的设置无法被NVIC正确识别。

完成上述配置后,按下EXTI4对应的按键,高优先级中断会抢占EXTI15_10的死循环,将blink置0,使死循环退出,恢复正常程序流程。

内容的提问来源于stack exchange,提问作者Lipin Kariappa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 01:37:34