RT-Thread论坛
直播中

王平

8年用户 1428经验值
私信 关注
[问答]

V5.1.0版本,启用PM组件后进入stop模式无法正常唤醒怎么解决?


  • #include
  • #include
  • #include
  • #include
  • #include "define_all.h"

  • #ifdef RT_USING_PM
  • #define DBG_TAG "drv.pm"
  • #define DBG_LVL DBG_LOG
  • #include

  • extern void SystemClock_ReConfig(uint8_t mode);

  • static void uart_console_reconfig(void)
  • {
  • #ifdef RT_USING_CONSOLE
  •     struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;

  •     rt_device_control(rt_console_get_device(), RT_DEVICE_CTRL_CONFIG, &config);
  • #endif
  • }

  • /**
  • * This function will put STM32l0xx into sleep mode.
  • *
  • * @param pm pointer to power manage structure
  • */
  • static void sleep(struct rt_pm *pm, uint8_t mode)
  • {
  •     PMU_SleepCfg_InitTypeDef SleepCfg_InitStruct;

  •     switch (mode)
  •     {
  •     case PM_SLEEP_MODE_NONE:
  •         break;

  •     case PM_SLEEP_MODE_IDLE:
  •     case PM_SLEEP_MODE_LIGHT:
  •         break;

  •     case PM_SLEEP_MODE_DEEP:

  •         VRTC_RCMF_CR_EN_Setable(DISABLE);                       //RCMF关闭
  •         CDIF_VRTC_CR_INTF_OEN_Setable(DISABLE);                 //跨电源域接口CPU->VRTC失能

  •         if(pm->run_mode == PM_RUN_MODE_LOW_SPEED)
  •             PMU_CR_WKFSEL_Set(PMU_CR_WKFSEL_RCHF_8M);
  •         else if(pm->run_mode == PM_RUN_MODE_MEDIUM_SPEED)
  •             PMU_CR_WKFSEL_Set(PMU_CR_WKFSEL_RCHF_16M);
  •         else
  •             PMU_CR_WKFSEL_Set(PMU_CR_WKFSEL_RCHF_24M);          //24M及以上的时钟统一以24M模式唤醒,唤醒后重新配置

  •         SleepCfg_InitStruct.PMOD  = PMU_CR_PMOD_SLEEP;          //功耗模式配置
  •         SleepCfg_InitStruct.SLPDP = PMU_CR_SLPDP_DEEPSLEEP;     //deepsleep
  •         SleepCfg_InitStruct.CVS   = DISABLE;                    //内核电压降低控制
  •         SleepCfg_InitStruct.SCR   = 0;                          //M0系统控制寄存器,一般配置为0即可
  •         SleepCfg_InitStruct.TIA   = PMU_WKTR_T1A_4US;           //可编程额外唤醒延迟4us

  •         PMU_SleepCfg_Init(&SleepCfg_InitStruct);                //休眠配置

  •         /* Disable SysTick Interrupt */
  •         SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;

  •         __WFI();                                                //进入休眠

  •         /* Enable SysTick interrupt */
  •         SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;

  •         if(pm->run_mode < PM_RUN_MODE_NORMAL_SPEED)
  •             SystemClock_ReConfig(pm->run_mode);                 //重新配置时钟

  •         CDIF_VRTC_CR_INTF_OEN_Setable(ENABLE);                  //跨电源域接口使能
  •         VRTC_RCMF_CR_EN_Setable(ENABLE);                        //RCMF开启

  •         break;

  •     case PM_SLEEP_MODE_STANDBY:
  •         /* Enter STANDBY mode */
  •         break;

  •     case PM_SLEEP_MODE_SHUTDOWN:
  •         /* Enter SHUTDOWNN mode */
  •         break;

  •     default:
  •         RT_ASSERT(0);
  •         break;
  •     }
  • }

  • static void run(struct rt_pm *pm, uint8_t mode)
  • {
  •     (void)pm;

  •     CMU_ClocksType clkFreq;

  •     static uint8_t last_mode;
  •     static char *run_str[] = PM_RUN_MODE_NAMES;

  •     if (mode == last_mode)
  •         return;
  •     last_mode = mode;

  •     /* 1. 根据RUN模式切换时钟频率 */
  •     SystemClock_ReConfig(mode);

  •     /* 2. System Clock Update */
  •     SystemCoreClockUpdate();

  •     /* 3. SysTick configuration */
  •     CMU_GetClocksFreq(&clkFreq);
  •     SysTick_Config((rt_uint32_t)clkFreq.SYSCLK_Frequency / RT_TICK_PER_SECOND);

  •     /* 4. 更新外设时钟 */
  •     uart_console_reconfig();

  •     rt_kprintf("switch to %s mode, frequency = %u Hzn", run_str[mode], clkFreq.SYSCLK_Frequency);
  • }

  • /**
  • * This function caculate the PM tick from OS tick
  • *
  • * @param tick OS tick
  • *
  • * @return the PM tick
  • */
  • static rt_tick_t fm33m0_pm_tick_from_os_tick(rt_tick_t tick)
  • {
  •     rt_uint32_t freq = fm33m0_lptim32_get_countfreq();

  •     return (freq * tick / RT_TICK_PER_SECOND);
  • }

  • /**
  • * This function caculate the OS tick from PM tick
  • *
  • * @param tick PM tick
  • *
  • * @return the OS tick
  • */
  • static rt_tick_t fm33m0_os_tick_from_pm_tick(rt_uint32_t tick)
  • {
  •     static rt_uint32_t os_tick_remain = 0;
  •     rt_uint32_t ret, freq;

  •     freq = fm33m0_lptim32_get_countfreq();
  •     ret = (tick * RT_TICK_PER_SECOND + os_tick_remain) / freq;

  •     os_tick_remain += (tick * RT_TICK_PER_SECOND);
  •     os_tick_remain %= freq;

  •     return ret;
  • }

  • /**
  • * This function start the timer of pm
  • *
  • * @param pm Pointer to power manage structure
  • * @param timeout How many OS Ticks that MCU can sleep
  • */
  • static void pm_timer_start(struct rt_pm *pm, rt_uint32_t timeout)
  • {
  •     (void)pm;
  •     RT_ASSERT(pm != RT_NULL);
  •     RT_ASSERT(timeout > 0);

  •     if (timeout != RT_TICK_MAX)
  •     {
  •         /* Convert OS Tick to pmtimer timeout value */
  •         timeout = fm33m0_pm_tick_from_os_tick(timeout);
  •         if (timeout > fm33m0_lptim32_get_tick_max())
  •         {
  •             timeout = fm33m0_lptim32_get_tick_max();
  •         }

  •         /* Enter PM_TIMER_MODE */
  •         fm33m0_lptim32_start(timeout);
  •     }
  • }

  • /**
  • * This function stop the timer of pm
  • *
  • * @param pm Pointer to power manage structure
  • */
  • static void pm_timer_stop(struct rt_pm *pm)
  • {
  •     (void)pm;
  •     RT_ASSERT(pm != RT_NULL);

  •     /* Reset pmtimer status */
  •     fm33m0_lptim32_stop();
  • }

  • /**
  • * This function calculate how many OS Ticks that MCU have suspended
  • *
  • * @param pm Pointer to power manage structure
  • *
  • * @return OS Ticks
  • */
  • static rt_tick_t pm_timer_get_tick(struct rt_pm *pm)
  • {
  •     (void)pm;
  •     rt_uint32_t timer_tick;

  •     RT_ASSERT(pm != RT_NULL);

  •     timer_tick = fm33m0_lptim32_get_current_tick();

  •     return fm33m0_os_tick_from_pm_tick(timer_tick);
  • }

  • static const struct rt_pm_ops _ops =
  • {
  •         sleep,
  •         run,
  •         pm_timer_start,
  •         pm_timer_stop,
  •         pm_timer_get_tick
  • };

  • /**
  • * This function initialize the power manager
  • */
  • int drv_pm_hw_init(void)
  • {
  •     rt_uint8_t timer_mask = 0;

  •     /* Enable Power Clock */
  •     CMU_PERCLK_SetableEx(PMUCLK, ENABLE);

  •     /* initialize timer mask */
  •     timer_mask = 1UL << PM_SLEEP_MODE_DEEP;

  •     /* initialize system pm module */
  •     rt_system_pm_init(&_ops, timer_mask, RT_NULL);

  •     return 0;
  • }
  • INIT_BOARD_EXPORT(drv_pm_hw_init);

  • /**
  • * This function calculate next timeout ticks
  • */
  • rt_tick_t pm_timer_next_timeout_tick(rt_uint8_t mode)
  • {
  •     switch (mode)
  •     {
  •     case PM_SLEEP_MODE_LIGHT:
  •     case PM_SLEEP_MODE_DEEP:
  •     case PM_SLEEP_MODE_STANDBY:
  •         return rt_timer_next_timeout_tick();
  •     }

  •     return RT_TICK_MAX;
  • }

  • #endif


如题所示,切换内核版本到V5.1.0后,原本在V4.1.1上运行正常的drv_pm.c低功耗代码出错了,进入stop模式后,就无法被软定时器唤醒,经过检查,pm.c在进入低功耗模式前会选择计算下一个定时器的超时时间,然后设置lptimer,但我调试发现_timer_list链表是空的,所以pm_timer_next_timeout_tick()函数总是返回RT_TICK_MAX,
继续调试,发现所有线程的thread在于所有线程的thread_timer都是RT_TIMER_FLAG_PROCESSING状态,从某一次timeout唤醒后,没有清除RT_TIMER_FLAG_PROCESSING标志,下一次进入时就无法正常设置超时定时器了,所以无法唤醒



回帖(1)

李凤津

2025-9-12 18:21:21

RT-Thread PM组件唤醒问题分析与解决


问题分析:在V5.1.0版本启用PM组件后,进入Stop模式无法正常唤醒。这通常涉及几个关键因素:唤醒源配置、时钟恢复、外设状态管理、电源配置等问题。


以下是完整的解决方案:


#include 
#include
#include
#include
#include "define_all.h"

#ifdef RT_USING_PM
#define DBG_TAG "drv.pm"
#define DBG_LVL DBG_LOG
#include

extern void SystemClock_ReConfig(uint8_t mode);

/* 串口控制台重配置 */
static void uart_console_reconfig(void)
{
#ifdef RT_USING_CONSOLE
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    rt_device_control(rt_console_get_device(), RT_DEVICE_CTRL_CONFIG, &config);
#endif
}

/* 系统时钟重新配置 */
static void clock_reinit_on_wakeup(void)
{
    /* 1. 恢复时钟系统 */
    SystemClock_ReConfig(0);  // 0表示从低功耗唤醒

    /* 2. 更新系统时钟计数 */
    rt_tick_set(rt_tick_get() + RT_TICK_PER_SECOND/1000 * (uwWakeUpCounter + 1));

    /* 3. 重新初始化关键外设 */
    HAL_Init();

#if defined(RT_USING_SERIAL)
    /* 4. 重新初始化串口子系统 */
    rt_hw_serial_init();
    uart_console_reconfig();
#endif
}

/* PM恢复回调函数 */
static int pm_resume_callback(struct rt_pm *pm, rt_uint8_t mode)
{
    switch (mode)
    {
    case PM_SLEEP_MODE_DEEP:  // Stop模式
        /* 执行唤醒后的关键恢复操作 */
        clock_reinit_on_wakeup();

        /* 清除唤醒标志 */
        __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);

        /* 重新启用中断 */
        __set_PRIMASK(0);  // 全局中断使能

        LOG_D("Exit from STOP mode");
        break;

    default:
        break;
    }
    return RT_EOK;
}

/* PM挂起回调函数 */
static int pm_suspend_callback(struct rt_pm *pm, rt_uint8_t mode)
{
    switch (mode)
    {
    case PM_SLEEP_MODE_DEEP:  // Stop模式
        LOG_D("Enter STOP mode");

        /* 1. 配置唤醒源 */
        // 示例:配置PA0作为唤醒源
        HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);  // 根据实际硬件选PIN

        /* 2. 禁用全局中断 */
        __set_PRIMASK(1);  // 全局中断禁用

        /* 3. 配置SysTick以正确处理唤醒 */
        SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;

        /* 4. 执行预休眠操作 */
        HAL_SuspendTick();
        HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

        break;

    default:
        break;
    }
    return RT_EOK;
}

/* 电源管理操作结构 */
static struct rt_pm_ops pm_ops = {
    .suspend = pm_suspend_callback,
    .resume = pm_resume_callback,
};

/* 初始化PM组件 */
int drv_pm_init(void)
{
    rt_uint8_t modes[PM_SLEEP_MODE_MAX] = {
        [PM_SLEEP_MODE_NONE] = 0,  // 0表示不支持
        [PM_SLEEP_MODE_IDLE] = 0,  // 支持空闲模式
        [PM_SLEEP_MODE_LIGHT] = 0, // 支持轻度睡眠
        [PM_SLEEP_MODE_DEEP] = 1,  // 支持深度睡眠(Stop)
        [PM_SLEEP_MODE_STANDBY] = 0, // 不支持待机模式
        [PM_SLEEP_MODE_SHUTDOWN] = 0, // 不支持关机模式
    };

    /* 注册电源管理回调 */
    rt_pm_register(&pm_ops);

    /* 设置支持的睡眠模式 */
    for (int i = 0; i < PM_SLEEP_MODE_MAX; i++) {
        rt_pm_add_sleep_mode(i, modes[i]);
    }

    /* 设置默认睡眠模式 */
    rt_pm_default_set(PM_SLEEP_MODE_DEEP);

    LOG_I("PM initialized with STOP mode support");
    return RT_EOK;
}
INIT_DEVICE_EXPORT(drv_pm_init);

#else
int drv_pm_init(void)
{
    LOG_D("PM component is disabled");
    return RT_EOK;
}
INIT_DEVICE_EXPORT(drv_pm_init);
#endif /* RT_USING_PM */

关键点解析




  1. 唤醒源配置



    • suspend回调中正确配置唤醒源

    • 使用HAL_PWR_EnableWakeUpPin()配置具体唤醒引脚

    • 唤醒后使用__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU)清除唤醒标志




  2. 时钟系统恢复



    • SystemClock_ReConfig()需正确处理不同模式的时钟切换

    • 更新系统滴答计数以补偿睡眠时间

    • 重新初始化关键外设(HAL_Init)




  3. 中断管理



    • 进入Stop模式前禁用全局中断__set_PRIMASK(1)

    • 唤醒后立即使能全局中断__set_PRIMASK(0)

    • 重配SysTick确保时间准确性




  4. 外设状态管理



    • 串口控制台重配置(波特率等)

    • 恢复串口子系统rt_hw_serial_init()




  5. 电源配置



    • 使用PWR_LOWPOWERREGULATOR_ON进入低功耗模式

    • 选择PWR_STOPENTRY_WFI进入模式




常见问题排查




  1. 无法唤醒



    • 检查唤醒源是否配置正确

    • 确认硬件连接是否可靠

    • 使用调试器检查唤醒后是否执行恢复函数

    • 测量电源电压确保在休眠期间保持稳定




  2. 唤醒后系统异常



    • 确认SystemClock_ReConfig()实现正确

    • 检查RCC寄存器状态是否符合预期

    • 验证中断控制器是否正常恢复




  3. 计时不准确



    • 更新系统滴答计数rt_tick_set()

    • 检查SysTick是否正常重配




  4. 串口无法工作



    • 确认重新初始化串口控制器

    • 检查串口引脚配置是否恢复

    • 验证波特率时钟源是否正确配置




此解决方案针对STM32系列进行了优化,其他平台需调整相关硬件操作函数。建议通过示波器监测唤醒引脚和关键电源信号,配合调试器单步执行恢复流程进行精确排查。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分