前言
本篇主要对RT-Thread PM2.0(2021更新版)做个使用指南,介绍PM管理的新API的使用
PM管理(电源管理、功耗管理)是如今穿戴类产品比较关心的产品卖点
前面的几篇文章,或多或少提过电源管理其实管理的是硬件与业务
用户尽量不要在PM框架上消耗时间,把重心用于功耗调优上
使用指南
开启PM框架,新的版本增加了几个API
API 介绍如下,具体可以查看 pm.h文件
/* 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); /* 释放:idle模式 */
void rt_pm_sleep_light_request(rt_uint16_t module_id); /* 请求不睡眠:light模式 */
void rt_pm_sleep_light_release(rt_uint16_t module_id); /* 释放:light模式 */
新增的几个睡眠(sleep)相关的API,请求与释放(request、release)是模块独立的,不带引用计数,也就是可以多次请求,但是一次释放就可以释放掉请求的电源模式,可以重复释放。
这里推荐使用【用户自定义的PM模块】,也就是用户自己新建一个pm_cfg.h文件,放在自己的平台工程里
pm_cfg.h 文件用于定义项目本身使用的模块ID,如按键、LCD、TP等等,可以与设备相关,可以与业务相关,也就是PM模块化,当然也可以使用pm.h中默认的(如果满足需求)。
ifndef PM_CFG_H
define PM_CFG_H
enum pm_module_id {
PM_NONE_ID = 0,
PM_POWER_ID,
PM_BOARD_ID,
PM_MODULE_MAX_ID, / enum must! /
};
endif
- 平台适配,[【PM组件】RT-Thread PM2.0 应用 -- 平台适配篇](https:
## 使用示例
```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);
else
rt_pm_sleep_idle_request(PM_BOARD_ID);
}
PM调试
原有的PM相关的shell命令不变
新增PM shell命令: pm_sleep_dump
msh >pm_sleep_dump
+-------------+--------------+
| Sleep Mode | Request List |
+-------------+--------------+
| Mode[0] : 0 | 0x00000000 |
| Mode[1] : 0 | 0x00000004 |
| Mode[2] : 0 | 0x00000000 |
| Mode[3] : 0 | 0x00000000 |
| Mode[4] : 0 | 0x00000000 |
+-------------+--------------+
每个模块占用一位,Mode[0] 为各个睡眠模式
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,
如果请求了一个 rt_pm_sleep_idle_request(PM_BOARD_ID),这里的 PM_BOARD_ID为 2,IDLE模式,所以 | Mode[1] : 0 | 0x00000004 |,这里的4,其实是 1<<2,ID从0 开始
低功耗定时器
rt-thread\components\drivers\pm\lptimer.c
rt-thread\components\drivers\include\drivers\lptimer.h
作用:默认深睡眠(DEEPSLEEP)时,只允许 lptimer.c 定义的定时器唤醒工作,其他普通的系统定时器深睡眠下不唤醒,用于优化深睡眠模式下的功耗(减少唤醒)。
定义lptimer的 API如下:详细查看 lptimer.h
struct rt_lptimer
{
struct rt_timer timer;
rt_list_t list;
void *parameter;
void (*timeout_func)(void *parameter);
};
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);
#ifdef RT_USING_HEAP
rt_lptimer_t rt_lptimer_create(const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag);
rt_err_t rt_lptimer_delete(rt_lptimer_t timer);
#endif
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);
目前只支持定义静态的timer,后期会增加动态创建lptimer的API
-【lptimer注意事项】
如果是一次性的定时器,需要在定时器回调中,手动调用: rt_lptimer_stop,因为目前的实现机制是【套壳系统定时器rt_timer】,后期会修复此问题
lptimer定义的timer,运行在:整个电源模式,包括深睡眠,为【全天候】定时器
不使用lptimer
如果用户想让所有的系统定时器timer,都可以在【深睡眠下】唤醒工作,方法如下
无须定义lptimer
重写【weak】函数 pm_timer_next_timeout_tick,让系统的所以定时器都可以【全天候】工作
rt_tick_t pm_timer_next_timeout_tick(rt_uint8_t mode)
{
return rt_timer_next_timeout_tick();
}
这个 pm_timer_next_timeout_tick为一个[weak]函数,默认定义如下:
RT_WEAK rt_tick_t pm_timer_next_timeout_tick(rt_uint8_t mode)
{
switch (mode)
{
case PM_SLEEP_MODE_LIGHT:
return rt_timer_next_timeout_tick();
case PM_SLEEP_MODE_DEEP:
case PM_SLEEP_MODE_STANDBY:
return rt_lptimer_next_timeout_tick();
}
return RT_TICK_MAX;
}
也就是PM框架,是通过获取系统定时器或lptimer定义的定时器的超时时间,设置到【真实的】lptimer上的。
小结
PM管理牵涉的内容较多,需要不断的优化,积累相关的优化经验,PM框架起到方便管理的目的
新的PM框架的PM模块化的改进,lptimer的定义与使用,会让PM管理起来更方便,调试更有效率(容易定位问题)
PM框架后期会持续完善,包括完善【变频】【延时睡眠】等机制,让用户使用起来更简单、高效。
原作者:张世争