FreeRTOS学习笔记9——低功耗

FreeRTOS———低功耗

本节没有进行翻译,如有需要请留言。

TOC

Power Saving Introduction

It is common to reduce the power consumed by the microcontroller on which FreeRTOS is running by using the Idle task hook to place the microcontroller into a low power state. The power saving that can be achieved by this simple method is limited by the necessity to periodically exit and then re-enter the low power state to process tick interrupts. Further, if the frequency of the tick interrupt is too high, the energy and time consumed entering and then exiting a low power state for every tick will outweigh any potential power saving gains for all but the lightest power saving modes.

The FreeRTOS tickless idle mode stops the periodic tick interrupt during idle periods (periods when there are no application tasks that are able to execute), then makes a correcting adjustment to the RTOS tick count value when the tick interrupt is restarted.

Stopping the tick interrupt allows the microcontroller to remain in a deep power saving state until either an interrupt occurs, or it is time for the RTOS kernel to transition a task into the Ready state.

The portSUPPRESS_TICKS_AND_SLEEP() Macro

portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime )

Built in tickless idle functionality is enabled by defining configUSE_TICKLESS_IDLE as 1 in FreeRTOSConfig.h (for ports that support this feature). User defined tickless idle functionality can be provided for any FreeRTOS port (including those that include a built in implementation) by defining configUSE_TICKLESS_IDLE to 2 in FreeRTOSConfig.h.

When the tickless idle functionality is enabled the kernel will call the portSUPPRESS_TICKS_AND_SLEEP() macro when the following two conditions are both true:

  1. The Idle task is the only task able to run because all the application tasks are either in the Blocked state or in the Suspended state.
  2. At least n further complete tick periods will pass before the kernel is due to transition an application task out of the Blocked state, where n is set by the configEXPECTED_IDLE_TIME_BEFORE_SLEEP definition in FreeRTOSConfig.h.

The value of portSUPPRESS_TICKS_AND_SLEEP()’s single parameter equals the total number of tick periods before a task is due to be moved into the Ready state. The parameter value is therefore the time the microcontroller can safely remain in a deep sleep state, with the tick interrupt stopped (suppressed).

Note: If eTaskConfirmSleepModeStatus() returns eNoTasksWaitingTimeout when it is called from within portSUPPRESS_TICKS_AND_SLEEP() then the microcontroller can remain in a deep sleep state indefinitely. eTaskConfirmSleepModeStatus() will only return eNoTasksWaitingTimeout when the following conditions are true:

  1. Software timers are not being used, so the scheduler is not due to execute a timer callback function at any time in the future.
  2. All the application tasks are either in the Suspended state, or in the Blocked state with an infinite timeout (a timeout value of portMAX_DELAY), so the scheduler is not due to transition a task out of the Blocked state at any fixed time in the future.

To avoid race conditions the RTOS scheduler is suspended before portSUPPRESS_TICKS_AND_SLEEP() is called, and resumed when portSUPPRESS_TICKS_AND_SLEEP() completes. This ensures application tasks cannot execute between the microcontroller exiting its low power state and portSUPPRESS_TICKS_AND_SLEEP() completing its execution. Further, it is necessary for the portSUPPRESS_TICKS_AND_SLEEP() function to create a small critical section between the tick source being stopped and the microcontroller entering the sleep state. eTaskConfirmSleepModeStatus() should be called from this critical section.

All GCC, IAR and Keil ARM Cortex-M3 and ARM Cortex-M4 ports now provide a default portSUPPRESS_TICKS_AND_SLEEP() implementation. Important information on using the ARM Cortex-M implementation is provided on the Low Power Features For ARM Cortex-M MCUs page.

Default implementations will be added to other FreeRTOS ports over time. In the mean time, the hooks described below can be used to add tickless functionality to any port.

Implementing portSUPPRESS_TICKS_AND_SLEEP()

If the FreeRTOS port in use does not provide a default implementation of portSUPPRESS_TICKS_AND_SLEEP(), then the application writer can provide their own implementation by defining portSUPPRESS_TICKS_AND_SLEEP() in FreeRTOSConfig.h.

If the FreeRTOS port in use does provide a default implementation of portSUPPRESS_TICKS_AND_SLEEP(), then the application writer can override the default implementation by defining portSUPPRESS_TICKS_AND_SLEEP() in FreeRTOSConfig.h.

The following source code is an example of how portSUPPRESS_TICKS_AND_SLEEP() might be implemented by an application writer. The example is basic, and will introduce some slippage between the time maintained by the kernel and calendar time. Official FreeRTOS versions attempt to remove any slippage (as far as is possible) by providing a more intricate implementation.

Of the functions calls shown in the example, only vTaskStepTick() and eTaskConfirmSleepModeStatus() are part of the FreeRTOS API. The other functions are specific to the clocks and power saving modes available on the hardware in use, and as such, must be provided by the application writer.


/* First define the portSUPPRESS_TICKS_AND_SLEEP() macro.  The parameter is the
time, in ticks, until the kernel next needs to execute. */
#define portSUPPRESS_TICKS_AND_SLEEP( xIdleTime ) vApplicationSleep( xIdleTime )

/* Define the function that is called by portSUPPRESS_TICKS_AND_SLEEP(). */
void vApplicationSleep( TickType_t xExpectedIdleTime )
{
unsigned long ulLowPowerTimeBeforeSleep, ulLowPowerTimeAfterSleep;
eSleepModeStatus eSleepStatus;

    /* Read the current time from a time source that will remain operational
    while the microcontroller is in a low power state. */
    ulLowPowerTimeBeforeSleep = ulGetExternalTime();

    /* Stop the timer that is generating the tick interrupt. */
    prvStopTickInterruptTimer();

    /* Enter a critical section that will not effect interrupts bringing the MCU
    out of sleep mode. */
    disable_interrupts();

    /* Ensure it is still ok to enter the sleep mode. */
    eSleepStatus = eTaskConfirmSleepModeStatus();

    if( eSleepStatus == eAbortSleep )
    {
        /* A task has been moved out of the Blocked state since this macro was
        executed, or a context siwth is being held pending.  Do not enter a
        sleep state.  Restart the tick and exit the critical section. */
        prvStartTickInterruptTimer();
        enable_interrupts();
    }
    else
    {
        if( eSleepStatus == eNoTasksWaitingTimeout )
        {
            /* It is not necessary to configure an interrupt to bring the
            microcontroller out of its low power state at a fixed time in the
            future. */
            prvSleep();
        }
        else
        {
            /* Configure an interrupt to bring the microcontroller out of its low
            power state at the time the kernel next needs to execute.  The
            interrupt must be generated from a source that remains operational
            when the microcontroller is in a low power state. */
            vSetWakeTimeInterrupt( xExpectedIdleTime );

            /* Enter the low power state. */
            prvSleep();

            /* Determine how long the microcontroller was actually in a low power
            state for, which will be less than xExpectedIdleTime if the
            microcontroller was brought out of low power mode by an interrupt
            other than that configured by the vSetWakeTimeInterrupt() call.
            Note that the scheduler is suspended before
            portSUPPRESS_TICKS_AND_SLEEP() is called, and resumed when
            portSUPPRESS_TICKS_AND_SLEEP() returns.  Therefore no other tasks will
            execute until this function completes. */
            ulLowPowerTimeAfterSleep = ulGetExternalTime();

            /* Correct the kernels tick count to account for the time the
            microcontroller spent in its low power state. */
            vTaskStepTick( ulLowPowerTimeAfterSleep - ulLowPowerTimeBeforeSleep );
        }

        /* Exit the critical section - it might be possible to do this immediately
        after the prvSleep() calls. */
        enable_interrupts();

        /* Restart the timer that is generating the tick interrupt. */
        prvStartTickInterruptTimer();
    }
}

参考资料

FreeRTOS-quick-start-guide
Mastering_the_FreeRTOS_RealTime_Kernel-A_Hand.pdf





posted @ 2021-01-06 13:55  JerryZheng2020  阅读(373)  评论(0编辑  收藏  举报