cancel
Showing results for 
Search instead for 
Did you mean: 

How to implement low-power support with tickless mode in FreeRTOS

kein
Associate III

Hello everyone,

I'm using Board NUCLEO-U031R8 for testing now.

I have an old FreeRTOS project designed for a power-saving application, and I am preparing to migrate it to a new IC STM32U031 series MCU.

In the old project, setting configUSE_TICKLESS_IDLE to 1 allowed the system to enter low-power mode when tasks were idle, and the low-power support feature provided by the manufacturer.

So I started with STM32CubeMX to create a simple project and install "X-CUBE-FREERTOS" middleware (CMSIS RTOS2), and set configUSE_TICKLESS_IDLE to 1. then using STM32CubeIDE to import the .ioc file, last adding a long-duration LED blink task to make sure the system would entry tickless idle mode.

The LED operates as expected, but I noticed that the current consumption averages around 5mA, which suggests that low-power features might not be functioning as intended. Do I missing something or wrong understanding?

Is there a recommended way to achieve low-power mode with tickless functionality in FreeRTOS?

Regards

Kein.

1 ACCEPTED SOLUTION

Accepted Solutions
kein
Associate III

I found the problem, here is the situation:

// FreeRTOSConfig.h
#define configPRE_SLEEP_PROCESSING(__x__)                           \
                                       do {                         \
                                         PreSleepProcessing(__x__); \
                                         __x__ = 0;                 \
                                      }while(0)
// ------------------------------------------------------------------------------------
// port.c
__attribute__( ( weak ) ) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) {
  // .......
  xModifiableIdleTime = xExpectedIdleTime;
  configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
  if( xModifiableIdleTime > 0 )
  {
    // TODO: this region won't excute, because xModifiableIdleTime is set to 0 by macro configPRE_SLEEP_PROCESSING()
    __asm volatile ( "dsb" ::: "memory" );
    __asm volatile ( "wfi" );
    __asm volatile ( "isb" );
  }
  configPOST_SLEEP_PROCESSING( xExpectedIdleTime );
  // .......
}

 

 

 

The solution is simple just add sleep function maybe HAL_PWR_EnterSTOPMode() in configPRE_SLEEP_PROCESSING() and the code looks like this:

 

void PreSleepProcessing(uint32_t ulExpectedIdleTime) {
  RTC_StartWakeUpTimer(ulExpectedIdleTime);
  timeBeforeSleep = RTC_GetCount();
  LL_SYSTICK_DisableIT();

  {
    // ----------------------------- Sleep ------------------------------
    LL_LPUART_EnableInStopMode(LPUART1);                      // Set   LPUART1->CR1.UESM
    Power_EnterSleep();
    LL_LPUART_DisableInStopMode(LPUART1);                     // Clear LPUART1->CR1.UESM
    // ------------------------------------------------------------------
  }
}
//======================================================================================
void PostSleepProcessing(uint32_t ulExpectedIdleTime) {
  uint32_t sleepTime = (timeBeforeSleep - RTC_GetCount()) >> 5;   // 32k LSI, convert counter to ms with /32

  RTC_StopWakeUpTimer();
  LL_SYSTICK_EnableIT();                                    // HAL_ResumeTick();

//   NOTE: call vTaskStepTick() here, must commit out the vTaskStepTick() in file port.c
  vTaskStepTick( sleepTime );
}

 

 

hope this would help someone.

View solution in original post

5 REPLIES 5
SofLit
ST Employee

Hello,


@kein wrote:

The LED operates as expected, but I noticed that the current consumption averages around 5mA, which suggests that low-power features might not be functioning as intended.


How are you measuring the power consumption as your are turning ON a LED?. It could be you are measuring the LED consumption. So don't turn it ON.- 

Refer also to these videos:

FreeRTOS on STM32 v2 - 21a Low power modes (tickless mode) 

FreeRTOS on STM32 v2 - 21b Low power modes (tickless SLEEP mode)

 

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
kein
Associate III

Hi SofLit:

Thanks for the reply, The LED ON time is very short, and the OFF time is a long delay to make sure system idle, and measure the current in the same time, so i think it won't be a problem, here's my task simple code:

 

  while(1) {
    // LED ON with 100ms.
    LL_GPIO_SetOutputPin(LED4_GPIO, LED4_PIN);
    HAL_Delay(100);
    LL_GPIO_ResetOutputPin(LED4_GPIO, LED4_PIN);
    // LED OFF with 5000ms, ensure system entry tickless state.
    osDelay(5000);
  }

 

I follow the video and create LPTIM, and add the code in fucntion as below, the current is 400uA in LED OFF time, and the LED blink time as excepted.


 

void PreSleepProcessing(uint32_t ulExpectedIdleTime) {
  HAL_SuspendTick();
  HAL_LPTIM_TimeOut_Start_IT(&hlptim1, ulExpectedIdleTime);
}
void PostSleepProcessing(uint32_t ulExpectedIdleTime) {
  HAL_LPTIM_TimeOut_Stop_IT(&hlptim1);
  HAL_ResumeTick();
}​

 

The next step is modify vPortSuppressTicksAndSleep().

But when i commit out the restart systick part, It seems that osDelay(5000) been skipped, lead to LED working abnormally and it seems like always ON.

 

// port.c
__attribute__( ( weak ) ) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) {
  // ......
  /* Restart SysTick. */
  // TODO: commit out the restart systick, osDelay() would been skipped.
  // portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;    

  xModifiableIdleTime = xExpectedIdleTime;
  configPRE_SLEEP_PROCESSING( xModifiableIdleTime );

  if( xModifiableIdleTime > 0 ) {
    __asm volatile ( "dsb" ::: "memory" );
    // __asm volatile ( "wfi" );    // using HAL function to instead of this.
    HAL_PWREx_EnterSTOP1Mode(PWR_STOPENTRY_WFI);
    __asm volatile ( "isb" );
  }
  // ......
}

 

Regards.

Did you watch the videos I shared? they contain how to implement low-power mode in tickless mode in FreeRTOS as well as how to measure. Did you reproduce the hands-on?

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
kein
Associate III

Yes, I follow the vedio and create a new project, everything goes fine until modify the code to enter STOP1, after the changed the function osDelay() seem been skipped and not works as expected.

Kein wrote:

The next step is modify vPortSuppressTicksAndSleep().

But when i commit out the restart systick part, It seems that osDelay(5000) been skipped, lead to LED working abnormally and it seems like always ON.


here is the test project if you needed.

Regards.

kein
Associate III

I found the problem, here is the situation:

// FreeRTOSConfig.h
#define configPRE_SLEEP_PROCESSING(__x__)                           \
                                       do {                         \
                                         PreSleepProcessing(__x__); \
                                         __x__ = 0;                 \
                                      }while(0)
// ------------------------------------------------------------------------------------
// port.c
__attribute__( ( weak ) ) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) {
  // .......
  xModifiableIdleTime = xExpectedIdleTime;
  configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
  if( xModifiableIdleTime > 0 )
  {
    // TODO: this region won't excute, because xModifiableIdleTime is set to 0 by macro configPRE_SLEEP_PROCESSING()
    __asm volatile ( "dsb" ::: "memory" );
    __asm volatile ( "wfi" );
    __asm volatile ( "isb" );
  }
  configPOST_SLEEP_PROCESSING( xExpectedIdleTime );
  // .......
}

 

 

 

The solution is simple just add sleep function maybe HAL_PWR_EnterSTOPMode() in configPRE_SLEEP_PROCESSING() and the code looks like this:

 

void PreSleepProcessing(uint32_t ulExpectedIdleTime) {
  RTC_StartWakeUpTimer(ulExpectedIdleTime);
  timeBeforeSleep = RTC_GetCount();
  LL_SYSTICK_DisableIT();

  {
    // ----------------------------- Sleep ------------------------------
    LL_LPUART_EnableInStopMode(LPUART1);                      // Set   LPUART1->CR1.UESM
    Power_EnterSleep();
    LL_LPUART_DisableInStopMode(LPUART1);                     // Clear LPUART1->CR1.UESM
    // ------------------------------------------------------------------
  }
}
//======================================================================================
void PostSleepProcessing(uint32_t ulExpectedIdleTime) {
  uint32_t sleepTime = (timeBeforeSleep - RTC_GetCount()) >> 5;   // 32k LSI, convert counter to ms with /32

  RTC_StopWakeUpTimer();
  LL_SYSTICK_EnableIT();                                    // HAL_ResumeTick();

//   NOTE: call vTaskStepTick() here, must commit out the vTaskStepTick() in file port.c
  vTaskStepTick( sleepTime );
}

 

 

hope this would help someone.