• 2.9 功耗管理
    • 2.9.1 低功耗
      • 概述
      • API讲解
      • 编程实例
      • 运行效果
    • 2.9.2 tickless
      • 概述
      • API讲解
      • 编程实例
      • 运行效果

    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