解析 FreeRTOS Tickless 低功耗模式:原理、配置与实验操作

1. FreeRTOS 的低功耗模式

1.1 Tickless 低功耗模式简介

​ Tickless 模式是 FreeRTOS 为了降低功耗而提供的一种机制。在默认情况下,FreeRTOS 采用固定间隔的系统滴答(Tick)中断 来管理任务调度,即 每隔固定时间(如 1ms)触发一次中断,即使系统处于空闲状态,Tick 也会继续触发,导致 CPU 无法进入深度低功耗模式

Tickless 模式的核心思想是:

当系统进入空闲任务时,FreeRTOS 关闭系统滴答定时器,进入深度低功耗模式,并在下一个需要唤醒的时间点自动唤醒 CPU,重新启用系统滴答。

Tickless 模式的工作流程:

当启用 Tickless 模式后,FreeRTOS 的 系统滴答定时器(SysTick) 行为如下:

  1. 当系统检测到所有任务都进入阻塞(空闲)状态
    • FreeRTOS 计算下一个任务准备就绪的时间(例如 100ms)。
    • 关闭滴答定时器(SysTick),减少 CPU 运行次数,降低功耗。
    • MCU 进入 低功耗模式
  2. 当定时器到达预定时间
    • SysTick 或 RTC 唤醒 CPU
    • FreeRTOS 恢复滴答定时器,继续任务调度。
  3. 如果中断发生
    • 例如串口接收、外部按键等中断触发,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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值