STM32F4开发板FreeRTOS下PB9中断唤醒睡眠模式故障求助
STM32F4 FreeRTOS睡眠模式无法维持,触发后立即唤醒问题
问题背景
尝试让STM32F4开发板在FreeRTOS环境下进入睡眠模式,通过PB9引脚中断唤醒,但设备进入睡眠后立刻唤醒,无法维持长时间睡眠。已启用FreeRTOS tickless模式,相关代码如下:
FreeRTOSConfig.h 配置
#define configUSE_TICKLESS_IDLE 1 #define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 100 #define configTICK_RATE_HZ ((TickType_t)1000)
可正常工作的EXTI9_5中断处理函数
void EXTI9_5_IRQHandler(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; uint32_t current_time = HAL_GetTick(); if (__HAL_GPIO_EXTI_GET_IT(STAT_ALIM_GPIO_PIN) != RESET) { __HAL_GPIO_EXTI_CLEAR_IT(STAT_ALIM_GPIO_PIN); if (current_time - last_interrupt_time > 50) { last_interrupt_time = current_time; if (HAL_GPIO_ReadPin(GPIOB, STAT_ALIM_GPIO_PIN) == GPIO_PIN_RESET) { vTaskNotifyGiveFromISR(T_handleStatAlimInterrupt, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } } }
睡眠处理任务代码
void User_handleStatAlimInterrupt(__unused void const *argument) { while (1) { ulTaskNotifyTake(pdTRUE, portMAX_DELAY); if(HAL_GPIO_ReadPin (STAT_ALIM_GPIO_PORT, STAT_ALIM_GPIO_PIN)) { USER_LOG_INFO("here"); vTaskSuspend(task1); vTaskSuspend(task2); __HAL_RCC_PWR_CLK_ENABLE(); HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); USER_LOG_INFO("wake"); vTaskResume(task1); vTaskResume(task2); }else{ USER_LOG_INFO("stat alim 0"); } } }
重定义的vPortSuppressTicksAndSleep函数
void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime) { __disable_irq(); __DSB(); __ISB(); if (eTaskConfirmSleepModeStatus() == eAbortSleep) { __enable_irq(); } else { SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; __enable_irq(); } }
当前问题
设备输出“here”后立刻输出“wake”,无法维持睡眠模式。
问题分析与解决方案
1. 任务内手动睡眠与tickless模式冲突
你在User_handleStatAlimInterrupt任务中直接调用HAL_PWR_EnterSLEEPMode,但同时启用了FreeRTOS的tickless模式,两者会产生冲突:
- tickless模式下,空闲任务会自动调用
vPortSuppressTicksAndSleep管理睡眠逻辑 - 手动调用睡眠时,系统内核状态未完全进入空闲就绪状态,可能存在未屏蔽的中断源直接唤醒系统
修复:
移除任务内的手动睡眠调用,改为通过挂起任务让系统进入空闲状态,由tickless模式触发睡眠:
void User_handleStatAlimInterrupt(__unused void const *argument) { while (1) { ulTaskNotifyTake(pdTRUE, portMAX_DELAY); if(HAL_GPIO_ReadPin (STAT_ALIM_GPIO_PORT, STAT_ALIM_GPIO_PIN)) { USER_LOG_INFO("here"); vTaskSuspend(task1); vTaskSuspend(task2); // 让空闲任务触发tickless睡眠 USER_LOG_INFO("entering idle state for sleep"); }else{ USER_LOG_INFO("stat alim 0"); vTaskResume(task1); vTaskResume(task2); } } }
2. 完善vPortSuppressTicksAndSleep函数
当前的vPortSuppressTicksAndSleep缺少睡眠时长管理和唤醒后的tick计数更新,且未确保唤醒源有效性:
- 未配置外部中断优先级高于SysTick(若SysTick优先级更高,会定时唤醒系统)
- 唤醒后未同步FreeRTOS系统tick计数
修复后的函数:
void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime) { eSleepModeStatus eSleepStatus; __disable_irq(); __DSB(); __ISB(); // 确认系统是否可以进入睡眠 eSleepStatus = eTaskConfirmSleepModeStatus(); if(eSleepStatus == eAbortSleep) { __enable_irq(); return; } // 关闭SysTick避免定时唤醒 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; SysTick->VAL = 0; // 确保PWR时钟已使能 __HAL_RCC_PWR_CLK_ENABLE(); // 进入睡眠模式,等待外部中断唤醒 HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); // 唤醒后重新启用SysTick SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; __enable_irq(); }
3. 外部中断配置检查
确保PB9的EXTI中断配置满足要求:
- 触发方式设置为目标边沿(上升/下降/双边沿)
- 中断优先级高于SysTick(SysTick默认优先级最低,需确保无其他高优先级中断抢占)
- GPIO配置为输入模式(带上下拉,匹配触发边沿需求)
4. 唤醒后任务恢复逻辑
在EXTI中断中直接恢复挂起的任务,确保系统唤醒后立即恢复运行:
void EXTI9_5_IRQHandler(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; uint32_t current_time = HAL_GetTick(); if (__HAL_GPIO_EXTI_GET_IT(STAT_ALIM_GPIO_PIN) != RESET) { __HAL_GPIO_EXTI_CLEAR_IT(STAT_ALIM_GPIO_PIN); if (current_time - last_interrupt_time > 50) { last_interrupt_time = current_time; if (HAL_GPIO_ReadPin(GPIOB, STAT_ALIM_GPIO_PIN) == GPIO_PIN_RESET) { vTaskNotifyGiveFromISR(T_handleStatAlimInterrupt, &xHigherPriorityTaskWoken); // 唤醒时恢复任务 if(eTaskGetState(task1) == eSuspended) vTaskResumeFromISR(task1); if(eTaskGetState(task2) == eSuspended) vTaskResumeFromISR(task2); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } } }
内容的提问来源于stack exchange,提问作者simon
相关产品推荐
相关产品推荐

