FreeRTOS Kernel Development Manual – Low Power Support

Word count 2266, reading time approximately 12 minutes

11 Low Power Support

11.1 Power Saving Introduction

FreeRTOS provides a simple method to enter low power mode using the IDLE task hook and tickless idle mode.

Typically, using the IDLE task hook allows the microcontroller running FreeRTOS to enter a low power state, reducing its power consumption. The energy savings achievable with this method are limited by the need to periodically exit and re-enter the low power state to handle tick interrupts. Furthermore, if the tick interrupt frequency is too high (waking from idle mode too frequently), the energy and time consumed for each tick entering and exiting the low power state will exceed all potential energy savings except for the lightest power-saving mode.

FreeRTOS supports low power states, allowing the microcontroller to periodically enter and exit low power states. The FreeRTOS tickless idle mode stops periodic tick interrupts during idle periods (when there are no executable application tasks), allowing the MCU to remain in a deep power-saving state until an interrupt occurs or the RTOS kernel transitions a task to the ready state. Then, when the tick interrupt restarts, it corrects the RTOS tick count value. The principle of FreeRTOS tickless mode is to allow the MCU to enter low power mode while executing idle tasks to save system power.

11.2 FreeRTOS Sleep Modes

FreeRTOS supports three sleep modes:

  1. 1. <span>eAbortSleep</span> – This mode indicates that a task is ready, context switching is suspended, or a tick interrupt has occurred, but it has been in a suspended state since the scheduler paused. It signals the RTOS to abort and enter sleep mode.
  2. 2. <span>eStandardSleep</span> – This mode allows entering sleep mode, which does not exceed the expected idle time.
  3. 3. <span>eNoTasksWaitingTimeout</span> – This mode is entered when no tasks are waiting for a timeout, allowing safe entry into sleep mode, and can only be exited by an external interrupt or reset.

11.3 Functions and Enabling Built-in Tickless Idle Feature

To enable the built-in tickless idle feature (for supported port versions), define <span>configUSE_TICKLESS_IDLE</span> as 1 in FreeRTOSConfig.h. User-defined tickless idle functionality can be provided for any FreeRTOS port version (including those with built-in implementations) by defining <span>configUSE_TICKLESS_IDLE</span> as 2 in FreeRTOSConfig.h.

Once the tickless idle feature is enabled, the kernel will call the <span>portSUPPRESS_TICKS_AND_SLEEP()</span> macro when the following two conditions are met:

  1. 1. The idle task is the only task that can run because all application tasks are in a blocked or suspended state.
  2. 2. At least n complete tick cycles must pass before the kernel transitions an application task out of the blocked state, where n is defined by <span>configEXPECTED_IDLE_TIME_BEFORE_SLEEP</span> in FreeRTOSConfig.h.

11.3.1 portSUPPRESS_TICKS_AND_SLEEP() Macro

portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime )

Listing 11.1 Prototype of the portSUPPRESS_TICKS_AND_SLEEP macro

<span> </span><code><span>portSUPPRESS_TICKS_AND_SLEEP()</span> function’s <span>xExpectedIdleTime</span> parameter value equals the total tick cycles before the task enters the ready state. Therefore, this parameter value is the time the microcontroller can safely remain in deep sleep mode while suppressing tick interrupts.

11.3.2 vPortSuppressTicksAndSleep Function

<span> </span><code><span>vPortSuppressTicksAndSleep()</span> function can be used to implement tickless mode. This function is weakly defined in the FreeRTOS Cortex-M port layer, and application writers can override this function.

void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime );

Listing 11.2 Prototype of the vPortSuppressTicksAndSleep API function

11.3.3 eTaskConfirmSleepModeStatus Function

<span> </span><code><span>eTaskConfirmSleepModeStatus</span> API function returns the sleep mode status to determine whether it can continue sleeping and whether it can sleep indefinitely. This function is only available when <span>configUSE_TICKLESS_IDLE</span> is set to 1.

eSleepModeStatus eTaskConfirmSleepModeStatus( void );

Listing 11.3 Prototype of the eTaskConfirmSleepModeStatus API function

If <span>eTaskConfirmSleepModeStatus()</span> returns <span>eNoTasksWaitingTimeout</span> when called within <span>portSUPPRESS_TICKS_AND_SLEEP()</span>, the microcontroller can remain in deep sleep mode indefinitely.<span>eTaskConfirmSleepModeStatus()</span> will only return <span>eNoTasksWaitingTimeout</span> under the following conditions:

  • • No software timers are used, so the scheduler will not execute timer callback functions at any future time.
  • • All application tasks are either in Suspended state or Blocked state (timeout value is <span>portMAX_DELAY</span>), so the scheduler will not transition tasks from Blocked state at any fixed time in the future.

To avoid race conditions, the FreeRTOS scheduler will suspend before calling <span>portSUPPRESS_TICKS_AND_SLEEP()</span> and resume when <span>portSUPPRESS_TICKS_AND_SLEEP()</span> completes. This ensures that no application tasks can execute between the microcontroller exiting low power state and the completion of <span>portSUPPRESS_TICKS_AND_SLEEP()</span>. Additionally, the <span>portSUPPRESS_TICKS_AND_SLEEP()</span> function requires creating a small critical section between stopping the timer and entering sleep mode to ensure that sleep mode can be entered. <span>eTaskConfirmSleepModeStatus()</span> should be called from this critical section.

Furthermore, FreeRTOS also provides two additional interface functions defined in FreeRTOSConfig.h. These two macros allow application writers to add extra steps before and after the MCU enters low power state.

11.3.4 configPRE_SLEEP_PROCESSING Configuration

configPRE_SLEEP_PROCESSING( xExpectedIdleTime )

Listing 11.4 Prototype of the configPRE_SLEEP_PROCESSING macro

Users must call <span>configPRE_SLEEP_PROCESSING()</span> to configure system parameters to reduce system power consumption before putting the MCU into low power mode, such as turning off other peripheral clocks, reducing system frequency, etc.

11.3.5 configPOST_SLEEP_PROCESSING Configuration

configPOST_SLEEP_PROCESSING( xExpectedIdleTime )

Listing 11.5 Prototype of the configPOST_SLEEP_PROCESSING macro

After exiting low power mode, users should call <span>configPOST_SLEEP_PROCESSING()</span> function to restore the system main frequency and peripheral functionality.

11.4 Implementing portSUPPRESS_TICKS_AND_SLEEP() Macro

If the FreeRTOS port version being used does not provide a default implementation of <span>portSUPPRESS_TICKS_AND_SLEEP()</span>, application writers can provide their own implementation by defining <span>portSUPPRESS_TICKS_AND_SLEEP()</span> in FreeRTOSConfig.h. If the FreeRTOS port version being used provides a default implementation of <span>portSUPPRESS_TICKS_AND_SLEEP()</span>, application writers can override the default implementation by defining <span>portSUPPRESS_TICKS_AND_SLEEP()</span> in FreeRTOSConfig.h.

The following source code is an example of how application writers can implement <span>portSUPPRESS_TICKS_AND_SLEEP()</span>. This example is quite simple and will lead to some discrepancies between the time maintained by the kernel and the calendar time. In the function calls shown in the example, only <span>vTaskStepTick()</span> and <span>eTaskConfirmSleepModeStatus()</span> belong to the FreeRTOS API. Other functions are specific to the clocks and power-saving modes available on the hardware being used, and 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 switch 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();
    }
}

Listing 11.6 Example of user-defined portSUPPRESS_TICKS_AND_SLEEP() implementation

11.5 Idle Task Hook Function

The Idle Task can optionally call an application-defined hook (or callback) function – the idle hook. The idle task has the lowest priority, so this idle hook function will only execute when there are no higher priority tasks to run. This makes the idle hook function an ideal choice for putting the processor into low power state – it can automatically save power when there is no processing to be done. The idle hook will only be called if <span>configUSE_IDLE_HOOK</span> is set to 1 in the FreeRTOSConfig.h file.

void vApplicationIdleHook( void );

Listing 11.7 Prototype of the vApplicationIdleHook API function

The idle hook will be repeatedly called as long as the idle task is running. Importantly, the idle hook function must not call any API functions that may cause it to block. Additionally, if the application uses the <span>vTaskDelete()</span> API function, the idle task hook must allow the idle task to return periodically, as the idle task is responsible for cleaning up resources allocated by the RTOS kernel to deleted tasks.

Original source: https://github.com/FreeRTOS/FreeRTOS-Kernel-Book/blob/main/ch11.md

Leave a Comment