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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 15:11:00