Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
STM32超低功耗
RT-Thread PM 组件 TICKLESS 原理分析
发布于 2021-06-26 22:07:37 浏览:1903
订阅该版
[tocm] ## 一,什么是 TICKLESS 在解释 TICKLESS 之前,回顾一些知识点: - RTOS 需要一个周期性的定时器来给操作系统提供一个时间基准,这个定时器会周期性的触发一个中断,一般来说会把这个频率设置为 1000,也就是说每秒每触发 1000 次中断 - 使用 WFI 指令进入睡眠模式之后,会被任意中断唤醒 这里就出现了一个矛盾点,睡眠时希望能降低功耗,但是每间隔一个毫秒就被唤醒了,就像人要睡觉,你刚把眼睛闭上就被人叫醒了,这怎么能实现功耗的降低呢?所以就出现了 TICKLESS,他的作用是尽可能的降低系统时钟的响应频率的同时,保证系统的实时性和降低系统的功耗。 ## 二,TICKLESS 的实现 TICKLESS 的为了保证任务与功耗的兼顾所以这个相关的处理都被放在 IDEL 线程中,所以只需要分析 IDEL 中调用的功耗处理函数即可, 最为核心的就是下面这个函数。 ``` /** * This function will enter corresponding power mode. */ void rt_system_power_manager(void) { if (_pm_init_flag == 0) return; /* CPU frequency scaling according to the runing mode settings */ _pm_frequency_scaling(&_pm); /* Low Power Mode Processing */ _pm_change_sleep_mode(&_pm); } ``` 这里只需分析这里面的两个函数即可: 1. 调整 CPU 的运行频率 ``` /** * This function will update the system clock frequency when idle */ static void _pm_frequency_scaling(struct rt_pm *pm) { rt_base_t level; if (pm->flags & RT_PM_FREQUENCY_PENDING) { level = rt_hw_interrupt_disable(); /* change system runing mode */ pm->ops->run(pm, pm->run_mode); /* changer device frequency */ _pm_device_frequency_change(pm->run_mode); pm->flags &= ~RT_PM_FREQUENCY_PENDING; rt_hw_interrupt_enable(level); } } ``` 首先检查是否有改变系统运行频率的请求被挂起,在关闭中断的情况下来设置 CPU 即将运行的频率。 ` pm->ops->run(pm, pm->run_mode);` 这里的运行模式通过 `int rt_pm_run_enter(rt_uint8_t mode)` 来让 CPU 进入不同频率的运行模式。 ` _pm_device_frequency_change(pm->run_mode);` 这里去设置 device 的运行频率 清楚改变系统运行频率的标志位。 根据前面文章移植的方法没有设置过改变系统的运行频率,也没有注册 device 设备运行频率改变的接口,所以这里并不会得到执行。如果有这方面的需求,需要去通过 `void rt_pm_device_register(struct rt_device *device, const struct rt_device_pm_ops *ops)` 去注册对应的回调函数即可。 2. 调整 CPU 的睡眠模式 代码较多逐段的分析 ``` /** * This function changes the power sleep mode base on the result of selection */ static void _pm_change_sleep_mode(struct rt_pm *pm) { rt_tick_t timeout_tick, delta_tick; rt_base_t level; int ret = RT_EOK; level = rt_pm_enter_critical(pm->sleep_mode); /* module busy request */ if (_pm_device_check_idle() == RT_FALSE) { pm->ops->sleep(pm, PM_SLEEP_MODE_NONE); rt_pm_exit_critical(level, pm->sleep_mode); return; } if (_pm.sleep_mode == PM_SLEEP_MODE_NONE) { pm->ops->sleep(pm, PM_SLEEP_MODE_NONE); rt_pm_exit_critical(level, pm->sleep_mode); } else { ``` 禁止调度,避免在进行睡眠模式的时候进行了任务切换。 检查所有的模块是否都处于空闲状态,如果不是则会退出睡眠模式,允许任务切换,并立刻退出。 判断当前的没有睡眠模式,那么直接切换到无睡眠模式的状态,然后开启允许任务切换 ``` else { /* Notify app will enter sleep mode */ if (_pm_notify.notify) _pm_notify.notify(RT_PM_ENTER_SLEEP, pm->sleep_mode, _pm_notify.data); /* Suspend all peripheral device */ ret = _pm_device_suspend(pm->sleep_mode); if (ret != RT_EOK) { _pm_device_resume(pm->sleep_mode); if (_pm_notify.notify) _pm_notify.notify(RT_PM_EXIT_SLEEP, pm->sleep_mode, _pm_notify.data); rt_pm_exit_critical(level, pm->sleep_mode); return; } ``` 检查是否有 app 通知进入低功耗模式。如果要使用 notify 的功能,需要用户自己通过 `void rt_pm_notify_set(void (*notify)(rt_uint8_t event, rt_uint8_t mode, void *data), void *data)` 去注册。 将所有的 device 是否已经挂起,如果挂起失败则恢复已经挂起的 device 如果有 notify 需要去通知,则进入 notify 回调,然后恢复任务调度 3. TICKLESS 的实现 ``` /* Tickless*/ if (pm->timer_mask & (0x01 << pm->sleep_mode)) { timeout_tick = rt_timer_next_timeout_tick(); if (timeout_tick == RT_TICK_MAX) { if (pm->ops->timer_start) { pm->ops->timer_start(pm, RT_TICK_MAX); } } else { timeout_tick = timeout_tick - rt_tick_get(); if (timeout_tick < RT_PM_TICKLESS_THRESH) { pm->sleep_mode = PM_SLEEP_MODE_IDLE; } else { pm->ops->timer_start(pm, timeout_tick); } } } ``` 检查 `timer_mask` 的值,这个值在初始化的时候被设置成了 `PM_SLEEP_MODE_DEEP`, 也就是说只有在深度睡眠模式下才会启用 TICKLESS 检查下一个超时事件发生时的 `tick` 值, 这个事件比如是线程的睡眠时间到了。 如果是 RT_TICK_MAX,且 LPTIM 已经使能,则设置 LPTIM 的唤醒时间为 RT_TICK_MAX 如果下一个超时节点的 `tick` 不为 RT_TICK_MAX,则计算出下一次超时还需要经过多长时间,如果小于 TICKLESS 的最小粒度,这个值是一个宏,默认是 2 ,用户可以自己修改,就直接设置 `sleep_mode` 即可,反之就启动 LPTIM 4. 功耗管理的退出 ``` /* enter lower power state */ pm->ops->sleep(pm, pm->sleep_mode); /* wake up from lower power state*/ if (pm->timer_mask & (0x01 << pm->sleep_mode)) { delta_tick = pm->ops->timer_get_tick(pm); pm->ops->timer_stop(pm); if (delta_tick) { rt_tick_set(rt_tick_get() + delta_tick); } } /* resume all device */ _pm_device_resume(pm->sleep_mode); if (_pm_notify.notify) _pm_notify.notify(RT_PM_EXIT_SLEEP, pm->sleep_mode, _pm_notify.data); rt_pm_exit_critical(level, pm->sleep_mode); if (pm->timer_mask & (0x01 << pm->sleep_mode)) { if (delta_tick) { rt_timer_check(); } } ``` 让 MCU 进入对应的睡眠模式。 检查当前是不是深度睡眠模式,如果是获取 LPTIM 经过了多少 `tick`,关闭 LPTIM,重新设置系统的 tick 数,用来补偿在深度睡眠模式下 systick 停止导致系统时钟的滞后的问题。 恢复素有的 device。 执行 notify. 允许调度。 如果是在深度睡眠模式下被唤醒,就去让所有的 timer 去检查是否已经有超时事件需要去处理 ## 总结 通过以上的分析,可以发现原理似乎没有多么的复杂,但是要更好的实现功耗的控制,还需用户自己做好以下几点: 1. 根据业务需求去做好 notify 2. 可以适当的增加或减少不同的功耗模式 3. tickless 是不能在 待机和关机模式下使用的,这个需要用户自行使用 RTC 的闹钟功能去唤醒 4. PM 是个框架,用户根据自身的需求去增加 device 等功耗处理逻辑 5. 熟练掌握其中的参数,随时调整,以备根据不同场景做出更改
1
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
whj467467222
开源,分享,交流,共同进步
文章
32
回答
1222
被采纳
149
关注TA
发私信
相关文章
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
cubemx
WIZnet_W5500
ota在线升级
PWM
BSP
flash
freemodbus
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
ulog
SFUD
msh
C++_cpp
MicroPython
本月问答贡献
RTT_逍遥
7
个答案
2
次被采纳
三世执戟
7
个答案
1
次被采纳
KunYi
6
个答案
1
次被采纳
winfeng
2
个答案
1
次被采纳
chenyaxing
2
个答案
1
次被采纳
本月文章贡献
出出啊
1
篇文章
4
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部