STM32 and Low Power Mode

STM32 ARM MCU are proposing different low power mode for saving energy when running IoT on battery. Some basic example of low power are delivered with the SDK as part of CubeMx solution but these example are not really complete, not well documented and in my point of view difficult to use in a Fresh new project. Basically my current feeling with STM32 is these guy are pretty good to write thesis on how to do magic stuff with the MCU, writing hundreds of pdf pages about really detailed stuff but they are really bad for providing some line of code to illustrate this valuable content with something practicable you can use on the go. So after spending a week working on a working and understood example of low power code, I’ll share with you the result of this work…

The different Low Power mode

Different Low Power mode are described in the STM32L Datasheet:

  • Low Power Run : CPU switch to 131KHz clock to save power. Current is down to 6,5uA
  • Sleep Mode : CPU is stopped, Memory & Register are retained, some peripheral stays active.
    • Sleep Mode : CPU is stopped but it can be wake-up by any of the active peripherals. Current is down to 400uA @ 16 Mhz to 1mAh
    • Low Power Sleep Mode : CPU is stopped and the active peripherals are limited and working at reduce frequency. Basically you can program a 32KHz wake-up in this mode. Current is down to 3,2uA
  • Stop Mode: In Stop Mode the CPU core is stopped but the RAM and Register are retained. Most of peripherals are stopped. Wake-up time: 5uS.
    • Stop Mode with RTC : Wake-up is external signal or RTC… Current is down to 0,8uA @ 3V
    • Stop mode w/o RTC : Compare to previous mode the RTC is stopped. Current is down to 0,38uA @ 3V
  • Standby Mode: In standby mode the CPU core is stopped, Registers & RAM are stopped. Only Registers in Standby circuit are preserved.
    • Standby with RTC: Wake-up is external or RTC… Current is down to 0,57uA @ 3V
    • Standby w/o RTC : The RTC is stopped also. Current is down to 0,26uA @ 3V

I will take a particular look on Stop Mode with RTC and potentially later on Low Power Sleep Mode are they are the two offering, for my usage, the best flexibility for a good energy saving. In Both case they use RTC for waking up and also I want to be able to wake-up on serial communication and GPIO interruption.

STM32 – Low Power – Stop Mode

The objective is to create a Stop Mode switch with automatic wake-up after 500ms, on Serial event and on external GPIO event (external interrupt).

The most important part is to correctly setup the device in CubeMX solution (you can also make it manually but you need to be really experienced and in this case you may not need to read this post 😉

The first step is to setup the device pin: we need to setup the SWD pins for uploading the firmware in SYS selection Debug Serial Wire, select LPUART TX/RX pin and choose asynchronous LPUART.

Regarding the GPIOs, PA7 is configured to be connected to a LED as Gpio_Output. PA9 will be the wake-up pin: it is setup as GPIO_EXTIx.

The RTC is also activated with Clock Source, Calendar and Internal WakeUp selected.

The second board is configuring the clock sources:

The RTC is clocked by LSI @ 37kHz, LPUART needs to be clocked by HSI (or LSE) to be used for wake-up. CPU core can be clocked by HSI or MSI.

The last configuration step is for each of the internal devices:

  • LPUART: is setup for 9600 Bp/s, 8Bytes, Parity None, Stop bit 1, LPUART1 interrupt / LPUART1 wake-up interrupt is Enabled through EXTI line 28.
  • GPIO: PA9 is configured as External Interrupt with Falling edge trigger detection and connected to a Pull-up. The EXTI line 4 to 15 interrupt is Enabled.
  • NVIC: Are activated RTC global interrupt, EXTI 4 to 15 and LPUART wake-up
  • RTC: Format is 24h, Async prediv is 0x7C and Sync prediv is 0x127. Wake Up clock is RTCCLK /16. This allows a 2314 RTC ticks / s. Wake Up Counter is configured to 0 at start. Calendar Time an Date default are ok. On NVIC tab RTC ExTI is activated.

With this configuration we can generate the skeleton of our project with the right hardware setup. This part was really important to limit the quantity of custom code and avoid some future conflict between generated code and custom code.

To make it simple let’s add everything in the main.c file:

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_LPUART1_UART_Init();
  MX_RTC_Init();

  while (1)
  {
    stm32l_lowPowerSetup();
    // sleeping
    stm32l_lowPowerResume();
  }
}

Once the cubeMx setup is correctly made we have no specific setting other than the lowPower switch. The first function place the MCU in low power mode when the second one restore the normal functions. Between the two, the MCU is in low power mode. You can add your custom workload before of after these 2 lines.

The function stm32l_lowPowerSetup is

// Configure RTC to wake up after 500ms 
uint32_t _time = (((uint32_t)500) * 2314) / 1000;
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, _time, RTC_WAKEUPCLOCK_RTCCLK_DIV16);

__HAL_RCC_PWR_CLK_ENABLE(); // Enable Power Control clock
HAL_PWREx_EnableUltraLowPower(); // Ultra low power mode
HAL_PWREx_EnableFastWakeUp(); // Fast wake-up for ultra low power mode

// Disable Unused Gpios
_stm32l_disableGpios(); // Disable GPIOs based on configuration

// Configure LPUART for Wake-up
// make sure that no UART transfer is on-going
while(__HAL_UART_GET_FLAG(&hlpuart1, USART_ISR_BUSY) == SET);
// make sure that UART is ready to receive
while(__HAL_UART_GET_FLAG(&hlpuart1, USART_ISR_REACK) == RESET);

UART_WakeUpTypeDef wakeup;
wakeup.WakeUpEvent=UART_WAKEUP_ON_STARTBIT; // UART_WAKEUP_ON_READDATA_NONEMPTY
HAL_UARTEx_StopModeWakeUpSourceConfig(&hlpuart1,wakeup);
__HAL_UART_ENABLE_IT(&hlpuart1, UART_IT_WUF);
HAL_UARTEx_EnableStopMode(&hlpuart1);

// GPIO Wake up is configured by CubeMx config and keeping the GPIO activated


// Switch to STOPMode
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

The function stm32l_lowPowerResume allow to resume from Stop mode:

// Reinit clocks
SystemClock_Config();
// Deactivate RTC wakeUp
HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
// Reinit GPIOs
MX_GPIO_Init();
// Reinit LPUART
HAL_UART_MspInit(&hlpuart1);
MX_LPUART1_UART_Init();

When using GPIOs for waking up we need to add the Interrupt handler somewhere:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
   /* Clear Wake Up Flag */
   __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
}

We also need to write the function to disable GPIOs, this is saving a lot of energy.

void _stm32l_disableGpios() {

   // GPIO_A is preserved to keep output status unchanged and have 
   // Interrupt working for waking Up.
 

   GPIO_InitTypeDef GPIO_InitStructure = {0};
   GPIO_InitStructure.Pin = GPIO_PIN_All;
   GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
   GPIO_InitStructure.Pull = GPIO_NOPULL; 

   // GPIO_B and GPIO_C are disabled
   HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
   __HAL_RCC_GPIOB_CLK_DISABLE();
   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
   __HAL_RCC_GPIOC_CLK_DISABLE();

}

With this setting, I achieve about 1.2uA in Stop mode with the RTC, LPUART and part of GPIO_A active for waking up the MCU on event or regular basis. The wake-up duration (for restoring configuration, calculating time and going back to sleep mode is about 1.2ms)

 

This entry was posted in Hardware and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.