Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
PM电源管理
低功耗
电源管理
RT-Thread 4.1.0 应用 PM 电源管理框架
发布于 2022-06-04 11:11:38 浏览:1552
订阅该版
[tocm] [RT-Thread 4.1.0 开启 PM 电源管理框架](https://club.rt-thread.org/ask/article/d56efd7358dd2828.html) [RT-Thread 4.1.0 调试 PM 电源管理框架](https://club.rt-thread.org/ask/article/ddf590dee22a19ae.html) ## 前言 - 前面讲解了RT-Thread 4.1.0 PM框架的开启与调试,实际上,我们还没有真正把PM框架应用起来,因为摆在我们面前的功耗问题,我们不清楚如何【分解】 - 裸机程序,代码就是一个【死循环】,可以分为多个执行段,可以在其中的任何一点加入睡眠与唤醒操作,因为系统运行是一条线,调试功耗,处理好中断,无须过多担心并发操作问题。 - 基于多任务操作系统,也就是多线操作,每个线程在不同时刻,都处于不同的状态,在睡眠前,如果需要主动通知所有线程,系统要睡眠了,要做相应的睡眠处理,系统唤醒后,又要通知所有线程,要做相应的唤醒操作,这非常的繁琐,尤其是线程数量非常多的时候。 - 如果某个或某几个线程没有做好睡眠唤醒的操作,系统电源模式将处于什么状态呢?睡眠?不睡眠?轻睡眠?这些问题其实就是引入一个管理的流程问题:【主动睡眠】与【被动睡眠】 - PM本身是个框架,所有的睡眠唤醒操作流程与测量,由用户来决定,根据实际功耗调优的经验,推荐使用【被动睡眠】管理策略 ## 分而治之 - 基于多任务的多线操作时,为了不影响各个任务的执行,引入电源管理的模块化思想,这种分而治之的思想,日常生活中用的非常广泛。 - 系统划分多个电源模块,默认是【深睡眠】模式,如果某个任务要执行,就唤醒系统,请求【不睡眠】,持睡眠锁,当干完活后,释放电源模式,释放这把锁。 - PM框架根据当前请求的电源模式【锁】,进行电源模式的切换。 - 系统在在整个睡眠或退出睡眠的流程下,根本没有去挨个去通知各个线程,系统要睡眠了,系统唤醒了。 - 这就是【被动睡眠】的策略,各个模式管理好自己,如果某个模块一直持锁,系统就不能睡眠,功耗管理者根据持锁的情况,可以排查为何不释放睡眠锁的原因,从而解决电源模式问题。 - 所以,【模块化】+【被动睡眠】,我们不再主动去通知各个模块、线程当前的电源模式,各个模块或线程各自处理好自己的事情即可。 - 这大大简化了功耗管理的复杂流程。 ## 电源管理模块化 - PM框架指导用户把整机的功耗,分解或者拆分为一个个小模块,如LCD、KEY、TP、UART等,这些小模块,或者是驱动,或者是一个线程,或者是一段代码操作。 - 开启PM框架后,功耗管理的重点就是:确认哪个功能模块因为系统进入【深睡眠】受到影响,受到影响的功能模块,定义为一个电源模块,在执行前请求一个正常工作的电源模式,一般为不睡眠或者浅睡眠模式,等这个电源模块操作操作完,释放这个请求的电源模式。保证这个操作在执行的过程中不受到【深睡眠】的干扰 - 电源管理的模块化管理,注意为模块的划分,模块的电源模式请求与释放,很多用户不清楚模块在哪个电源模式下不受影响,那就直接请求一个不睡眠即可,像STM32这种MCU,一般的操作,IDLE模式(WFI、WFE)就足以满足日常的线程操作,但是在一些特殊的场合,如OTA固件升级时,建议不睡眠,请求None模式 - 【说明】RT-Thread 电源管理执行的操作,实在空闲线程(IDLE线程)执行的,也就是优先级最低,理论上,日常的操作,根本不需要【请求】或【释放】电源模式,依旧可以正常工作,一旦有某些特殊操作,如LCD刷DMA操作,此时频繁进入退出【深睡眠】,会造成影响,此时可以通过请求【不睡眠】操作完成【释放不睡眠】,来消除【深睡眠】进入退出切换系统时钟、切换时间久引起的影响。 ## 搭建调试环境 - 调试时,可以使用PM框架提供的串口shell命令操作,正式产品,基本上都是调用PM框架的API,完成睡眠模式的【请求】与【释放】操作 - 这里先实现一个按键中的操作,在按键中断操作中,进行电源模式的切换 - 硬件平台:stm32l476-st-nucleo开发板,基于STM32L476RG ```c #include
#include
#define DBG_ENABLE #define DBG_SECTION_NAME "key" #define DBG_LEVEL DBG_LOG #include
#define PIN_KEY0 45 /* PC13 32+13=45 */ void key0_irq_callback(void *parameter) { static uint8_t key0_status = 0x00; LOG_D("[key0_irq]\n"); key0_status ^= 0x01; if(key0_status == 0x00) rt_pm_sleep_idle_release(PM_BOARD_ID); else rt_pm_sleep_idle_request(PM_BOARD_ID); } int key_gpio_init(void) { LOG_D("key_gpio_init.\n"); /* set key pin mode to input */ LOG_D("PIN_KEY0=%d\n", PIN_KEY0); rt_pin_mode(PIN_KEY0, PIN_MODE_INPUT_PULLUP); /* set interrupt mode and attach interrupt callback function */ rt_pin_attach_irq(PIN_KEY0, PIN_IRQ_MODE_FALLING, key0_irq_callback, RT_NULL); /* enable interrupt */ rt_pin_irq_enable(PIN_KEY0, PIN_IRQ_ENABLE); return 0; } INIT_APP_EXPORT(key_gpio_init); ``` - 自定义电源模块ID,新建一个:`pm_cfg.h` 文件,用于自定义系统的电源模块ID ![2022-06-04_102230.png](https://oss-club.rt-thread.org/uploads/20220604/261cdc1492182180990251625aab4ac5.png.webp) ![2022-06-04_102717.png](https://oss-club.rt-thread.org/uploads/20220604/2e2d328dbf440c83b7df7410ec6b4b0e.png.webp) - 这个 `pm_cfg.h` 文件内容如下,可以参考RT-Thread BSP中的 `rt-thread\bsp\stm32\stm32l475-atk-pandora\board\pm_cfg.h` ![2022-06-04_103111.png](https://oss-club.rt-thread.org/uploads/20220604/dfa660cecc26af3c6bc54b92b2069912.png.webp) ```c #ifndef __PM_CFG_H__ #define __PM_CFG_H__ enum pm_module_id { PM_NONE_ID = 0, PM_POWER_ID, PM_BOARD_ID, PM_LCD_ID, PM_KEY_ID, PM_TP_ID, PM_OTA_ID, PM_SPI_ID, PM_I2C_ID, PM_ADC_ID, PM_RTC_ID, PM_GPIO_ID, PM_UART_ID, PM_SENSOR_ID, PM_ALARM_ID, PM_BLE_ID, PM_MODULE_MAX_ID, /* enum must! */ }; #endif ``` - 这个 `pm_cfg.h` 文件可以放在BSP 工程的 board目录下 - 这个 `pm_cfg.h` 的电源模块的ID,可以随便的修改、添加,删除,注意留着最后的一个枚举值:`PM_MODULE_MAX_ID, /* enum must! */` - 如增加一个:`PM_SENSOR_ID`,就可以调用系统API,请求释放电源模式时,把这个电源模块ID `PM_SENSOR_ID`带入。 - 模块划分没有什么特殊原则,细分为了更好的管理,如有三个传感器,可以划分三个传感器的ID,方便遇到功耗问题时排查 ![2022-06-04_103424.png](https://oss-club.rt-thread.org/uploads/20220604/860f3e9d3631925a1ce80103b093a477.png.webp) ## 调试 - 按键Key操作的例程中,每次按键切换一次电源模式,这个电源模式的切换,可以放在【中断回调】中使用,电源模式的执行会在执行到空闲线程后【真正切换】。 - 【用户担心】当前为Light模式,我请求了None模式,可是没有立即切换到None模式,上面说要等到IDLE线程执行时,才会切换电源模式,这样会造成我的操作收到影响? > 疑问的解答:一般的电源模式,只有深睡眠会打断用户的操作,而深睡眠的执行在【IDLE线程】,在没有进入【IDLE】线程时,其实系统算是【不睡眠】状态,不会影响操作,因为你在【深睡眠】情况下,这个请求睡眠模式的操作执行不了,如果能执行,就代表【CPU】活着,没有进入【深睡眠】模式。 - 电源模式的改变与硬件模式实际的切换,不是同步的,在IDLE 线程切换,会比较的安全。 ## 经典PM API - PM 框架的API,在:`rt-thread\components\drivers\include\drivers\pm.h` 文件中 ```c void rt_pm_request(rt_uint8_t sleep_mode); /* 电源模式:请求某个电源模式 */ void rt_pm_release(rt_uint8_t sleep_mode); /* 电源模式:释放某个电源模式 */ void rt_pm_release_all(rt_uint8_t sleep_mode); /* 电源模式:全部释放某个电源模式 */ ``` - 这接个API,没有电源模块ID,也就是请求全局的电源模式,有【引用计数】,如你请求了两次,就要释放【两次】,也就是请求与释放 必须成对出现 ```c rt_pm_request(PM_SLEEP_MODE_IDLE); /* 请求 IDLE模式 */ do_some_work(); /* 用户操作 */ rt_pm_release(PM_SLEEP_MODE_IDLE); /* 释放 IDLE模式 */ ``` - 多次电源请求,一次性全部释放,使用:`rt_pm_release_all` ```c rt_pm_request(PM_SLEEP_MODE_IDLE); /* 请求 IDLE模式 */ rt_pm_request(PM_SLEEP_MODE_IDLE); /* 请求 IDLE模式 */ rt_pm_request(PM_SLEEP_MODE_IDLE); /* 请求 IDLE模式 */ do_some_work(); /* 用户操作 */ rt_pm_release_all(PM_SLEEP_MODE_IDLE); /* 一次性释放 IDLE模式 */ ``` ## 带模式ID的PM API - 推荐使用的,推荐的原因是模块的独立性,请求与释放不产生引用计数,也就是可以多次请求,一次释放。某个电源模块的请求与释放,不影响其他电源模块。 ```c /* sleep : request or release */ void rt_pm_sleep_request(rt_uint16_t module_id, rt_uint8_t mode); /* 请求某个睡眠模式,灵活,常用 */ void rt_pm_sleep_release(rt_uint16_t module_id, rt_uint8_t mode); /* 请求某个睡眠模式灵活,常用 */ void rt_pm_sleep_none_request(rt_uint16_t module_id); /* 请求None 模式,常用 */ void rt_pm_sleep_none_release(rt_uint16_t module_id); /* 释放None 模式,常用 */ void rt_pm_sleep_idle_request(rt_uint16_t module_id); /* 请求IDLE 模式,常用 */ void rt_pm_sleep_idle_release(rt_uint16_t module_id); /* 释放None 模式,常用 */ void rt_pm_sleep_light_request(rt_uint16_t module_id);/* 请求Light 模式,不常用 */ void rt_pm_sleep_light_release(rt_uint16_t module_id);/* 释放Light 模式,不常用 */ ``` - 非推荐 PM 带模块ID的API,不推荐的原因是这个带模块的接口,没有实现模块的请求释放的独立化,也就是带【全局引用计数】,请求释放的次序,可能会影响其他电源模块的执行。 ```c void rt_pm_module_request(uint8_t module_id, rt_uint8_t sleep_mode); /* 请求某电源模式,模块非独立 */ void rt_pm_module_release(uint8_t module_id, rt_uint8_t sleep_mode);/* 释放某电源模式,模块非独立 */ void rt_pm_module_release_all(uint8_t module_id, rt_uint8_t sleep_mode);/* 一次释放某电源模式,模块非独立 */ ``` - 使用例程,如上面提到的按键的中断中切换电源模式 ```c void key0_irq_callback(void *parameter) { static uint8_t key0_status = 0x00; LOG_D("[key0_irq]\n"); key0_status ^= 0x01; if(key0_status == 0x00) rt_pm_sleep_idle_release(PM_BOARD_ID); /* 释放模式 : IDLE模式, PM_BOARD_ID */ else rt_pm_sleep_idle_request(PM_BOARD_ID); /* 请求模式 : IDLE模式, PM_BOARD_ID */ } ``` ## 电源模块的调试 - 电源模块采用【位图】的表示方法,电源模块32个分为一组,个数不限制,个数越多,占用的内存资源会多一点 - 每个电源模块,请求,会【置位】,多次请求,其实这个电源模式的【位】一直为1 - 由于模块细分化不再使用引用计数,所以是否电源模式,直接【位清零】 - 为何模块细分后,不再使用引用计数?因为使用引起计数,需要用户时刻注意模式请求与释放的成对性,不采用引用计数,请求可以嵌套、循环使用,最后一次性释放即可。 ![2022-06-04_105703.png](https://oss-club.rt-thread.org/uploads/20220604/bc28fd3ca2996056210cca50c23e2e02.png) ## 延时睡眠 - `void rt_pm_module_delay_sleep(rt_uint8_t module_id, rt_tick_t timeout);` - 这个API调用后,就一直请求【BUSY】模式,这个BUSY模式,默认为:IDLE模式,可以配置为None模式 - 好处是不需要频繁请求与释放电源模式,如某个操作大概执行30ms,就直接 ```c rt_pm_module_delay_sleep(PM_LCD_ID, 30); /* timeout 单位:系统tick */ ``` - 灵活的使用这个`rt_pm_module_delay_sleep`,可以降低电源管理的难度,让用户把更多的时间用于实际的功耗拆解与业务管理上。 ## 小结 - 本篇讲解 PM API 的使用,RT-Thread 4.1.0 支持新电源模块管理方法 - PM 框架用于指导用户把整机的电源管理系统拆解为一个个独立的电源模块,各个模块独立请求与释放电源模式,类似于多线程的管理方式。 - 当某个线程操作或外设操作因为【深睡眠】模式受到影响时,请使用电源模式请求与释放的API,保证操作过程正常的执行
3
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
张世争
学以致用
文章
131
回答
802
被采纳
173
关注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
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
ulog
C++_cpp
at_device
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
13
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
本月文章贡献
程序员阿伟
8
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
3
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部