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 片上外设的时钟,以是系统能够正常运行。

posted @ 2024-03-25 18:05  星光映梦  阅读(461)  评论(0)    收藏  举报