Low Power Support
Tickless Idle Mode
[More Advanced]
[See also Low Power Features For ARM Cortex-M MCUs]
[See also Tickless Demos on SAM4L, RX100,
STM32L,
CEC1302
and EFM32 MCUs ]
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:
-
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.
-
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:
-
Software timers are not being used, so the scheduler is not due to
execute a timer callback function at any time in the future.
-
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();
}
}
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.
|