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.
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:
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:
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.
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();
}
}