2.9 功耗管理
2.9.1 低功耗
概述
TencentOS tiny提供了多级低功耗管理框架。初级低功耗的方案是,当系统处于“空闲”状态,也即进入idle任务时,系统调用处理器(目前支持的架构是arm v7m)低功耗接口进入短暂的睡眠模式。
API讲解
编程实例
对于初级低功耗模式,无需用户编写任何代码,直接通过在tos_config.h打开TOS_CFG_PMR_MGR_EN开关即可:
#define TOS_CFG_PWR_MGR_EN 1u
运行效果
2.9.2 tickless
概述
TencentOS tiny的tickless机制提供了一套非周期性时钟的方案,在系统无需systick驱动调度的情况下,停掉systick。
初级功耗管理方案下,因为还有系统systick的存在,因此系统进入idle任务后,并不会在睡眠模式下停留太久。要想进入到更极致的低功耗状态,需要暂停systick。
arm架构提供三级低功耗模式,sleep、stop、standby模式,三种模式运行功耗逐次降低,standby模式最低。TencentOS tiny的内核提供了简洁清晰的接口来管理各级模式。
API讲解
- void tos_tickless_wkup_alarm_install(k_cpu_lpwr_mode_t mode, k_tickless_wkup_alarm_t *wkup_alarm);
此接口用以安装各低功耗模式下的唤醒闹钟。当内核进入tickless模式下后,systick以及停止了,因此需要其他计时器来将CPU从低功耗模式下唤醒。
根据arm v7m的芯片规格,三种模式下的唤醒源分别为:
- sleep
CPU进入sleep模式后,可以由systick、硬件timer、RTC时钟唤醒(wakeup/alarm中断)。
- stop
CPU进入stop模式后,可以由RTC时钟(wakeup/alarm中断)唤醒。
- standby
CPU进入standby模式后,只可由RTC时钟的alarm中断唤醒(还可以通过外部管脚唤醒,但这不属于TencentOS tiny内核机制设计的范畴)。
k_tickless_wkup_alarm_t定义如下:
- typedef struct k_tickless_wakeup_alarm_st {
- int (*init)(void);
- int (*setup)(k_time_t millisecond);
- int (*dismiss)(void);
- k_time_t (*max_delay)(void); /* in millisecond */
- } k_tickless_wkup_alarm_t;
一个唤醒闹钟有四个成员方法:
- init
闹钟初始化函数。
- setup
闹钟设定函数,入参为闹钟到期时间(单位毫秒)。此闹钟在设定完毕后的millisecond毫秒时来中断。
- dismiss
闹钟解除函数,执行完后闹钟中断不会再来。
- max_delay
此闹钟最长的到期时间(单位为毫秒)。
- k_err_t tos_tickless_wkup_alarm_init(k_cpu_lpwr_mode_t mode);
此函数用来初始化特定模式下的唤醒闹钟(实际上调用的是tos_tickless_wkup_alarm_install接口中安装的k_tickless_wkup_alarm_t的init方法)。
- k_err_t tos_pm_cpu_lpwr_mode_set(k_cpu_lpwr_mode_t cpu_lpwr_mode);
设置内核在tickless模式下进入的CPU低功耗模式。
编程实例
1、在tos_config.h中,配置低功耗组件开关TOS_CFG_PWR_MGR_EN:
#define TOS_CFG_PWR_MGR_EN 1u
2、在tos_config.h中,配置tickless组件开关TOS_CFG_TICKLESS_EN:
#define TOS_CFG_TICKLESS_EN 1u
3、编写main.c示例代码:
- #include "tos.h"
- #include "mcu_init.h"
- #define STK_SIZE_TASK_DEMO 512
- #define PRIO_TASK_DEMO 4
- k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];
- k_task_t task_demo;
- extern void entry_task_demo(void *arg);
- void timer_callback(void *arg)
- {
- printf("timer callback: %lld\n", tos_systick_get());
- }
- void entry_task_demo(void *arg)
- {
- k_timer_t tmr;
- // 创建一个软件定时器,每6000个tick触发一次
- tos_timer_create(&tmr, 0u, 6000u, timer_callback, K_NULL, TOS_OPT_TIMER_PERIODIC);
- tos_timer_start(&tmr);
- // 此任务体内每3000个tick运行一次
- while (K_TRUE) {
- printf("entry task demo: %lld\n", tos_systick_get());
- tos_task_delay(3000);
- }
- }
- int main(void)
- {
- board_init();
- tos_knl_init();
- (void)tos_task_create(&task_demo, "demo1", entry_task_demo, NULL,
- PRIO_TASK_DEMO, stack_task_demo, STK_SIZE_TASK_DEMO,
- 0);
- tos_knl_start();
- }
4、实现tos_bsp_tickless_setup回调(参考board\TOS_tiny_EVK_STM32L431CBT6\BSP\Src\tickless\bsp_pwr_mgr.c、board\TOS_tiny_EVK_STM32L431CBT6\BSP\Src\tickless\bsp_tickless_alarm.c):
- #include "tos.h"
- #include "tickless/bsp_pm_device.h"
- #include "tickless/bsp_tickless_alarm.h"
- int tos_bsp_tickless_setup(void)
- {
- #if TOS_CFG_TICKLESS_EN > 0u
- // sleep模式下的唤醒源,基本定时器
- tos_tickless_wkup_alarm_install(TOS_LOW_POWER_MODE_SLEEP, &tickless_wkup_alarm_tim);
- // 初始化唤醒源闹钟
- tos_tickless_wkup_alarm_init(TOS_LOW_POWER_MODE_SLEEP);
- // 设置tickless状态时进入sleep模式
- tos_pm_cpu_lpwr_mode_set(TOS_LOW_POWER_MODE_SLEEP);
- #endif
- }
5、为了观察在tickless时是否确实没有systick中断,在tos_sys.c的idle任务体内加一句调试代码:
- __STATIC__ void knl_idle_entry(void *arg)
- {
- arg = arg; // make compiler happy
- while (K_TRUE) {
- // 这里在idle任务体内加上一句打印,如果systick正常开启,在没有用户任务运行时,此调试信息会不断打印;如果是tickless状态,此调试信息应该只会第一次进入idle任务时,或在用户任务等待到期,或用户的软件定时器到期时,才打印一次。
- printf("idle entry: %lld\n", tos_systick_get());
- #if TOS_CFG_PWR_MGR_EN > 0u
- pm_power_manager();
- #endif
- }
- }
运行效果
entry task demo: 0idle entry: 2entry task demo: 3002idle entry: 3002timer callback: 6000idle entry: 6000entry task demo: 6002idle entry: 6002entry task demo: 9002idle entry: 9002timer callback: 12000idle entry: 12000entry task demo: 12002idle entry: 12002entry task demo: 15002idle entry: 15002timer callback: 18000idle entry: 18000entry task demo: 18002idle entry: 18002