FreeRTOS 低功耗 Tickless 模式

举报
追zhui 发表于 2025/11/03 19:28:15 2025/11/03
【摘要】 一、低功耗模式简介FreeRTOS 的 Tickless 模式是一种特殊的运行模式,用于最小化系统的时钟中断频率,以降低功耗。在 Tickless 模式下,系统只在有需要时才会启动时钟中断,而在无任务要运行时则完全进入休眠状态,从而降低功耗。在滴答中断重启时,会对 RTOS 滴答计数值进行校正调整。Tickless模式的实现方式通常依赖于微控制器的硬件特性,尤其是低功耗定时器或实时时钟单元...

一、低功耗模式简介

FreeRTOS 的 Tickless 模式是一种特殊的运行模式,用于最小化系统的时钟中断频率,以降低功耗。在 Tickless 模式下,系统只在有需要时才会启动时钟中断,而在无任务要运行时则完全进入休眠状态,从而降低功耗。在滴答中断重启时,会对 RTOS 滴答计数值进行校正调整。

Tickless模式的实现方式通常依赖于微控制器的硬件特性,尤其是低功耗定时器或实时时钟单元。以下是 Tickless 模式的一般工作原理:

  • 空闲任务检测:FreeRTOS 会通过空闲任务(Idle Task)来检测系统是否有任务需要执行。如果没有任务需要执行,系统可以进入休眠状态。
  • 时钟中断:当有任务需要执行时,系统会启动时钟中断,唤醒处理器。
  • 时钟中断处理:在时钟中断处理函数中,FreeRTOS 将检查任务的状态并决定是否继续执行。
  • 休眠状态:如果没有任务需要执行,系统可以进入休眠状态,关闭时钟中断。在休眠状态下,处理器可以进入更低功耗的模式。
  • 任务唤醒:当有任务需要执行时,系统会再次启动时钟中断,唤醒处理器,然后执行相应的任务。

在 Tickless 模式下,系统的时钟中断频率明显降低,从而降低了系统的平均功耗。Tickless 模式适用于那些对功耗要求较高、需要长时间运行在低功耗状态的嵌入式系统。比如:电池驱动设备、物联网(IoT)设备、低功耗传感器节点、无线通信模块等。

二、Tickless模式详解

STM32F103xC、STM32F103xD和STM32F103xE增强型产品支持三种低功耗模式,可以在要求低功耗、短启动时间和多种唤醒事件之间达到最佳的平衡。

  1. 睡眠模式(Sleep Mode)

只有CPU停止**,所有外设处于工作状态并可在发生中断/事件时唤醒CPU。**

  1. 停机模式(Stop Mode)

在保持SRAM和寄存器内容不丢失的情况下,停机模式可以达到最低的电能消耗。在停机模式下,停止所有内部1.8V部分的供电,PLL、HSI的RC振荡器和HSE晶体振荡器被关闭,调压器可以被置于普通模式或低功耗模式。可以通过任一配置成EXTI的信号把微控制器从停机模式中唤醒,EXTI信号可以是16个外部I/O 口之一、PVD的输出、RTC闹钟或USB的唤醒信号。

  1. 待机模式(Standby Mode)

在待机模式下可以达到最低的电能消耗。内部的电压调压器被关闭,因此所有内部1.8V部分的供电被切断;PLL、HSI的RC振荡器和HSE晶体振荡器也被关闭;进入待机模式后,SRAM和寄存器的内容将消失,但后备寄存器的内容仍然保留,待机电路仍工作。从待机模式退出的条件是:NRST上的外部复位信号、IWDG复位、WKUP引脚上的一个上升边 沿或RTC的闹钟到时。

注意:在进入停机或待机模式时,RTC、IWDG和对应的时钟不会被停止。

主要使用睡眠模式,任何中断或事件都可以唤醒睡眠模式。Tickless低功耗模式通过调用指令 __WFI 实现睡眠模式

FreeRTOS系统中的所有其它任务都不在运行时(处于阻塞或挂起),会运行空闲任务。所以想不影响系统运行又降低功耗,可以在空闲任务执行的期间,让MCU 进入相应的低功耗模式。

由于滴答定时器频繁中断则会影响低功耗,所以FreeRTOS的Tickless低功耗模式会自动把滴答定时器的中断周期修改为低功耗运行时间,退出低功耗后再补上系统时钟节拍数。

  1. Tickless模式相关配置项(掌握)
配置项 说明
configUSE_TICKLESS_IDLE 使能低功耗 Tickless 模式,默认0
configEXPECTED_IDLE_TIME_BEFORE_SLEEP 系统进入相应低功耗模式的最短时长,默认2
configPRE_SLEEP_PROCESSING(x) 在系统进入低功耗模式前执行的事务,比如关闭外设时钟
configPOST_SLEEP_PROCESSING(x) 系统退出低功耗模式后执行的事务,比如开启之前关闭的外设时钟

三、Tickless低功耗模式实验

  1. 实验目标

学习使用 FreeRTOS 中的Tickless低功耗模式

  1. FreeRTOSConfig.h代码清单

#define configUSE_TICKLESS_IDLE 1

#include “freertos_demo.h”

#define configPRE_SLEEP_PROCESSING( x ) PRE_SLEEP_PROCESSING()

#define configPOST_SLEEP_PROCESSING( x ) POST_SLEEP_PROCESSING()

  1. freertos_demo.c代码清单

引入信号量头文件

#include “semphr.h”

  1. 低功耗处理函数

/* 进入低功耗前所需要执行的操作 */

void PRE_SLEEP_PROCESSING(void)

{

__HAL_RCC_GPIOA_CLK_DISABLE();

__HAL_RCC_GPIOB_CLK_DISABLE();

__HAL_RCC_GPIOC_CLK_DISABLE();

__HAL_RCC_GPIOD_CLK_DISABLE();

__HAL_RCC_GPIOE_CLK_DISABLE();

__HAL_RCC_GPIOF_CLK_DISABLE();

__HAL_RCC_GPIOG_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();

__HAL_RCC_GPIOE_CLK_ENABLE();

__HAL_RCC_GPIOF_CLK_ENABLE();

__HAL_RCC_GPIOG_CLK_ENABLE();

}

  1. 任务配置

/* 启动任务函数 */

#define START_TASK_PRIORITY 1

#define START_TASK_STACK_DEPTH 128

TaskHandle_t start_task_handler;

void Start_Task(void *pvParameters);

/* Task1 任务 配置 */

#define TASK1_PRIORITY 2

#define TASK1_STACK_DEPTH 128

TaskHandle_t task1_handler;

void Task1(void *pvParameters);

/* Task2 任务 配置 */

#define TASK2_PRIORITY 3

#define TASK2_STACK_DEPTH 128

TaskHandle_t task2_handler;

void Task2(void *pvParameters);

  1. 入口函数

QueueHandle_t semphore_handle;

/**

* @description: FreeRTOS入口函数:创建任务函数并开始调度

* @return {*}

*/

void FreeRTOS_Start(void)

{

semphore_handle = xSemaphoreCreateBinary();

if (semphore_handle != NULL)

{

​ printf(“二值信号量创建成功\r\n”);

}

xTaskCreate((TaskFunction_t)Start_Task,

​ (char *)“Start_Task”,

​ (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,

​ (void *)NULL,

​ (UBaseType_t)START_TASK_PRIORITY,

​ (TaskHandle_t *)&start_task_handler);

vTaskStartScheduler();

}

  1. 初始任务函数

void Start_Task(void *pvParameters)

{

taskENTER_CRITICAL(); /* 进入临界区 */

xTaskCreate((TaskFunction_t)Task1,

​ (char *)“Task1”,

​ (configSTACK_DEPTH_TYPE)TASK1_STACK_DEPTH,

​ (void *)NULL,

​ (UBaseType_t)TASK1_PRIORITY,

​ (TaskHandle_t *)&task1_handler);

xTaskCreate((TaskFunction_t)Task2,

​ (char *)“Task2”,

​ (configSTACK_DEPTH_TYPE)TASK2_STACK_DEPTH,

​ (void *)NULL,

​ (UBaseType_t)TASK2_PRIORITY,

​ (TaskHandle_t *)&task2_handler);

vTaskDelete(NULL);

taskEXIT_CRITICAL(); /* 退出临界区 */

}

  1. task1任务函数

/**

* @description: 释放二值信号量

* @param {void *} pvParameters

* @return {*}

*/

void Task1(void *pvParameters)

{

uint8_t key = 0;

BaseType_t err;

while (1)

{

​ key = Key_Detect();

​ if (key == KEY1_PRESS)

​ {

​ if (semphore_handle != NULL)

​ {

​ err = xSemaphoreGive(semphore_handle);

​ if (err == pdPASS)

​ {

​ printf(“信号量释放成功\r\n”);

​ }

​ else

​ printf(“信号量释放失败\r\n”);

​ }

​ }

​ vTaskDelay(10);

}

}

  1. task2任务函数

/**

* @description: 获取二值信号量

* @param {void *} pvParameters

* @return {*}

*/

void Task2(void *pvParameters)

{

uint32_t i = 0;

BaseType_t err;

while (1)

{

​ /* 一直等待获取信号量 */

​ err = xSemaphoreTake(semphore_handle, portMAX_DELAY);

​ if (err == pdTRUE)

​ {

​ printf(“获取信号量成功\r\n”);

​ }

​ else

​ {

​ printf(“已超时%d\r\n”, ++i);

​ }

}

}

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。