14. FreeRTOS的Tickless低功耗模式
一、FreeRTOS的低功耗模式简介
FreeRTOS 的低功耗 Tickless 模式是基于硬件层面的相应低功耗模式实现的。
在整个系统的运行过程中,其实大部分的时间是在执行空闲任务的,而空闲任务之所及叫做空闲任务,是因为空闲任务是在系统中的所有其它都阻塞或被挂起时才运行的,因此可以在本该空闲任务执行的期间,让 MCU 进入相应的低功耗模式,接着在其他任务因被解除阻塞或其他原因,而准备运行的时候,让 MCU 退出相应的低功耗模式,去执行相应的任务。
在以上这一过程中,主要的难点在于,MCU 进入相应的低功耗模式后,如何判断有除空闲任务外的其他任务就绪,并退出相应的空闲模式去执行就绪任务,也就是如何计算 MCU 进入相应低功耗模式的时间,而 FreeRTOS 的低功耗 Tickless 模式机制已经处理好了这个问题。
在调用 vTaskStartScheduler() 函数开启任务调度器后,会通过 prvCreateIdleTasks() 函数创建空间任务,然后会调用 portTASK_FUNCTION() 任务函数。
static portTASK_FUNCTION( prvIdleTask, pvParameters )
{
/* Stop warnings. */
( void ) pvParameters;
portALLOCATE_SECURE_CONTEXT( configMINIMAL_SECURE_STACK_SIZE );
#if ( configNUMBER_OF_CORES > 1 )
{
taskYIELD();
}
#endif /* #if ( configNUMBER_OF_CORES > 1 ) */
for( ; configCONTROL_INFINITE_LOOP(); )
{
prvCheckTasksWaitingTermination();
#if ( configUSE_PREEMPTION == 0 )
{
taskYIELD();
}
#endif /* configUSE_PREEMPTION */
#if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) )
{
if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) configNUMBER_OF_CORES )
{
taskYIELD();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */
#if ( configUSE_IDLE_HOOK == 1 )
{
vApplicationIdleHook();
}
#endif /* configUSE_IDLE_HOOK */
#if ( configUSE_TICKLESS_IDLE != 0 ) // 此宏用于启用 FreeRTOS 低功耗 Tickless 模式
{
TickType_t xExpectedIdleTime;
// 计算进入相应低功耗模式的时长,本次计算的结果并不一定准确,因为可能会收到任务调度器的影响
xExpectedIdleTime = prvGetExpectedIdleTime();
// 如果时长大于 configEXPECTED_IDLE_TIME_BEFORE_SLEEP,才进入相应的低功耗模式
if( xExpectedIdleTime >= ( TickType_t ) configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
vTaskSuspendAll(); // 挂起任务调度器
{
configASSERT( xNextTaskUnblockTime >= xTickCount );
// 重新计算进入相应低功耗模式的时长,此时任务调度器已经被挂起, 因此本次的计算结果就是 MCU 进入相应低功耗模式的时长
xExpectedIdleTime = prvGetExpectedIdleTime();
// 如果不希望进入低功耗模式,可以定义此宏将 xExpectedIdleTime 设置为 0
configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime );
// 如果时长大于 configEXPECTED_IDLE_TIME_BEFORE_SLEEP,才进入相应的低功耗模式
if( xExpectedIdleTime >= ( TickType_t ) configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
traceLOW_POWER_IDLE_BEGIN(); // 用于调试
// 此宏就是用来让 MCU 进入相应的低功耗模式的,传入 MCU 需要进入相应低功耗模式的时长
portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
traceLOW_POWER_IDLE_END(); // 用于调试
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
( void ) xTaskResumeAll(); // 恢复任务调度器
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_TICKLESS_IDLE */
#if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_PASSIVE_IDLE_HOOK == 1 ) )
{
vApplicationPassiveIdleHook();
}
#endif /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_PASSIVE_IDLE_HOOK == 1 ) ) */
}
}
从上面的代码中可以看出,FreeRTOS 首先会调用函数 prvGetExpectedIdleTime() 计算 MCU 需要进入相应低功耗模式的时长,只有当这个时长大于宏定义 configEXPECTED_IDLE_TIME_BEFORE_SLEEP 定义的值时,才会让 MCU 进入相应的低功耗模式。接下来还会第二次计算 MCU 需要进入相应低功耗模式的时长,这是因为第一次计算的时长可能会收到任务调度器的影响,并不准确,第二次计算的时长是在挂起了任务调度器之后计算的,最终会调用函数 portSUPPRESS_TICKS_AND_SLEEP() 使得 MCU 进入相应的低功耗模式。
二、Tickless模式相关配置项
#define configUSE_TICKLESS_IDLE 0 // 1: 使能tickless低功耗模式, 默认: 0
此宏用于 使能低功耗 Tickless 模式,当此宏定义为 1 时,系统会在进入空闲任务期间进入相应的低功耗模式大于 configEXPECTED_IDLE_TIME_BEFORE_SLEEP 的时长。
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2
此宏用于 定义系统进入相应低功耗模式的最短时长,如果系统在进入相应低功耗模式前,计算出系统将进入相应低功耗的时长小于 configEXPECTED_IDLE_TIME_BEFORE_SLEEP 定义的最小时长,则系统不进入相应的低功耗模式,要注意的是,此宏的值不能小于 2。
在 FreeRTOSConfig.h 文件中并没有定义宏 configEXPECTED_IDLE_TIME_BEFORE_SLEEP,这是因为,此宏在 FreeRTOS.h 文件中已经有默认定义了。
#define configPRE_SLEEP_PROCESSING( x ) PRE_SLEEP_PORCESSING(void)
void PRE_SLEEP_PORCESSING(void)
{
// 进入低功耗前要执行的操作
}
此宏用于 定义一些需要在系统进入相应低功耗模式前执行的事务,例如可以在进入低功耗模式前关闭一些 MCU 片上外设的时钟,以达到降低功耗的目的。
#define configPOST_SLEEP_PROCESSING( x ) POST_SLEEP_PROCESSING(void)
void POST_SLEEP_PROCESSING(void)
{
// 退出低功耗后要执行的操作
}
此宏用于 定义一些需要在系统退出相应低功耗模式后执行的事务,例如开启在系统在进入相应低功耗模式前关闭的 MCU 片上外设的时钟,以是系统能够正常运行。

浙公网安备 33010602011771号