解析 FreeRTOS Tickless 低功耗模式:原理、配置与实验操作
FreeRTOS Tickless 低功耗模式是一种智能节能机制,通过关闭系统滴答定时器(SysTick)让 MCU 在空闲时进入低功耗模式,仅在必要时唤醒 CPU,从而降低功耗。本文深入解析 Tickless 模式的工作原理、常见低功耗模式(Sleep、Stop、Standby)、关键配置项及其作用,并结合 FreeRTOS 任务同步实验,展示 Tickless 低功耗模式的实战应用。
1. FreeRTOS 的低功耗模式
1.1 Tickless 低功耗模式简介
Tickless 模式是 FreeRTOS 为了降低功耗而提供的一种机制。在默认情况下,FreeRTOS 采用固定间隔的系统滴答(Tick)中断 来管理任务调度,即 每隔固定时间(如 1ms)触发一次中断,即使系统处于空闲状态,Tick 也会继续触发,导致 CPU 无法进入深度低功耗模式。
Tickless 模式的核心思想是:
当系统进入空闲任务时,FreeRTOS 关闭系统滴答定时器,进入深度低功耗模式,并在下一个需要唤醒的时间点自动唤醒 CPU,重新启用系统滴答。
Tickless 模式的工作流程:
当启用
Tickless模式后,FreeRTOS 的 系统滴答定时器(SysTick) 行为如下:
- 当系统检测到所有任务都进入阻塞(空闲)状态:
- FreeRTOS 计算下一个任务准备就绪的时间(例如 100ms)。
- 关闭滴答定时器(SysTick),减少 CPU 运行次数,降低功耗。
- MCU 进入 低功耗模式。
- 当定时器到达预定时间:
- 由
SysTick或 RTC 唤醒 CPU。- FreeRTOS 恢复滴答定时器,继续任务调度。
- 如果中断发生:
- 例如串口接收、外部按键等中断触发,MCU 立即唤醒,并执行中断任务。
1.2 Tickless 模式详解
STM32F103xC、STM32F103xD 和 STM32F103xE 增强型产品支持三种低功耗模式,可以在要求低功耗、短启动时间和多种唤醒事件之间达到最佳的平衡。
(1)睡眠模式(Sleep Mode)
只有 CPU 停止,所有外设处于工作状态并可在发生中断/事件时唤醒 CPU。
(2)停机模式(Stop Mode)
在保持 SRAM 和寄存器内容不丢失的情况下,停机模式可以达到最低的电能消耗。在停机模式下,停止所有内部 1.8V 部分的供电,PLL、HSI 的 RC 振荡器和 HSE 晶体振荡器被关闭,调压器可以被置于普通模式或低功耗模式。可以通过任一配置成 EXTI 的信号把微控制器从停机模式中唤醒,EXTI 信号可以是 16 个外部 I/O 口之一、PVD 的输出、RTC闹钟或 USB 的唤醒信号。
(3)待机模式(Standby Mode)
在待机模式下可以达到最低的电能消耗。内部的电压调压器被关闭,因此所有内部1.8V 部分的供电被切断;PLL、HSI 的 RC 振荡器和 HSE 晶体振荡器也被关闭;进入待机模式后,SRAM 和寄存器的内容将消失,但后备寄存器的内容仍然保留,待机电路仍工作。从待机模式退出的条件是:NRST 上的外部复位信号、IWDG 复位、WKUP 引脚上的一个上升边 沿或 RTC 的闹钟到时。
注意:在进入停机或待机模式时,RTC、IWDG 和对应的时钟不会被停止。
主要使用睡眠模式,任何中断或事件都可以唤醒睡眠模式。Tickless 低功耗模式通过调用指令 __WFI 实现睡眠模式
FreeRTOS 系统中的所有其它任务都不在运行时(处于阻塞或挂起),会运行空闲任务。所以想不影响系统运行又降低功耗,可以在空闲任务执行的期间,让 MCU 进入相应的低功耗模式。
由于滴答定时器频繁中断则会影响低功耗,所以 FreeRTOS 的 Tickless 低功耗模式会自动把滴答定时器的中断周期修改为低功耗运行时间,退出低功耗后再补上系统时钟节拍数。
1.3 必要配置宏
#define configUSE_TICKLESS_IDLE 1 // 使能 Tickless 模式,默认为 0
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 // 系统进入 Tickless 模式前,任务必须保持空闲至少多少 Tick
#define configPRE_SLEEP_PROCESSING(x) do_something_before_sleep(x) // 进入低功耗前执行的函数
#define configPOST_SLEEP_PROCESSING(x) do_something_after_sleep(x) // 退出低功耗后执行的函数
1.4 实验
在二值信号量实验案例中,加入低功耗模式,对比功耗结果,观察是否降低功耗(万用表测量电流或使用示波器或功耗分析仪直接测量 MCU 的电流消耗)。
1 ) FreeRTOS 配置
/* Tickless 模式配置 */
#include "user.h"
#define configUSE_TICKLESS_IDLE 1 // 使能 Tickless 模式,默认为 0
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 10 // 系统进入 Tickless 模式前,任务必须保持空闲至少多少 Tick
#define configPRE_SLEEP_PROCESSING(x) PRE_SLEEP_PROCESSING() // 进入低功耗前执行的函数
#define configPOST_SLEEP_PROCESSING(x) POST_SLEEP_PROCESSING() // 退出低功耗后执行的函数
2 ) 低功耗处理函数
/* 进入低功耗前所需要执行的操作 */
void PRE_SLEEP_PROCESSING(void)
{
printf("Entering Sleep Mode\r\n");
__HAL_RCC_GPIOA_CLK_DISABLE();
__HAL_RCC_GPIOB_CLK_DISABLE();
__HAL_RCC_GPIOC_CLK_DISABLE();
__HAL_RCC_GPIOD_CLK_DISABLE();
}
/* 退出低功耗后所需要执行的操作 */
void POST_SLEEP_PROCESSING(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
printf("Waking Up\r\n");
}
3 ) 任务配置
void task1(void *pvParameters)
{
BaseType_t err;
while (1)
{
if (key[0].flag == 1)
{
err = xSemaphoreGive(semaphore_handle);
if (err != pdTRUE)
printf("task1 释放信号量失败\r\n");
led[0].state = !led[0].state;
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, (GPIO_PinState)led[0].state);
key[0].flag = 0;
}
vTaskDelay(500);
}
}
void task2(void *pvParameters)
{
BaseType_t err = 0;
while (1)
{
err = xSemaphoreTake(semaphore_handle, 10000);
if (err != pdTRUE)
{
printf("task2 获取信号量失败\r\n");
}
else
{
printf("task2 成功获取信号量\r\n");
}
vTaskDelay(1000);
}
}
更多推荐



所有评论(0)