cancel
Showing results for 
Search instead for 
Did you mean: 

Sleep mode with FreeRTOS enabled

xgon
Associate

I want to be able to make the STM32G474 go to sleep when certain conditions are met.

I've created a small project to test the feature. The `app-freertos.c` file is like this:

#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#include "usart.h"
#include "lptim.h"
 
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .priority = (osPriority_t) osPriorityNormal,
  .stack_size = 128 * 4
};
 
void StartDefaultTask(void *argument);
void MX_FREERTOS_Init(void);
 
static uint32_t rccCfgrSave;
static uint32_t rccCrSave;
 
 
__weak void PreSleepProcessing(uint32_t ulExpectedIdleTime)
{
    HAL_UART_Transmit(&huart3, "pre sleep\r\n", 11, 10);
    HAL_SuspendTick();
    HAL_LPTIM_TimeOut_Start_IT(&hlptim1, 0xFFFF, ulExpectedIdleTime);
}
 
__weak void PostSleepProcessing(uint32_t ulExpectedIdleTime)
{
    HAL_LPTIM_TimeOut_Stop_IT(&hlptim1);
    HAL_ResumeTick();
    HAL_UART_Transmit(&huart3, "post sleep\r\n", 12, 10);
}
 
 
void MX_FREERTOS_Init(void) {
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
}
 
void StartDefaultTask(void *argument)
{
  for(;;)
  {
    HAL_UART_Transmit(&huart3, "test\r\n", 7, 10);
    osDelay(2000);
  }
}

The default task `StartDefaultTask` works as expected, but the `PreSleepProcessing`is always being called, and `PostSleepProcessing` is NEVER called.

I've followed many videos and tutorials from ST and I'm sure I did not overlooked anything.

Any help is appreciated!

Thanks.

7 REPLIES 7
JPeac.1
Senior

I don't use the ST HAL but I do have a question. How do you wake up from sleep mode, and is that source powered up with clock and interrupt enabled? From your description it appears you never exit sleep mode.

Also, I assume ST must update the FreeRTOS tick counter on wakeup after sleep to account for the lost RTOS tick interrupts while sleeping. If you never wake up, that delay call in the main task will never unblock. Does the UART transmit call in pre sleep complete at least one time?

FreeRTOS will enter sleep mode right after the start of the osdelay call, since there are no other application tasks. LPTIM should generate a wakeup interrupt 2000 ticks (expected idle time) later, which in turn calls the post sleep routine. If LPTIM isn't running the RTOS will hang, forever waiting for an LPTIM interrupt before unblocking the main task.

I use the ARM system timer for ticks, RTC and subseconds for elapsed ticks while sleeping, and the RTC wakeup timer for fixed sleep intervals. But, this is HAL, so you might start by looking at the LPTIM register settings just before exiting the pre sleep routine.

Jack Peacock

@Community member​ 

The MCU never goes to sleep, although the `PreSleepProcessing` function is called. The UART transmits every time. Here is a portion of the uart output:

pre sleep
pre sleep
pre sleep
pre sleep
test
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep
pre sleep

Another thing I find strange is the argument `ulExpectedIdleTime`on the `PreSleepProcessing` function is always zero.

JPeac.1
Senior

You found the symptom of the issue in ulExpectedIdleTime. Because LPTIM is your only interrupt sleep mode doesn't exit until LPTIM signals that the expected idle has completed, so the main task can unblock. If it's set to zero it never times out. The log shows the LPTIM overflow interrupts, where you do actually exit sleep mode as expected, but because no task is scheduled the RTOS goes right back to idle and enters sleep mode again and again. Looks like something is wrong with the osdelay call when using sleep mode. The Idle time should be set to 2000 ticks at first, decreasing after every LPTIM timeout restarts sleep mode.

Remove the osdelay call and see if it works. There may be some HAL problems translating osdelay to the FreeRTOS task delay API call.

Also, FreeRTOS has an API, xTaskGetTickCount(), to read the internal system tick counter. Add it to your pre-sleep trace string to verify the clock ticks are increasing after LPTIM timeouts. Pre sleep runs inside the idle task context so you don't need the ISR version. Looks like you have to debug some of the ST HAL code...

Jack Peacock

Piranha
Chief II

For just putting a CPU into sleep mode during idle time, you only need this:

void vApplicationIdleHook(void)
{
	__DSB();
	__WFI();
}

If your goal is tickless idle, then that's a whole different story:

https://www.freertos.org/low-power-tickless-rtos.html

JPeac.1
Senior

A simple WFI won't work well with FreeRTOS because of the system tick timer, in this case LPTIM1 used by ST. If LPTIM isn't halted the timer interrupt will continue to wake up every system tick, defeating the purpose of sleeping. Also, I assume the osdelay call translates to a FreeRTOS task delay API and not a crude spinlock, in which case sleep must exit in time to schedule tasks once the delay ends.

For anything but a very simple application a tickless idle is a necessity when using FreeRTOS, unless power reduction through sleep mode isn't required. The real question here is how ST chose to handle sleep with a fixed timeout (the expected idle time variable). A good tickless idle has to account for this by some type of hardware timer that can wake up the CPU at or before the expected time limit (and adjust the RTOS tick count for sleep time). I'm sure ST's documentation on how this is handled is up to the high standards set by HAL code...

An ugly fix is to remove the references to LPTIM1 timeouts, and don't disable the system tick. Remove all the code in pre and post sleep (i.e. no tickless idle mode). RTOS will wake up on every system tick, check scheduling, and go back to sleep. You'll only see a fraction of the power savings from a single sleep interval, but you won't have to debug the HAL.

dhs
Senior

I am run the same example with identical behavior, but a guy from ST run this example on a MOOC and works, ¿how it is possible?

OliM
Senior

Using LPTIM as the FreeRTOS tick for nearly all STM32 is comprehensibly solved by this project: https://github.com/jefftenney/LPTIM-Tick

The things you really need are lptimTick.c, ulp.c/h and the additions to FreeRTOSConfig.h

, the rest is a project around it to show and measure how it works.