Tickless Idle on STM32L4 Nucleo and low power modes question

Hi all,
I’ve been actively using FreeRTOS for the last few months in various projects, and we’re very happy with the results.

At the moment I’m working on an STM32L4 nucleo board, and I have a few questions regarding low power modes and FreeRTOS. Some questions may actually be STM32 related, but I’ll take my chances here anyway!

I’ve set configUSE_TICKLESS_IDLE to 1 in this project. My understanding is the scheduler will try to go to sleep whenever the expected idle time is more than configEXPECTED_IDLE_TIME_BEFORE_SLEEP (2). Now, the application works as follows:

  • There is one task that calls vTaskDelay(250), which is an LED toggle.
  • All other tasks start either with xStreamBufferReceive(…) or ulTaskNotifyTake(…) in different shapes and sizes as they are all interrupt based (USB comms, UART, etc).

Can I expect FreeRTOS to actually enter:

traceLOW_POWER_IDLE_BEGIN();
portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
traceLOW_POWER_IDLE_END();

?

Here’s another question on the same system. I’d like to put the system on sleep for an indefinite amount of time, and be woken up either by push button (GPIO) interrupt or the RTC alarm. When the system wakes up, it would call a system reset, it doesn’t really care for how long it was asleep.

There is a system task which wakes up with xTaskNotifyWait(…), within that task, the system gets deinitialised like this:

vTaskSuspendAll();
vPortRaiseBASEPRI();

HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFE); ///< Enters sleep mode here
HAL_NVIC_SystemReset();

That puts the system to sleep until the button is pushed and it restarts. RTC values are kept. So far so good.

If I change the stop mode entry to _WFI and enable the GPIO pin as interrupt instead of event:
vTaskSuspendAll();
vPortRaiseBASEPRI();
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI); ///< Enters sleep mode here
HAL_NVIC_SystemReset();

The system goes to sleep but fails to wake up when I push the button!

Below, I remove vPortRaiseBASEPRI():

vTaskSuspendAll();
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI); ///< Enters sleep mode here
HAL_NVIC_SystemReset();

And the system jumps from the enter power mode to reset, starting again.

The GPIO button interrupt is set to 7. if I add this line:

vTaskSuspendAll();
vPortSetBASEPRI(7);
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI); ///< Enters sleep mode here
HAL_NVIC_SystemReset();

Same as before, the system goes directly to reset. Same thing if I add
HAL_SuspendTick();
after suspending the tasks.

My guess is there is an interrupt running configured by FreeRTOS which wakes the system up immediately after going to sleep in stop mode.

How would I go deactivating only those interrupts used by FreeRTOS and not the ones I need, the GPIO interrupt and the RTC interrupt?

Alternatively, I could disable all interrupts and just wake up on events, but for the sake of understanding, I’d like to get both methods to work.

Many thanks in advance!

Cheers,

Alberto

Hi Alberto,

Yes.

On CM4, interrupts masked with BASEPRI cannot wake the CPU from WFI sleep. But the corresponding event can still wake the system from WFE sleep.

Since you’re already using tickless idle, you could implement configPRE_SLEEP_PROCESSING() and use it to induce STOP2. When that macro executes, FreeRTOS has already temporarily suspended its tick interrupt (not to be confused with the HAL tick). You’ll want configPRE_SLEEP_PROCESSING() to induce STOP2 conditionally, only when application conditions require STOP2 instead of normal tickless idle. Using that macro, you’ll be able to implement with WFI or WFE just as you said. Also, as you said, it’s good to watch out for the HAL tick as it will also wake you unnecessarily from tickless idle. You can stop the HAL tick, or you can override the HAL tick functions to use the FreeRTOS tick (a little more advanced).