概述
TencentOS tiny提供了多级低功耗管理框架。初级低功耗的方案是,当系统处于“空闲”状态,也即进入idle任务时,系统调用处理器(目前支持的架构是arm v7m)低功耗接口进入短暂的睡眠模式。
API讲解
编程实例
对于初级低功耗模式,无需用户编写任何代码,直接通过在tos_config.h打开TOS_CFG_PMR_MGR_EN开关即可:
#define TOS_CFG_PWR_MGR_EN 1u
运行效果
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: 0 idle entry: 2 entry task demo: 3002 idle entry: 3002 timer callback: 6000 idle entry: 6000 entry task demo: 6002 idle entry: 6002 entry task demo: 9002 idle entry: 9002 timer callback: 12000 idle entry: 12000 entry task demo: 12002 idle entry: 12002 entry task demo: 15002 idle entry: 15002 timer callback: 18000 idle entry: 18000 entry task demo: 18002 idle entry: 18002