FreeRTOS学习(二十九)——低功耗Tickless Idle

上一节学习了钩子函数与运行时间统计,知道了FreeRTOS可以通过Idle Hook判断系统什么时候处于空闲状态。

当系统没有任务需要运行时:

复制代码
CPU还在干什么?

正常情况下,即使所有任务都在等待,FreeRTOS的系统Tick仍然会周期性产生中断。

例如:

复制代码
configTICK_RATE_HZ = 1000

那么每隔:

复制代码
1ms

CPU就会被Tick中断唤醒一次。

对于普通系统影响不大,但是对于电池供电设备,这种频繁唤醒会增加功耗。

因此FreeRTOS提供了:

复制代码
Tickless Idle

也就是低功耗无节拍空闲模式

本节先学习它的基本工作原理和配置方法。


一、普通Idle模式有什么问题

假设有一个任务:

复制代码
void Task1(void *pvParameters)
{
    while(1)
    {
        GPIO_ToggleBits(
            GPIOE,
            GPIO_Pin_3
        );

        vTaskDelay(
            pdMS_TO_TICKS(1000)
        );
    }
}

Task1执行一次以后:

复制代码
阻塞1000ms

如果系统中没有其他任务需要运行:

复制代码
Task1阻塞
    ↓
Idle Task运行

但是系统Tick仍然存在。

假设:

复制代码
Tick = 1ms

那么Task1等待的这1秒内,理论上仍然会产生约:

复制代码
1000次Tick中断

CPU不断:

复制代码
休息
 ↓
Tick唤醒
 ↓
处理Tick
 ↓
继续休息

对于低功耗系统来说,这些频繁唤醒很多时候没有必要。

二、什么是Tickless Idle

Tickless可以拆成:

复制代码
Tick + less

可以简单理解成:

复制代码
减少不必要的Tick

假设FreeRTOS发现:

复制代码
接下来1000ms

没有任务需要运行

那么系统可以:

复制代码
暂时停止周期Tick
        ↓
进入低功耗状态
        ↓
等待接近任务唤醒时间
        ↓
重新恢复Tick

因此原来的:

复制代码
Tick Tick Tick Tick Tick......

变成:

复制代码
进入休眠
      ↓
长时间不产生周期Tick
      ↓
唤醒

这样CPU就可以拥有更长的连续休眠时间。

三、开启Tickless Idle

在:

复制代码
FreeRTOSConfig.h

中配置:

复制代码
#define configUSE_TICKLESS_IDLE    1

表示:

复制代码
开启Tickless Idle功能。

但是需要注意:

打开这个宏并不代表任何STM32工程都能直接获得理想的低功耗效果。

FreeRTOS负责判断:

复制代码
什么时候可以休眠

而STM32具体:

复制代码
进入哪种低功耗模式

使用什么时钟唤醒

唤醒后怎样恢复时钟

还与移植层和具体工程配置有关。

四、Tickless Idle什么时候进入

FreeRTOS并不是:

复制代码
一进入Idle Task

马上停止Tick。

系统会先计算:

复制代码
距离下一个任务唤醒还有多少Tick

例如:

复制代码
Task1

还需要等待500Tick

同时:

复制代码
Task2

还需要等待800Tick

那么最近需要运行的是:

复制代码
Task1

因此系统知道:

复制代码
未来大约500Tick内

没有任务需要正常运行

这时才有机会进入较长时间的低功耗状态。

五、configEXPECTED_IDLE_TIME_BEFORE_SLEEP

FreeRTOS中还有一个相关配置:

复制代码
configEXPECTED_IDLE_TIME_BEFORE_SLEEP

它可以理解成:

复制代码
至少预计空闲多少个Tick

才值得进入Tickless Idle。

例如:

复制代码
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP  2

如果系统预计只空闲:

复制代码
1Tick

可能就没有必要:

复制代码
进入低功耗
 ↓
马上又退出

因为进入和退出低功耗本身也需要一定开销。

六、一个简单实验

创建两个LED任务:

复制代码
void Task1(void *pvParameters)
{
    while(1)
    {
        GPIO_ToggleBits(
            GPIOE,
            GPIO_Pin_3
        );

        vTaskDelay(
            pdMS_TO_TICKS(1000)
        );
    }
}

第二个任务:

复制代码
void Task2(void *pvParameters)
{
    while(1)
    {
        GPIO_ToggleBits(
            GPIOE,
            GPIO_Pin_4
        );

        vTaskDelay(
            pdMS_TO_TICKS(2000)
        );
    }
}

两个任务执行完成以后都会进入阻塞态。

当:

复制代码
Task1阻塞

Task2阻塞

并且没有其他任务需要运行时:

复制代码
FreeRTOS
    ↓
进入Idle
    ↓
计算预计空闲时间
    ↓
满足条件
    ↓
进入Tickless Idle

到了任务需要运行的时间,再恢复正常调度。

从LED现象上看:

复制代码
LED1仍然约1秒翻转一次

LED2仍然约2秒翻转一次

也就是说:

降低功耗并不意味着原来的任务延时逻辑失效。

七、Tickless Idle和Idle Task的区别

这一点很容易混淆。

普通Idle Task:

复制代码
没有其他任务运行

↓

CPU执行空闲任务

但是:

复制代码
系统Tick仍然周期产生。

Tickless Idle:

复制代码
没有其他任务运行

↓

预计能够空闲较长时间

↓

抑制一段时间的周期Tick

↓

CPU进入低功耗状态

因此:

复制代码
Idle Task

不一定等于

CPU真正长时间休眠

Tickless Idle才是FreeRTOS专门为降低空闲期间周期唤醒次数提供的机制。

八、中断还能不能唤醒CPU

可以。

假设STM32进入低功耗以后:

复制代码
UART收到数据

按键产生外部中断

其他允许的外设产生中断

都可能让CPU提前醒来,具体取决于STM32所使用的低功耗模式和外设配置。

例如:

复制代码
预计休眠500ms

但是:

复制代码
100ms时

UART中断到来

CPU就可能提前退出低功耗状态。

之后FreeRTOS需要根据实际经过的时间:

复制代码
修正系统Tick

然后继续进行任务调度。

九、实际使用时注意什么

Tickless Idle并不是简单打开:

复制代码
configUSE_TICKLESS_IDLE

就结束了。

STM32实际工程中还需要注意:

复制代码
系统时钟

低功耗模式

唤醒源

调试器

UART

定时器

等外设的状态。

特别是在调试阶段,如果发现:

复制代码
开启Tickless以后程序异常

串口异常

系统时间不准确

不要首先怀疑任务代码。

应该重点检查:

复制代码
低功耗进入和退出过程

以及时钟恢复是否正确。

另外,如果系统中的任务一直:

复制代码
while(1)
{
    /* 一直运行 */
}

没有:

复制代码
vTaskDelay();

也没有等待:

复制代码
Queue

Semaphore

Notification

那么系统几乎没有空闲时间:

复制代码
Tickless Idle

自然也没有机会发挥作用。

十、总结

这一节学习了FreeRTOS中的Tickless Idle低功耗机制

普通FreeRTOS运行时:

复制代码
任务全部阻塞
    ↓
Idle Task运行
    ↓
Tick仍然周期产生

开启Tickless Idle以后:

复制代码
任务全部阻塞
    ↓
计算预计空闲时间
    ↓
停止一段时间的周期Tick
    ↓
进入低功耗
    ↓
定时或中断唤醒
    ↓
恢复系统运行

核心配置:

复制代码
#define configUSE_TICKLESS_IDLE    1

因此Tickless Idle真正解决的问题并不是:

复制代码
让任务运行得更快

而是:

复制代码
CPU没有事情做的时候

尽量让它真正休息更久

对于:

复制代码
电池供电设备

无线传感节点

便携式设备

这类对功耗比较敏感的系统非常有意义。

不过STM32真正进入低功耗模式还涉及具体的时钟和唤醒配置,本节先把FreeRTOS层面的工作原理搞清楚即可。

相关推荐
蒸蒸yyyyzwd2 小时前
后端面经学习笔记
笔记·学习
lucky_syq3 小时前
专栏目录与学习路线 | 48 篇正文导览
人工智能·学习
小心亦新3 小时前
STM32学习23--I2C-1
stm32·嵌入式硬件·学习
red_redemption3 小时前
自由学习记录(217)
学习
LYS_06183 小时前
Python学习(3)(数据类型小尾巴,条件控制,循环语句)
学习
迪丽热爱4 小时前
每日英语-12
学习
PC2005-cloud4 小时前
Elasticsearch 学习笔记:集群实战(3 控制节点 + 3 数据节点部署与故障转移)
笔记·学习·elasticsearch
世人万千丶4 小时前
物品借还闭环:鸿蒙物品清单种子数据与清单效果
学习·华为·harmonyos·鸿蒙
我命由我123454 小时前
商圈六大配套
学习·职场和发展·求职招聘·职场发展·产品经理·学习方法·零售