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.
Implementation
The following code and documentation has been made when I started to create the Disk91 IoT SDK. This SDK is open-source and available on GitHub. Since the code has been completed and I invite you to take a look at the SDK if you are starting a STM32 project. The following paragraphe explains how it has been built and how it works.
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:
(open the image in a new tab to get it larger)
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)
great article. It would help if you would state which STM32 chip you are using, provide the clock sources picture in a higher resolution, and perhaps post the complete code somewhere (like github)
Thank you for your feedback. You can get a larger clock image by opening the image in a new tab. The complete code is accessible in the ItSdk project on github. Here is the direct link with the STM32 low_power implementation : https://github.com/disk91/stm32-it-sdk/blob/master/Src/stm32l_sdk/lowpower/lowpower.c
Hello, I tried this example with the NUCLEO-L053R8 and adapted the code to use the respective pins for the LED and its EXTI, regrettly I didn’t change the correct GPIO port to remain enabled and be used as a wake up source. After trying to debug it now it get an error each time I try to connect to the board, have you an idea of how can it be fixed?
My first was that I left the MCU in STOP mode and can’t get out. Could it work if I erase the whole chip memory? And if so, how do you recommend me to do it?
When device is in STOP mode you need to reset to connect from most of the programming board.
Hello Paul,
Thanks for this great example. I reproduced this project on an STM32L051C8T6 and got the power down to 3uA. On what processor exactly did you got 1.2uA? I’m very curious.
Leo.
Was made a long time ago, I assume it was one of the STM32L0 having a soic14 package.
Thank you Paul !!!
Your Article is very useful to me!
An Iranian Boy
Hello Paul!!
Can I use this code in a STM32F103C8T6? I want to use this mcu in a iot application
Greetings from Brazil
Most is based on HAL so it should work. The STM32F could have some internal part working differently so it still something to check
Hi. I am using STM32l010. I’m using your code and it doesn’t come out of stand by mode, how can I get it out? There is no interrupt pin that I can use. I need to sleep for 15 seconds. how can I do that?
You can use my SDK, it works well for me to wake up from interrupt, serial or RTC
Paul