当前位置: 首页 > article >正文

FreeRTOS低功耗总结

前言

Cortex-M核的MCU一般支持以下三种低功耗方式:

● 睡眠(Sleep)模式

● 停止(Stop)模式

● 待机(Standby)模式

睡眠模式

 进入睡眠模式有两种指令:WFI(等待中断)和WFE(等待事件),

WFI进入睡眠模式后,任意中断都可唤醒。

WFE进入睡眠模式后,任意唤醒事件都可唤醒

FreeRTOS 使用 WFI 指令进入实现低功耗控制。

停止模式

在特定的条件下执行WFI(等待中断)或者WFE(等待事件)指令,保留SRAM数据,调压器正常工作或者低功耗,大部分时钟关闭。

由外部中断唤醒

待机模式

相比于停止,待机模式的功耗更低。在停止模式基础上,SRAM数据丢失,调压器也关闭,大部分寄存器内容也丢失

由wakeup引进,复位引脚,看门狗或者RTC退出

由于停止/待机模式下软件的正常运行功能都将无法使用,并且其实现需要特定的硬件设计及系统方案,不在本次总结的讨论范围内。

本次总结仅对FreeRTOS系统功能正常运行情况下的低功耗总结,即睡眠模式。

任务运行状态

FreeRTOS作为多任务实时操作系统,开发过程中研发难免需要对软件任务的状态进行查看,FreeRTOS提供了相关的接口用来查询相关状态,

void vTaskList( char *pcWriteBuffer );获取当前所有任务状态
void vTaskGetRunTimeStats( char *pcWriteBuffer );获取当前所有任务占用率

size_t xPortGetFreeHeapSize( void )

获取当前堆剩余字节数

size_t xPortGetMinimumEverFreeHeapSize( void )

获取历史堆最小剩余字节数

任务状态

void vTaskList( char *pcWriteBuffer )

FreeRTOSConfig.h 中 必须定义才能使用vTaskList

#define configUSE_TRACE_FACILITY  1

#define configUSE_STATS_FORMATTING_FUNCTIONS 1

任务占用率

void vTaskGetRunTimeStats( char *pcWriteBuffer )

Abs time : 任务占用的tick数

% time : 占用率

FreeRTOSConfig.h 中 必须定义才能使用vTaskList

#define configGENERATE_RUN_TIME_STATS 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1

configGENERATE_RUN_TIME_STATS 定义为1 后 还需要定义以下宏

统计定时器配置函数  

portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
portGET_RUN_TIME_COUNTER_VALUE()/portALT_GET_RUN_TIME_COUNTER_VALUE(Time)

统计定时器值获取函数  

方式1:高精度定时器

用户实现自己实现高精度定时器

    #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() configureTimeStats()//初始化硬件定时器
    #define portGET_RUN_TIME_COUNTER_VALUE() getRunTime()//获取定时器的计数值

优点:占用率计算准确

缺点:多使用硬件资源,多了个更高频率的定时器,影响性能

方式2:直接使用系统ticks

#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() xTaskGetTickCount() //初始化执行一次
#define portGET_RUN_TIME_COUNTER_VALUE()         xTaskGetTickCount()

优点:不占用额外任何资源,不影响性能

缺点:占用率计算精度不高

堆使用状态

size_t xPortGetFreeHeapSize( void )  //当前堆剩余字节数
size_t xPortGetMinimumEverFreeHeapSize( void ) //获取历史堆最小剩余字节数

关系到 FreeRTOSConfig.h 中的 configTOTAL_HEAP_SIZE     大小设置

#define configTOTAL_HEAP_SIZE    ((size_t)(64 * 1024))

空闲任务


        空闲任务是 FreeRTOS 必不可少的一个任务,因此,空闲任务的优先级肯定是最低的,FreeRTOS 在空闲任务中也会执行一些其他的处理。

         vTaskStartScheduler()启动任务调度器的时候FreeRTOS会自动创建空闲任务,如果某个任务要调用函数 vTaskDelete()删除自身,那么这个任务的资源需要在空闲任务中释放掉。因此,空闲任务也需要一定的时间片执行。

       对于系统而言,一般软件都不会CPU满额运行,大部分时间都处于空闲状态,若用户需要在空闲状态下做一些特殊处理,如低功耗模式、关闭某些外设、降低系统主频等。

FreeRTOS 提供了相关的功能,即钩子函数,可简单理解为回调函数

常见钩子函数如下

FreeRTOSConfig.h 宏定义钩子函数说明

configUSE_IDLE_HOOK

void vApplicationIdleHook(void)

空闲回调

configUSE_TICK_HOOK

void  vApplicationTickHook(void);

tick自加回调
configCHECK_FOR_STACK_OVERFLOW

void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName)

任务栈溢出回调

configUSE_MALLOC_FAILED_HOOK

void vApplicationMallocFailedHook(void)

内存申请失败回调
configUSE_DAEMON_TASK_STARTUP_HOOKvoid vApplicationDaemonTaskStartupHook( void );定时器任务启动回调

定义举例

void vApplicationIdleHook(void)
{
    /* Enter sleep-mode */
    Cy_SysPm_Sleep(CY_SYSPM_WAIT_FOR_INTERRUPT);
}


void vApplicationStackOverflowHook(TaskHandle_t *pxTask, 
                                   signed char *pcTaskName)
{
    /* Remove warning for unused parameters */
    (void)pxTask;
    (void)pcTaskName;
    
    /* Print the error message with task name if debug is enabled in 
       uart_debug.h file */
    DebugPrintf("Error!   : RTOS - stack overflow in %s \r\n", pcTaskName);
    
    /* Halt the CPU */
    CY_ASSERT(0);
}


void vApplicationMallocFailedHook(void)
{
    /* Print the error message if debug is enabled in uart_debug.h file */
    DebugPrintf("Error!   : RTOS - Memory allocation failed \r\n");
    
    /* Halt the CPU */
    CY_ASSERT(0);
}

Idle低功耗

最简单的低功耗方式即:在空闲任务钩子函数中将处理器设置为低功耗模式

void vApplicationIdleHook(void)
{
    /* Enter sleep-mode */
    Cy_SysPm_Sleep(CY_SYSPM_WAIT_FOR_INTERRUPT);
}

几乎所有支持 RTOS 系统的MCU都可以使用这种方法实现低功耗,该方式有以下特点:

大多数场景下,系统时钟是由滴答定时器中断来提供的,系统时钟频率越高,那么滴答定时器中断频率也就越高,但中断MCU从睡眠模式中唤醒, 使得MCU周期性的进入和退出睡眠模式。因此,如果滴答定时器中断频率太高的话会导致大量的能量和时间消耗在进出睡眠模式中,这样导致的结果就是低功耗模式的作用被大大的削弱。

Tickless 低功耗

针对通用低功耗的问题,FreeRTOS提供了另一种低功耗方式,该方式不同MCU的支持可能存在不同,即为 Tickless 模式

当处理器进入空闲任务周期以后就关闭系统节拍中断(滴答定时器中断),只有当其他中断发生或者其他任务需要处理的时候处理器才会被从低功耗模式中唤醒。

由此需要解决两个问题:

问题1:关闭系统节拍中断会导致系统节拍计数器停止,系统tick就会停止

FreeRTOS解决方法:

       FreeRTOS 可通过另一定时器记录下系统节拍中断的关闭时间,当恢复的时候补上这段时间,如果是专用的低功耗处理器,该定时器一般都是用专用的低功耗定时器。

问题2:如何保证下一个要运行的任务能被准确的唤醒。即使处理器进入了低功耗模式,但是中断和应用层任务也要保证及时的响应和处理。 中断不用说,本身就可以唤醒。但是应用层任务就不行了,它无法将处理器从低功耗模式唤醒,无法唤醒就无法运行。

FreeRTOS解决方法:

       计算在进入低功耗模式之前能够获取到还有多长时间切换到下一任务,借助新增的定时器,将其中断周期修改为低功耗运行时间,其中断到来后即可唤醒处理器。

以apollo为例,其官方支持Tickless模式并且提供相应文档说明,但cypress不支持


#define configOVERRIDE_DEFAULT_TICK_CONFIGURATION 1 // Enable non-SysTick based Tick
#define configUSE_TICKLESS_IDLE                   2 // Ambiq specific implementation for Tickless

#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
extern uint32_t am_freertos_sleep(uint32_t);
extern void am_freertos_wakeup(uint32_t);

#define configPRE_SLEEP_PROCESSING( time ) \
    do { \
        (time) = am_freertos_sleep(time); \
    } while (0);

#define configPOST_SLEEP_PROCESSING(time)    am_freertos_wakeup(time)
#endif
/*-----------------------------------------------------------*/
#ifndef AM_PART_APOLLO
#define AM_FREERTOS_USE_STIMER_FOR_TICK
#endif

#ifdef AM_FREERTOS_USE_STIMER_FOR_TICK
#ifdef APOLLO4_FPGA
#define configSTIMER_CLOCK_HZ                     1500000
#define configSTIMER_CLOCK                        AM_HAL_STIMER_HFRC_6MHZ
#else
#define configSTIMER_CLOCK_HZ                     32768
#define configSTIMER_CLOCK                        AM_HAL_STIMER_XTAL_32KHZ
#endif
#else // Use CTimer
#define configCTIMER_NUM                          3
#define configCTIMER_CLOCK_HZ                     32768
#define configCTIMER_CLOCK                        AM_HAL_CTIMER_XT_32_768KHZ
#endif

两种模式对比分析

图中有三个任务,它们分别为一个空闲任务(Idle),两个用户任务(Task1 和 Task2), 其中空闲任务一共有运行了三次,分别为(1) 、(2) 、(3),其中 T1 到 T12 是 12 个时刻,下面我们分别从这两种低功耗的实现方法去分析一下整个过程。

1、Idle低功耗模式

       如果使用通用低功耗模式的话每个滴答定时器中断都会将处理器从低功耗模式中唤醒,以 (1)为例,再 T2 时刻处理器从低功耗模式中唤醒,但是接下来由于没有就绪的其他任务所以处理器又再一次进入低功耗模式。T2、T3 和 T4 这三个时刻都一样,反复的进入低功耗、退出低功耗,最理想的情况应该是从 T1 时刻就进入低功耗,然后在 T5 时刻退出。

      在(2)中空闲任务只工作了两个时钟节拍,但是也执行了低功耗模式的进入和退出,显然这个意义不大,因为进出低功耗也是需要时间的。

      (3)中空闲任务在 T12 时刻被某个外部中断唤醒,中断的具体处理过程在任务 2(使用信号量实现中断与任务之间的同步)。

2 、 Tickless 低功耗模式

     在(1)中的 T1 时刻处理器进入低功耗模式,在 T5 时刻退出低功耗模式。相比通用低功耗模式少了 3 次进出低功耗模式的操作。

   在(2)中由于空闲任务只运行了两个时钟节拍, 所以就没必要进入低功耗模式。说明在 Tickless  模式中只有空闲任务要运行时间的超过某个最小阈值的时候才会进入低功耗模式,此阈值通过 configEXPECTED_IDLE_TIME_BEFORE_SLEEP 来设置,上一章已经讲过了。

     (3)中的情况和通用低功耗模式一样。

总结

    1、可以看出相对与通用低功耗模式, Tickless 模式更加合理有效,所以如果有低功耗设计需求的话大家尽量使用 Tickless 模式。

    2、几乎所有支持FreeRTOS的MCU都支持Idle低功耗模式,但不一定支持Tickless 模式,Tickless 模式的支持需要依赖于MCU厂家对该功能的支持,具体实现方式一般在对应的FreeRTOS的port.c文件中。

参考资料:

FreeRTOS的低功耗Tickless模式与空闲函数_freertos 空闲任务进入低功耗模式-CSDN博客

FreeRTOS_Reference_Manual_V10.0.0.pdf

FreeRTOS™ - FreeRTOS™


http://www.kler.cn/a/545866.html

相关文章:

  • Azure从0到1
  • 蓝桥与力扣刷题(108 将有序数组转换成二叉搜索树)
  • TCP文件传输
  • 人工智能任务21-飞蛾火焰优化算法(MFO)在深度学习中的应用
  • 如何做好抖音小视频推广呢?
  • 九.Spring Boot使用 ShardingSphere + MyBatis + Druid 进行分库分表
  • TikTok成功打破传统媒体壁垒,用户涌入平台创作
  • 3D数字化技术:重塑“人货场”,开启营销新纪元
  • 华为云+硅基流动使用Chatbox接入DeepSeek-R1满血版671B
  • Docker 镜像的构建与管理(二)
  • VM ubuntu20.04虚拟机找不到可移动设备怎么解决
  • 智能手表表带圆孔同心度检测
  • 【Qt】:概述(下载安装、认识 QT Creator)
  • 第1章 信息化发展(一)
  • 达梦分布式集群DPC_架构详解_yxy
  • 朝天椒USB服务器解决前置机U盾虚拟机远程连接
  • web自动化笔记(二)
  • MySQL 定时备份与恢复
  • snort3.0 获取注册规则(19000多条)
  • DeepSeek 开放平台无法充值使用 改用其他中转平台API调用DeepSeek-chat模型方法