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讲解
  1. 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定义如下:

  1. typedef struct k_tickless_wakeup_alarm_st {
  2. int (*init)(void);
  3. int (*setup)(k_time_t millisecond);
  4. int (*dismiss)(void);
  5. k_time_t (*max_delay)(void); /* in millisecond */
  6. } k_tickless_wkup_alarm_t;

一个唤醒闹钟有四个成员方法:

  • init

闹钟初始化函数。

  • setup

闹钟设定函数,入参为闹钟到期时间(单位毫秒)。此闹钟在设定完毕后的millisecond毫秒时来中断。

  • dismiss

闹钟解除函数,执行完后闹钟中断不会再来。

  • max_delay

此闹钟最长的到期时间(单位为毫秒)。

  1. 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方法)。

  1. 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示例代码:

  1. #include "tos.h"
  2. #include "mcu_init.h"
  3.  
  4. #define STK_SIZE_TASK_DEMO 512
  5.  
  6. #define PRIO_TASK_DEMO 4
  7.  
  8. k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];
  9.  
  10. k_task_t task_demo;
  11.  
  12. extern void entry_task_demo(void *arg);
  13.  
  14. void timer_callback(void *arg)
  15. {
  16. printf("timer callback: %lld\n", tos_systick_get());
  17. }
  18.  
  19. void entry_task_demo(void *arg)
  20. {
  21. k_timer_t tmr;
  22.  
  23. // 创建一个软件定时器,每6000个tick触发一次
  24. tos_timer_create(&tmr, 0u, 6000u, timer_callback, K_NULL, TOS_OPT_TIMER_PERIODIC);
  25. tos_timer_start(&tmr);
  26.  
  27. // 此任务体内每3000个tick运行一次
  28. while (K_TRUE) {
  29. printf("entry task demo: %lld\n", tos_systick_get());
  30. tos_task_delay(3000);
  31. }
  32. }
  33.  
  34. int main(void)
  35. {
  36. board_init();
  37. tos_knl_init();
  38. (void)tos_task_create(&task_demo, "demo1", entry_task_demo, NULL,
  39. PRIO_TASK_DEMO, stack_task_demo, STK_SIZE_TASK_DEMO,
  40. 0);
  41. tos_knl_start();
  42. }

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):

  1. #include "tos.h"
  2. #include "tickless/bsp_pm_device.h"
  3. #include "tickless/bsp_tickless_alarm.h"
  4.  
  5. int tos_bsp_tickless_setup(void)
  6. {
  7. #if TOS_CFG_TICKLESS_EN > 0u
  8. // sleep模式下的唤醒源,基本定时器
  9. tos_tickless_wkup_alarm_install(TOS_LOW_POWER_MODE_SLEEP, &tickless_wkup_alarm_tim);
  10. // 初始化唤醒源闹钟
  11. tos_tickless_wkup_alarm_init(TOS_LOW_POWER_MODE_SLEEP);
  12. // 设置tickless状态时进入sleep模式
  13. tos_pm_cpu_lpwr_mode_set(TOS_LOW_POWER_MODE_SLEEP);
  14. #endif
  15. }

5、为了观察在tickless时是否确实没有systick中断,在tos_sys.c的idle任务体内加一句调试代码:

  1. __STATIC__ void knl_idle_entry(void *arg)
  2. {
  3. arg = arg; // make compiler happy
  4.  
  5. while (K_TRUE) {
  6. // 这里在idle任务体内加上一句打印,如果systick正常开启,在没有用户任务运行时,此调试信息会不断打印;如果是tickless状态,此调试信息应该只会第一次进入idle任务时,或在用户任务等待到期,或用户的软件定时器到期时,才打印一次。
  7. printf("idle entry: %lld\n", tos_systick_get());
  8. #if TOS_CFG_PWR_MGR_EN > 0u
  9. pm_power_manager();
  10. #endif
  11. }
  12. }
运行效果

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