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.
<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.
<span>eStandardSleep</span>– This mode allows entering sleep mode, which does not exceed the expected idle time. - 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. The idle task is the only task that can run because all application tasks are in a blocked or suspended state.
- 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