Working with FreeRTOS in low power tickless mode for long stop periods

I’m using FreeRTOS in an STM32WL55 based application that needs to be active only few time per day (3-6).
The system need to be in stop mode for most of the time and I have an external line that is connected to the LPTIM and based on this line the system needs to wake up do some activities.
All the FreeRTOS & STM32 discussion I read refer to FreeRTOS Tickless mode where the system needs to wake up periodically on a relatively fast rate (this is based on the system clock mainly) to keep its internal tick. This is indeed needed if you are using OS timers or some task are waiting on a service that is time based (e.g. wait for a message or osDelay)
But if the system does not use any OS timebase services while in stop mode one would wants the system to stay in stop mode as long as possible.
How can this be achieve?
Is there any examples for that?

Did you already found and read the official docs (including a link to STM23L demo) ?

See also e.g. this maybe interesting thread and / or search the forum for tickless idle.
Hope it helps !

Yes, I read both of your references, but I did not find an answer to my question, although it might be that the answer is there but I was not able to identified it.
I run ST sample with the tickless mode but its periodically wake up when the tick timer is wraparound

Sounds like you should use the default implementation of tickless idle from FreeRTOS (not the one from ST). Set configUSE_TICKLESS_IDLE to 1. Then you should implement the configPRE_SLEEP_PROCESSING() macro to select STOP mode (SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;) in place of standard sleep mode if your application code is ready for STOP mode. (Don’t use stop mode while your application is actively working.) Then in configPOST_SLEEP_PROCESSING(), select sleep mode instead of stop mode (SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;).

With this solution, time will stand still during STOP mode. You’ll wake up at the same tick count you went to sleep with, even if hours have passed.

Thank you Jeff,
I did a lot of experiments since I got your reply and I have some unclear behavior that I hope you can explain.
Even if I does not set anything in the pre sleep function and there is no other wakeup mechanism in my program (Like setting RTC wakeup after x seconds for example) the system somehow wake up after the maximum time of a 24bit clock at 4MHz (Which is my core clock speed for the testing I’m doing) that is around 4 seconds (4194 is the FreeRTOS calculation of this max time) and then goes to sleep again and this thing continue forever if no task is ready.
How come? What’s wake up the system at this rate?
I thought that if I HAL_SuspendTick then things are stand still.

All of the behavior you describe is expected, so you’re almost there.

The ST HAL operates its own independent tick timer. Let’s assume it’s TIM4. FreeRTOS also operates its own tick timer, SysTick. Each timer produces its own periodic interrupt. When you call HAL_SuspendTick(), the HAL stops TIM4. That function does not affect SysTick, which provides the FreeRTOS tick.

After you call HAL_SuspendTick() and eliminate other sources of interrupts, FreeRTOS is willing to sleep for as much as the SysTick timer will allow (4 seconds in your case). With no tasks ready, that’s exactly what you saw. FreeRTOS does this automatically (when configUSE_TICKLESS_IDLE is 1) without you doing anything. FreeRTOS determines when to go to sleep and when to wake up all on its own, and it induces sleep and wakeup accordingly.

The key to using the STOP modes is to implement configPRE_SLEEP_PROCESSING() and configPOST_SLEEP_PROCESSING() which FreeRTOS will call just before/after going to sleep. If you execute SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; in configPRE_SLEEP_PROCESSING(), then the sleep state used is the STOP state. The STOP state also stops the SysTick timer, preventing the wakeup that would have occurred in 4 seconds. The STOP state is what makes time stand still.

1 Like

OK, I thought from some reason (Maybe the name “tickless” or the function name HAL_SuspendTick) that FreeRTOS tick is paused between the pre and the post SleppProcessing.
Now, as I see and as you explained so nicely, this is not the case and FreeRTOS set its next wakeup based on its internal calculation for the next schedule operation (task delay, OS timer etc.) and if there is no such operation waiting it use the maximum possible value (here I’m not sure I understand why it is a 24bit timer) that is based on the core clock (~4sec at 4MHz).
Now, what I’m doing at PreSleepProcessing is this:

__weak void PreSleepProcessing(uint32_t *ulExpectedIdleTime)
    // Wakeup after 10 sec
	HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0x5000, RTC_WAKEUPCLOCK_RTCCLK_DIV16,0);
    // Set STOP mode and wfi, set ulExpectedIdleTime to zero to signal 
    // vPortSuppressTicksAndSleep that the user implementation contains its own wfi
	*ulExpectedIdleTime = 0;

And I expect FreeRTOS to go to sleep and wake up after 10 sec, this does not happened.
Any idea what I’m doing wrong here?

Are you sure this code is executing? What is the behavior you are observing with this code in place?

Well, I have a printout to the UART at the post function and I get to it but then the next pre function seems to not stop at the wfi but I never get again to the post.
I tired this within t he debugger as well as without it.
BTW, if I’m executing this code without FreeRTOS just after initialization part of main things are working as expected:

	  printf("Start wait 1s\r\n");
	  printf("Goto sleep\r\n");
	  HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0x5000, RTC_WAKEUPCLOCK_RTCCLK_DIV16,0);
	  /* Enter STOP 2 mode */

And the output looks like this:

Start wait 1s
Goto sleep
Start wait 1s
Goto sleep
Start wait 1s
Goto sleep
Start wait 1s
Goto sleep

To get your code to work correctly from PreSleepProcessing():

  1. You must make the use of STOP mode conditional. Don’t use STOP mode every time FreeRTOS determines no tasks are in the ready state. Instead, use STOP mode only when your application is ready for it. The primary consideration here is that time stands still during STOP mode. Imagine that FreeRTOS is waiting 100 ms for the next task to become ready. It decides to go to sleep for 100 ms. If you use STOP mode, then 10 seconds later when STOP mode ends, FreeRTOS will see that 0 ms has elapsed and goes back to sleep to wait 100 ms. The secondary consideration here is that many peripherals do not function in STOP mode. If you enter STOP mode while a UART is working, it quits and may go into an invalid state. Bottom line – make sure your application is ready for STOP mode before invoking it.
  2. Your RTC wakeup interrupt must wake a task. If you don’t do this, then whatever amount of time FreeRTOS is waiting for will never elapse. See #1 above.

I’m trying to get the principle to work here and after that to make it work with my application.
My application needs to wake up only very limited times per day, based on an external event that will issue a wakeup.
When this wake up occurs some FreeRTOS activities will start (i.e. some tasks will be resumed or started and will communicate with messages and timer etc.) and at that time I plan to bypass the pre sleep by using flag or based on the xExpectedIdleTime so the system will not go to sleep.
Only when I’m ready to go to sleep I plan to suspend (or delete) the tasks and then the pre sleep will put the system to sleep.
At that sleep time I have no task or timer that needs to know the tick so I can freeze everything.
As I have an RTC I can always know the time passed when the system is wakeup from an external event or wakeup from a RTC wake up event as I want to be able to wake up a few times a day even if my external trigger was not raised (i.e. for keepalive handling).
The only issue I have know is that I cannot make this long sleep with the RTC wakeup to work.

But you did make it work. The while(1) loop you posted above is working as expected. I think you have proven the principle and should move on to applying it to your application as you describe above.

By the way, it’s best to avoid suspending tasks as a general rule. Instead, signal the task to stop its work so it can do so cleanly. Then it can either report that it is stopped, or perhaps wait for a signal “forever”, as an indication that STOP mode is now safe (see eNoTasksWaitingTimeout). Or, for simple tasks, they can be designed to “always” be ready for STOP mode anytime they are not in the ready state.