Preventing entry to sleep when using tickless idle

bryce78 wrote on Tuesday, November 15, 2016:

Hello Community,

I have a question about preventing (“turning off”) entry to sleep during specific idle periods. For example, suppose I am performing a network provisioning task which involves a lot of waiting during which the MCU could go to sleep. But, for simplicity and to prevent continuous sleep-wake cycles, I wish to have the idle task run (skipping entry to sleep mode) until the network provisioning completes or timesout. Essentially I would like to control (select) either entry to low-power sleep, or simply remain idling.

Here is a little background on my project: I am using Keil uVision v5.20 for development with FreeRTOS v9.0.0 on STM32L471 ARM Cortex M4 MCU. My RTOS project defines 5 tasks, 4 of which use a Queue and wait for events to process, and the fifth uses Task Notifications to block until something happens. Some relevant configuration:

#define configOVERRIDE_DEFAULT_TICK_CONFIGURTION 1
#define configUSE_TICKLESS_IDLE                  2
#define configUSE_IDLE_HOOK                      0

I use 32.768kHz oscillator to drive low-power timer and generate the RTOS tick. I have the RTOS tick independent of the MCU SYSCLK so I can achieve longer sleep periods, and also I need to keep SYSCLK running at a low frequency while in SLEEP. To do all this I had to implemented my own portSUPPRESS_TICKS_AND_SLEEP() for tickless idle.

Back to the question of selecting either idle or sleep: I am thinking of introducing a simple global variable into the idle task which will determine whether the tickless idle code is skipped or run.

I think it’s pretty simple but wouldn’t mind a little feedback about this approach or more generally how to control entry to sleep mode. (For example, if portSUPPRESS_TICKS_AND_SLEEP() is to be skipped, perhaps I should call vApplicationIdleHook() to increment an idle time counter that could be cleared when any task wakes. It could be used to ensure a “minimum idle time” before allowing entry to sleep in general circumstances.)

Thanks for any suggestions!
Bryce

edwards3 wrote on Tuesday, November 15, 2016:

The expected way would be to define a portPRE_SLEEP_PROCESSING( x ) macro in FreeRTOSConfig.h. If sleep should not be entered set x to 0 in the macro.

What does setting configUSE_TICKLESS_IDLE to 2 do?

bryce78 wrote on Wednesday, November 16, 2016:

Thanks for the reply!

Ah, yes, now I see that option to set x = 0 and cause the sleep to be skipped. I think that could work – though the code will run right “up to” the sleep instruction and skip at the last possible chance. I think I’m looking for something a bit more “coarse” – i.e. not stop the scheduler at all. I will keep this in mind and trying some other options. Thanks for reminding me of this possibility.

The #define configUSE_TICKLESS_IDLE 2 setting doesn’t add anything – from what I saw looking at the code it just ensures the default vPortSetupTimerInterrupt() and portSUPPRESS_TICKS_AND_SLEEP() and anything related to the default config_USE_TICKLESS_IDLE == 1 setting (which assumes that SysTick is the source for RTOS tick) isn’t compiled. According to website notes it appropriate when defining your own clock other than SysTick to generate the RTOS tick interrupt. There’s some notes about it here and here.

bryce78 wrote on Wednesday, November 16, 2016:

To be more clear, I’m thinking more of an higher “application level” approach to preventing the idle task from entering into sleep, rather than lower “RTOS level”. For example, during configuration of peripherals or devices there might be periods of idle (e.g. waiting for sensor to initialize), during which the application can be considered “busy waiting”.

Is it possible (safe?) to suspend the idle task using vTaskSuspend()? Any special considerations for doing that?

edwards3 wrote on Wednesday, November 16, 2016:

You must always have a task to run, so if you suspend the idle task you must have an application task that can run. If all the tasks are blocked or suspended it will crash.

This will help http://www.freertos.org/a00021.html#xTaskGetCurrentTaskHandle

edwards3 wrote on Wednesday, November 16, 2016:

Correct link is http://www.freertos.org/a00021.html#xTaskGetIdleTaskHandle

mrazoun wrote on Wednesday, November 16, 2016:

Hi,
I asked few weeks ago maybe the same question. You can look : https://sourceforge.net/p/freertos/discussion/382005/thread/063d54e8/
The conclusion was that you can move the responsibily to sleep_sleep which is EnergyMicro library function in our case. The usage of macros in FreeRTOS give you very strong possibilities.
Thank’s to FreeRTOS developers.
Zdenek

bryce78 wrote on Thursday, November 17, 2016:

Thanks for the info on what can happen if Idle task is suspended and there’s nothing to run! I’ll keep this information in mind!

bryce78 wrote on Friday, November 18, 2016:

Hello and thanks for the pointer to your thread! Definitely it’s a question on the same topic. I’ve had a read from your thread and got further ideas! Plenty to work with now :slight_smile:

xz8987f wrote on Friday, November 18, 2016:

Hi Bryce,
I had the problem that I need to prevent entering low power or tickless idle mode in case there is some communication pending (e.g. UART). For this I have extended FreeRTOS with two defines:

#define configUSE_TICKLESS_IDLE_DECISION_HOOK     1 /* set to 1 to enable application hook, zero otherwise */
#define configUSE_TICKLESS_IDLE_DECISION_HOOK_NAME xEnterTicklessIdle /* function name of decision hook */

The first one turns on an extra hook, and the second one provides the name.

You can find an implementation on GitHub here: https://github.com/ErichStyger/Hexiwear_v2/tree/master/KDS/Hexiwear_SDKv2/freertos/Source

The extension is in task.c


around line 3336:

		/* This conditional compilation should use inequality to 0, not equality
		to 1.  This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when
		user defined low power mode	implementations require
		configUSE_TICKLESS_IDLE to be set to a value other than 1. */
		#if ( configUSE_TICKLESS_IDLE != 0 )
		#if configUSE_TICKLESS_IDLE_DECISION_HOOK /* << EST */
		if (configUSE_TICKLESS_IDLE_DECISION_HOOK_NAME()) /* ask application if it shall enter tickless idle mode */
		#endif
		{
		TickType_t xExpectedIdleTime;

			/* It is not desirable to suspend then resume the scheduler on
			each iteration of the idle task.  Therefore, a preliminary
			test of the expected idle time is performed without the
			scheduler suspended.  The result here is not necessarily
			valid. */
xExpectedIdleTime = prvGetExpectedIdleTime();

Basically this extra hook allows the application to decide if the idle (and low power) mode really shall be entered. That approach worked really well for me. See as well https://mcuoneclipse.com/2014/02/09/iot-freertos-down-to-the-micro-amps/ and https://mcuoneclipse.com/2013/07/06/low-power-with-freertos-tickless-idle-mode/

I hope this helps,
Erich

rtel wrote on Friday, November 18, 2016:

I think there might be an easier way because
portSUPPRESS_TICKS_AND_SLEEP() is declared in a way that allows the user
to override its default implementation by providing their own.

The default definition is as follows:

#ifndef portSUPPRESS_TICKS_AND_SLEEP
     extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime );
     #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) 
vPortSuppressTicksAndSleep( xExpectedIdleTime )
#endif

To override the definition you can add your own implementation of
portSUPPRESS_TICKS_AND_SLEEP into FreeRTOSConfig.h, and can use that
definition to decide if you want to go into sleep mode or not.
Something like the following to be added to FreeRTOSConfig.h:

extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime );
#define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) \
     if( AppWantsToEnterSleepMode() == pdTRUE )            \
     {                                                     \
         vPortSuppressTicksAndSleep( xExpectedIdleTime );  \
     }