Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
PM电源管理
功耗调优
电源管理
RT-Thread 4.1.0 调试 PM 电源管理框架
发布于 2022-06-04 00:47:46 浏览:1900
订阅该版
[tocm] [RT-Thread 4.1.0 开启 PM 电源管理框架](https://club.rt-thread.org/ask/article/d56efd7358dd2828.html) ## 前言 - 上一篇讲了基于RT-Thread 4.1.0 开启了PM电源管理框架,并且通过【手动释放】PM框架初始化时默认请求的【不睡眠None模式】,系统进入了【深睡眠】,并且【睡得很深】,一睡不醒。 - 本篇讲解PM框架的工作流程,如何适配电源模式与低功耗定时器,如何让系统低功耗运行 ## 电源模式 - 如果系统一睡不醒,就像关机一样,虽然功耗确实低了,但是系统也不干活了。我们的想法就是:让系统间歇性工作,该干活时醒来干活,空闲时就【见缝插针】小睡一把。如何实现这个操作呢? - 这里引入MCU的【电源模式】的概念,像STM32这种MCU,默认为正常工作模式,此时功耗比较的高,我们把这个模式称之为【不睡眠模式】,也就是PM框架中的:`PM_SLEEP_MODE_NONE`,此时MCU一直不睡眠,随叫随到,干活也最流畅。 - STM32 还有其他的几个电源模式:`IDLE`模式、`LOW Power Sleep`模式、`Deep Sleep`模式、`StandBy`模式、`ShutDown`模式等,这些模式,功耗一个比一个低,但是进入睡眠与唤醒越来越复杂,并且越来越耗时。 - 进入`IDLE`模式,STM32只有CPU内核睡了,片内的外设没有关闭,功耗没有将至最低,进入退出这个睡眠模式很快。 - 进入`Deep Sleep`(深睡眠)模式,CPU内核与片内的大部分外设时钟都关闭了,功耗降到很低的水平,如几个uA(微安),但是因为进入这个睡眠模式关闭的东西太多了,唤醒后,需要手动一一打开,造成唤醒恢复的时间变长了。 ## 模式切换 - 如果说RT-Thread调度器为了线程的切换,那么PM框架的作用就是系统电源模式的切换。 - 线程切换是保存旧线程状态、运行优先级更高的线程,恢复新线程状态。 - PM电源模式切换,也需要实现切换到【优先级】更高的电源模式。 - 一般情况下,为了满足不同外设的最大需求,功耗越高的电源模式,优先级越高。 - 如不睡眠模式:`PM_SLEEP_MODE_NONE`优先级最高,一旦请求这个模式,那么PM框架就立即切换到这个优先级最高的电源模式 ![2022-06-04_011125.png](https://oss-club.rt-thread.org/uploads/20220604/95de789367c9a742068ddabee0b95c73.png) ## 电源模式适配 - PM框架适配的第一个步骤就是:实现所使用的MCU(CPU)平台的各个电源模式,一般也没有几个,如PM框架定义了如下几个: ```c /* All modes used for rt_pm_request() and rt_pm_release() */ enum { /* 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, }; ``` - 适配这几个MCU电源模式后,供PM框架调度来回切换,PM框架会一直选择运行用户请求的【功耗最高】的电源模式,功耗最高的电源模式,优先级最高 ## STM32L4 适配电源模式 - 这里参考:STM32L4 系列的PM框架适配文件:`drv_pm.c` ```c /** * 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_ReConfig(pm->run_mode); 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; } } ``` - 这里的BSP是个开发板,所以PM框架仅仅适配了几个MCU的电源模式,实际开发的产品,一般需要增加很多额外的操作,如电源引脚的控制、外设的处理、中断的管理等 - 产品整机的功耗,不只是MCU本身的功耗,还有片外外设的、引脚的、其他功能模块的功耗。 ## 电源模式切换 - 上面说了,PM框架负责电源模式的切换,而电源模式进入与退出、电源模式切换,需要很多额外的操作,这些操作可能在RT-Thread PM 框架 STM32L4 适配文件`drv_pm.c`一笔带过,但是实际产品的开发中,外设等还是比较的多的,管理起来也是比较的复杂的。 - 各个电源模式切换,牵涉到时钟的切换、外设的开关管理、一些唤醒中断的配置操作等,也就是【进入睡眠】->【睡眠】->【睡眠唤醒】往复切换,根据实际的平台,需要一些额外的操作 - 如STM32L4 的 DeepSleep模式,大部分的外设时钟关闭了,MCU内核也关闭了,唤醒后需要重新配置时钟等,有些在【睡眠】模式下可以关闭的外设或引脚,唤醒后可能需要恢复。 - PM框架为了实现电源模式的切换,需要工作时请求一个【浅睡眠】模式,或者称之请求一个【可以工作的电源模式】,如你请求了浅睡眠模式:`PM_SLEEP_MODE_IDLE`,此时PM框架切换到这个`PM_SLEEP_MODE_IDLE`模式,工作(一般只当前的一小段操作)完成后,需要释放这个请求的【浅睡眠】模式,PM框架会再次【电源模式调度】,选择当前【优先级最高】,也就是【功耗最高】的电源模式执行 - 如果用户不请求任何电源模式,也就是系统空闲,PM框架会进入默认的最高功耗模式【深睡眠】,也就是空闲时,默认进入深睡眠模式:`PM_SLEEP_MODE_DEEP`,如开机后,用户释放掉PM框架默认请求的【不睡眠模式:PM_SLEEP_MODE_NONE】后,就会因为没有任何模式请求,进入:深睡眠`PM_SLEEP_MODE_DEEP`模式。 - 调试时发现,串口无法输入了,甚至LED灯都不闪烁了,下载程序都无法正常下载了,因为没有请求任何的电源模式,MCU处于深睡眠模式,CPU内核掉电了,J-Link、SWD等调试器,都无法连接了。如果想烧写,只能复位系统,复位过程中MCU不睡眠,因为PM框架默认请求了【不睡眠】。 - 所以为了调试方便,不建议系统初始化完成后,立即释放所有请求的电源模式,进入【深睡眠】,防止系统无法唤醒,从而【变砖】 - 如果用户请求了一个【高功耗】的电源模式,并且一直不释放,就会造成系统无法进入更低的功耗模式。如请求了IDLE模式不释放,就不会运行【DEEP SLEEP】等功耗较低的睡眠模式。 ## 电源模式不宜过多 - 系统运行被PM框架【分割】成一个个的短操作,工作时间短,可以进入【高功耗】电源模式,空闲时进入【低功耗】的睡眠模式,并且尽可能让系统在【低功耗】电源模式下持续更久。 - 通过整机的功耗的电流波形发现,这就是【间歇性】工作模式,可以称之为:Tickless模式,深睡眠时,系统的Systick定时器也停止了,唤醒后Systick恢复工作。这种一会干活一会停的操作,就是Tick-less模式。深睡眠时,系统的心跳 Tick 都停止了一段时间,直到下次被唤醒 - 既然可以很快的睡眠、唤醒工作,睡眠模式过多反而容易造成功耗切换损失,并且系统也会因为电源模式频繁的切换产生【额外的】不稳定,如系统或外设时钟频繁的切换配置。 ## 防止外设电源频繁切换 - PM框架可能会切换的比较的频繁,如在IDLE睡眠模式,可能1ms就被唤醒一次,这个1ms内,就不需要开关某些外设电源或控制某个引脚了,如果想降低睡眠,还是要进入深睡眠,并且保证深睡眠不频繁的切换。 - 所以一般只需实现两个电源模式即可:IDLE模式、DEEP SLEEP模式,None模式就是空操作,也就是【完全不睡眠】,在STM32上,IDLE模式可以简单的使用:`__WFI();`。 - STM32的PM框架电源模式部分适配时,只需要重点处理深睡眠【DEEP SLEEP】电源模式的进入与退出。 ## 适配Lptimer - 如果系统可以被外部间歇性唤醒,可以不启用Lptimer,如果系统没有外部的唤醒源,进入【深睡眠】模式后,systick停掉了,系统将无法唤醒。 - STM32L 系列,有一个Lptimer,这是个低功耗的定时器,在`DEEP Sleep`模式下,可以打开,并且可以设置超时时间,用于唤醒MCU,MCU在唤醒后,就可以处理工作,如一些定时操作任务,处理完,就再次设置好Lptimer,进入深睡眠。 - 也就是上述:Tick-less模式实现的前提:需要一个深睡眠下可以工作、可以配置定时唤醒的定时器,如STM32的Lptimer - STM32L 系统之外的MCU,没有Lptimer,可以使用RTC、或者Alarm定时唤醒,显然这种的操作粒度,不如STM32L系列好。 - STM32L4 系统的Lptimer,已经适配好了,时钟源可以是外部的LSE(32768Hz),也可以是STM32内部的(LSI),定时器的超时时间可以配置。 - RT-Thread 4.1.0,可以使用PM框架文件中的:`lptimer.c`,配合PM框架 lptimer 适配文件:`drv_lptim.c`,完成低功耗定时器的适配与操作 ## drv_lptim.c - 参照:STM32L4 系列 PM框架的lptimer 适配文件:`drv_lptim.c`,需要实现定功耗定时器的开启、停止、获取tick等操作。 ```c static const struct rt_pm_ops _ops = { sleep, run, pm_timer_start, pm_timer_stop, pm_timer_get_tick }; ``` ## lptimer.c - 这个属于PM框架低功耗定时器管理部分,功能初步实现,可以通过:`lptimer.h`查看响应的API ```c struct rt_lptimer { struct rt_timer timer; rt_list_t list; }; typedef struct rt_lptimer *rt_lptimer_t; void rt_lptimer_init(rt_lptimer_t timer, const char *name, void (*timeout)(void *parameter), void *parameter, rt_tick_t time, rt_uint8_t flag); rt_err_t rt_lptimer_detach(rt_lptimer_t timer); rt_err_t rt_lptimer_start(rt_lptimer_t timer); rt_err_t rt_lptimer_stop(rt_lptimer_t timer); rt_err_t rt_lptimer_control(rt_lptimer_t timer, int cmd, void *arg); rt_tick_t rt_lptimer_next_timeout_tick(void); ``` - 使用说明:如果系统的软件定时器很多,如有一个`50ms`的周期性定时器,会造成系统频繁的进行电源模式切换,如在【不睡眠】与【深睡眠】之间频繁切换,切换开销造成的功耗很大,系统稳定性也受到影响。 - 因此这里把【软件定时器】与【低功耗定时器】区分开来,普通的软件定时器,在进入深睡眠时,不需要手动停止,会自动停止,【低功耗定时器】可以正常的工作,也就是进入深睡眠前,PM框架获取【低功耗定时器】链上最短的超时时间,设置到【物理lptimer】上,保证低功耗时【按时唤醒】操作 ## lptimer平台适配调试 - 上篇开启PM框架后,发现进入深睡眠后,使用:`rt_thread_mdelay(500);` 产生的线程普通定时器,无法唤醒LED灯,这是正常的。 - 如果定时器唤醒系统并点亮LED灯,有两种做法 - 第一种:使用PM管理框架 `lptimer.c` 提供的低功耗定时器API,初始化一个lptimer定时器,在定时器超时回调中执行点亮LED - 第二种:屏蔽PM管理框架lptimer功能,让普通的定时器,可以正常的唤醒系统 ### 第一种:使用lptimer,在深睡眠定时器唤醒的测试例程 ```c #include
#include
#define DBG_ENABLE #define LOG_LVL DBG_LOG #define LOG_TAG "pm.lptim" #include
static struct rt_lptimer lp_timer_t0 = { 0 }; static rt_bool_t lp_timer_t0_flag = RT_FALSE; static struct rt_lptimer lp_timer_t1 = { 0 }; static rt_bool_t lp_timer_t1_flag = RT_FALSE; #define LP_TIMER_T0_TIMEOUT 5*1000 #define LP_TIMER_T1_TIMEOUT 5*1000 void lp_timer_t0_entry(void *param) { rt_kprintf("%s: tick = %d\n", __func__, rt_tick_get()); LOG_D("%s: --lp-timer-t0 ok!\n", __func__); } int lp_timer_t0_init(void) { rt_lptimer_init(&lp_timer_t0, "lptim_t0", lp_timer_t0_entry, RT_NULL, LP_TIMER_T0_TIMEOUT, RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER); lp_timer_t0_flag = RT_FALSE; return RT_EOK; } int lp_timer_t0_start(void) { if (lp_timer_t0_flag == RT_FALSE) { rt_lptimer_start(&lp_timer_t0); lp_timer_t0_flag = RT_TRUE; } return RT_EOK; } int lp_timer_t0_stop(void) { if (lp_timer_t0_flag == RT_TRUE) { rt_lptimer_stop(&lp_timer_t0); lp_timer_t0_flag = RT_FALSE; } return RT_EOK; } int lp_timer_t0_delete(void) { rt_lptimer_detach(&lp_timer_t0); lp_timer_t0_flag = RT_FALSE; return RT_EOK; } void lp_timer_t1_entry(void *param) { rt_kprintf("%s: tick = %d\n", __func__, rt_tick_get()); LOG_D("%s: --lp-timer-t1 ok!\n", __func__); } int lp_timer_t1_init(void) { rt_lptimer_init(&lp_timer_t1, "lptim_t1", lp_timer_t1_entry, RT_NULL, LP_TIMER_T1_TIMEOUT, RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER); lp_timer_t1_flag = RT_FALSE; return RT_EOK; } int lp_timer_t1_start(void) { if (lp_timer_t1_flag == RT_FALSE) { rt_lptimer_start(&lp_timer_t1); lp_timer_t1_flag = RT_TRUE; } return RT_EOK; } int lp_timer_t1_stop(void) { if (lp_timer_t1_flag == RT_TRUE) { rt_lptimer_stop(&lp_timer_t1); lp_timer_t1_flag = RT_FALSE; } return RT_EOK; } int lp_timer_t1_deinit(void) { rt_lptimer_detach(&lp_timer_t1); lp_timer_t0_flag = RT_FALSE; return RT_EOK; } INIT_APP_EXPORT(lp_timer_t0_init); MSH_CMD_EXPORT(lp_timer_t0_start, soft timer t0 start); MSH_CMD_EXPORT(lp_timer_t0_stop, soft timer t0 stop); INIT_APP_EXPORT(lp_timer_t1_init); MSH_CMD_EXPORT(lp_timer_t1_start, soft timer t1 start); MSH_CMD_EXPORT(lp_timer_t1_stop, soft timer t1 stop); ``` ![2022-06-04_003331.png](https://oss-club.rt-thread.org/uploads/20220604/69451207e8f86e96d9e15b2e81c3bec9.png) ### 第二种:屏蔽PM管理框架lptimer功能 - PM框架每次在进入深睡眠前,通过获取系统软件定时器链表或者定功耗定时器链表上的最短定时器超时时间,然后不断的设置到MCU lptimer 定时器外设上,从而实现深睡眠下的定时器唤醒。 - 用户重写 PM框架的这个函数 :`RT_WEAK rt_tick_t pm_timer_next_timeout_tick(rt_uint8_t mode)`函数 ![2022-06-04_003649.png](https://oss-club.rt-thread.org/uploads/20220604/47153bda67a27729c263ed063f077ddb.png.webp) - 如在main 文件中重写为: ```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(); /* 从系统软件定时器链表上获取即将超时定时器的timeout tick */ } return RT_TICK_MAX; } ``` ![2022-06-04_004216.png](https://oss-club.rt-thread.org/uploads/20220604/6d7d75e16b9222414ea7484041d1a7f2.png.webp) - 测试发现,系统运行在【深睡眠模式】,此时串口shell已经不能输入,但是LED等500ms 切换一次,也就是系统定时器:`rt_thread_mdelay` 产生的线程定时器,可以周期性的唤醒【深睡眠】模式 ## 小结 - 本篇注意讲解【电源模式】与【睡眠模式】【工作模式】的概念 - 讲解电源模式,主要是【深睡眠】模式的适配问题 - 讲解Tickless模式,也就是lptimer 在【深睡眠】模式中的作用,工作原理 - 周期性唤醒【深睡眠】的两种方法:lptimer、普通定时器
4
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
张世争
学以致用
文章
131
回答
813
被采纳
177
关注TA
发私信
相关文章
1
STM32F103的低功耗
2
最近用rtthread系统下AD采样并低功耗,中断响应不及时
3
rt-thread低功耗休眠应用问题请教
4
关于RTT对低功耗的支持
5
RT-Thread怎么休眠实现低功耗。
6
RT-Thread V3.0支持的低功耗,OS会自行进入吗?
7
关于RTThread3.0低功耗休眠模式
8
RTT3.0的bsp包中哪些MCU自带低功耗定时器?
9
关于低功耗上次说针对L4出个BSP的,怎么迟迟不见呀
10
低功耗问题。
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
cubemx
PWM
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
xusiwei1236
8
个答案
2
次被采纳
踩姑娘的小蘑菇
1
个答案
2
次被采纳
用户名由3_15位
9
个答案
1
次被采纳
bernard
4
个答案
1
次被采纳
RTT_逍遥
3
个答案
1
次被采纳
本月文章贡献
聚散无由
2
篇文章
15
次点赞
catcatbing
2
篇文章
5
次点赞
Wade
2
篇文章
4
次点赞
Ghost_Girls
1
篇文章
7
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部