在老的rt-thread 版本系统pm组件调试ok,后来使用4.1.1版本时发现进入低功耗后无法正常唤醒,问题解决路径如下
硬件信息:cpu STM32L431CCT6
新建系统打开pm组件后也没有drv_pm.c和drv_lptim.c自动添加,需要到系统目下找到并复制到driver目录下
C:\RT-ThreadStudio\repo\Extract\RT-Thread_Source_Code\RT-Thread\4.1.1\bsp\stm32\libraries\HAL_Drivers
将drv_lptim.h放到drivers/include下
drv_pm.c需要做如下改动
1.添加头文件 #include <drivers/pm.h>
2.sleep函数 case PM_SLEEP_MODE_DEEP:下时钟设定改为系统初始化时的SystemClock_Config();
3.run函数不同速度下的时钟配置改为系统初始化时的SystemClock_Config();,此处可不修改,因为系统运行时一般不调整系统时钟速度,有需要的调速功能根据速度修改主频即可
代码如下
/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2019-05-06 Zero-Free first version */ #include <board.h> #include <drv_lptim.h> #include <drivers/pm.h> static void uart_console_reconfig(void) { struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; rt_device_control(rt_console_get_device(), RT_DEVICE_CTRL_CONFIG, &config); } /** * This function will put STM32L4xx into sleep mode. * * @param pm pointer to power manage structure */ static void sleep(struct rt_pm *pm, uint8_t mode) { switch (mode) { case PM_SLEEP_MODE_NONE: break; case PM_SLEEP_MODE_IDLE: // __WFI(); break; case PM_SLEEP_MODE_LIGHT: if (pm->run_mode == PM_RUN_MODE_LOW_SPEED) { /* Enter LP SLEEP Mode, Enable low-power regulator */ HAL_PWR_EnterSLEEPMode(PWR_LOWPOWERREGULATOR_ON, PWR_SLEEPENTRY_WFI); } else { /* Enter SLEEP Mode, Main regulator is ON */ HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); } break; case PM_SLEEP_MODE_DEEP: /* Enter STOP 2 mode */ HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI); /* Re-configure the system clock */ SystemClock_Config(); break; case PM_SLEEP_MODE_STANDBY: /* Enter STANDBY mode */ HAL_PWR_EnterSTANDBYMode(); break; case PM_SLEEP_MODE_SHUTDOWN: /* Enter SHUTDOWNN mode */ HAL_PWREx_EnterSHUTDOWNMode(); break; default: RT_ASSERT(0); break; } } static uint8_t run_speed[PM_RUN_MODE_MAX][2] = { {80, 0}, {80, 1}, {24, 2}, {2, 3}, }; static void run(struct rt_pm *pm, uint8_t mode) { static uint8_t last_mode; static char *run_str[] = PM_RUN_MODE_NAMES; if (mode == last_mode) return; last_mode = mode; /* 1. 设置 MSI 作为 SYSCLK 时钟源,以修改 PLL */ SystemClock_MSI_ON(); /* 2. 根据RUN模式切换时钟频率(HSI) */ switch (mode) { case PM_RUN_MODE_HIGH_SPEED: case PM_RUN_MODE_NORMAL_SPEED: HAL_PWREx_DisableLowPowerRunMode(); //SystemClock_80M(); SystemClock_Config(); /* Configure the main internal regulator output voltage (Range1 by default)*/ HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); break; case PM_RUN_MODE_MEDIUM_SPEED: HAL_PWREx_DisableLowPowerRunMode(); SystemClock_Config(); /* Configure the main internal regulator output voltage */ HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE2); break; case PM_RUN_MODE_LOW_SPEED: //SystemClock_2M(); SystemClock_Config(); /* Enter LP RUN mode */ HAL_PWREx_EnableLowPowerRunMode(); break; default: break; } /* 3. 关闭 MSI 时钟 */ // SystemClock_MSI_OFF(); /* 4. 更新外设时钟 */ uart_console_reconfig(); /* Re-Configure the Systick time */ HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / RT_TICK_PER_SECOND); /* Re-Configure the Systick */ HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); rt_kprintf("switch to %s mode, frequency = %d MHz\n", run_str[mode], run_speed[mode][0]); } /** * This function caculate the PM tick from OS tick * * @param tick OS tick * * @return the PM tick */ static rt_tick_t stm32l4_pm_tick_from_os_tick(rt_tick_t tick) { rt_uint32_t freq = stm32l4_lptim_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 stm32l4_os_tick_from_pm_tick(rt_uint32_t tick) { static rt_uint32_t os_tick_remain = 0; rt_uint32_t ret, freq; freq = stm32l4_lptim_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) { RT_ASSERT(pm != RT_NULL); RT_ASSERT(timeout > 0); if (timeout != RT_TICK_MAX) { /* Convert OS Tick to pmtimer timeout value */ timeout = stm32l4_pm_tick_from_os_tick(timeout); if (timeout > stm32l4_lptim_get_tick_max()) { timeout = stm32l4_lptim_get_tick_max(); } /* Enter PM_TIMER_MODE */ stm32l4_lptim_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) { RT_ASSERT(pm != RT_NULL); /* Reset pmtimer status */ stm32l4_lptim_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) { rt_uint32_t timer_tick; RT_ASSERT(pm != RT_NULL); timer_tick = stm32l4_lptim_get_current_tick(); return stm32l4_os_tick_from_pm_tick(timer_tick); } /** * This function initialize the power manager */ int drv_pm_hw_init(void) { static const struct rt_pm_ops _ops = { sleep, run, pm_timer_start, pm_timer_stop, pm_timer_get_tick }; rt_uint8_t timer_mask = 0; /* Enable Power Clock */ __HAL_RCC_PWR_CLK_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);
drv_lptim.c
int stm32l4_hw_lptim_init(void)
选用的时钟可改为LSE,如果没有外部32.768的晶振就不用改,代码如下
int stm32l4_hw_lptim_init(void) { //RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct = {0}; // /* Enable LSI clock */ // RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI; // RCC_OscInitStruct.LSIState = RCC_LSI_ON; // RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; // HAL_RCC_OscConfig(&RCC_OscInitStruct); /* Select the LSI clock as LPTIM peripheral clock */ RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1; RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE;//选择LSE,默认是LSI HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct); LptimHandle.Instance = LPTIM1; LptimHandle.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC; LptimHandle.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV32; LptimHandle.Init.Trigger.Source = LPTIM_TRIGSOURCE_SOFTWARE; LptimHandle.Init.OutputPolarity = LPTIM_OUTPUTPOLARITY_HIGH; LptimHandle.Init.UpdateMode = LPTIM_UPDATE_IMMEDIATE; LptimHandle.Init.CounterSource = LPTIM_COUNTERSOURCE_INTERNAL; if (HAL_LPTIM_Init(&LptimHandle) != HAL_OK) { return -1; } NVIC_ClearPendingIRQ(LPTIM1_IRQn); NVIC_SetPriority(LPTIM1_IRQn, 0); NVIC_EnableIRQ(LPTIM1_IRQn); return 0; }
board.c中添加void HAL_LPTIM_MspInit(LPTIM_HandleTypeDef *hlptim)
void HAL_LPTIM_MspInit(LPTIM_HandleTypeDef *hlptim) { if (hlptim->Instance == LPTIM1) { /* Peripheral clock enable */ __HAL_RCC_LPTIM1_CLK_ENABLE(); /* LPTIM1 interrupt Init */ } }
board.h中添加头文件#include <rtdevice.h>
#include <rtdevice.h>
pm.c中的函数RT_WEAK rt_tick_t pm_timer_next_timeout_tick(rt_uint8_t mode)中的
return rt_lptimer_next_timeout_tick();暂时没有研究用来干啥的,但肯定不对,可能是调试使用的,暂时不研究,需要在其他地方重构这个函数,此函数带有WEAK头,因此pm.c中的函数自动失效,修改为如下即可
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; }
添加进入和退出低功耗时的配置外设的函数
void rt_pm_notify(uint8_t event, uint8_t mode, void *data) { if(event == RT_PM_ENTER_SLEEP) { default_psw_pin_config(); } else { default_psw_pin_deconfig(); } }
添加函数用法
rt_pm_notify_set(rt_pm_notify, RT_NULL);//设置进出低功耗时调用的函数
rt_pm_default_set(PM_SLEEP_MODE_DEEP);//设置默认低功耗模式
低功耗功能使用
1.默认PM_SLEEP_MODE_NONE模式已被申请,不用重新申请
2.rt_pm_request(PM_SLEEP_MODE_DEEP);//请求低功耗模式,请求的模式中只剩下低功耗时,再进入idle任务时会切换到此低功耗模式
rt_pm_release(PM_SLEEP_MODE_NONE);//释放正常模式,此时只剩下低功耗模式 delay(1000);//执行延迟或任务挂起,进入idle任务时自动进入低功耗模式 //唤醒 rt_pm_request(PM_SLEEP_MODE_NONE);//请求正常模式,此时不会进入低功耗 //正常工作 //完成工作时可再次释放正常模式就能再次进入低功耗 rt_pm_release(PM_SLEEP_MODE_NONE); //进入低功耗的条件就是当前申请的模式中只有低功耗模式,idle函数进入时会切换模式,多个模式时按照最高优先级模式切换,PM_SLEEP_MODE_NONE最高,PM_SLEEP_MODE_SHUTDOWN最低 /* sleep modes */ PM_SLEEP_MODE_NONE = 0, PM_SLEEP_MODE_IDLE, PM_SLEEP_MODE_LIGHT, PM_SLEEP_MODE_DEEP, PM_SLEEP_MODE_STANDBY, PM_SLEEP_MODE_SHUTDOWN, PM_SLEEP_MODE_MAX,
标签:rt,4.1,thread,pm,SLEEP,MODE,tick,PM From: https://www.cnblogs.com/arthurly/p/18227123