cancel
Showing results for 
Search instead for 
Did you mean: 

How to enter Standby or Shutdown mode on STM32

Piranha
Chief II

Entering low-power modes, especially the Standby and Shutdown modes, is another mostly software related disaster from ST. Additionally there also seems to be one or two hardware bugs and/or undocumented conditions. This topic provides an example of how to do it correctly and reliably and an explanation of the issues present in the current ST provided code and documentation.

 

Correct example code based on L43x devices:

void Power_Off(void)
{
	// Configure wake-up features
	// WKUP1(PA0) - active high, WKUP4(PA2) - active low, pull-up
	PWR->PUCRA = PWR_PUCRA_PA2; // Set pull-ups for standby modes
	PWR->CR4 = PWR_CR4_WP4; // Set wakeup pins' polarity to low level
	PWR->CR3 = PWR_CR3_APC | PWR_CR3_EWUP4 | PWR_CR3_EWUP1; // Enable pin pull configurations and wakeup pins
	PWR->SCR = PWR_SCR_CWUF; // Clear wakeup flags
 
	// Configure MCU low-power mode for CPU deep sleep mode
	PWR->CR1 |= PWR_CR1_LPMS_STANDBY; // PWR_CR1_LPMS_SHUTDOWN
	(void)PWR->CR1; // Ensure that the previous PWR register operations have been completed
 
	// Configure CPU core
	SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; // Enable CPU deep sleep mode
#ifdef NDEBUG
	DBGMCU->CR = 0; // Disable debug, trace and IWDG in low-power modes
#endif
 
	// Enter low-power mode
	for (;;) {
		__DSB();
		__WFI();
	}
}

 

Issues and solutions explained:

 

// Configure wake-up features
...
PWR->SCR = PWR_SCR_CWUF; // Clear wakeup flags

1. Clearing of the wakeup flags must be done as the last operation after configuring wakeup features. When clearing the flags is not the last operation, MCU wakes up immediately after entering the low-power mode. After that, if the firmware will enter the low-power mode again with the same wakeup configuration, then because of some internal hardware logic nuances the second time the MCU will stay in low-power mode and wait for an actual wakeup event.

 

(void)PWR->CR1; // Ensure that the previous PWR register operations have been completed

2. Because the PWR peripheral is located on APB bus, the values written to the PWR registers take some time (clock cycles) to reach the peripheral. To ensure that the last (and therefore all) of the previous store operations have been completed, a read operation of some PWR register is required. If this is not ensured, the CPU can enter low-power mode before the PWR peripheral is configured.

 

#ifdef NDEBUG
	DBGMCU->CR = 0; // Disable debug, trace and IWDG in low-power modes
#endif

3a. To reach the lowest power consumption, the debug interface must also be disabled. When enabled, it typically results in up to 5 mA of additional current consumption. At least in debug builds the firmware can set these bits to enable debugging with low-power modes or, for example, J-Link debuggers always set these bits after the connection. As these bits are not reset by a system reset and power-on reset can be problematic on some devices, including because of a capacitors of a few uF can retain the voltage for several minutes, it is recommended to always clear those bits manually at least in release builds.

3b. If debug interface is enabled, contrary to the reference manual, IWDG continues running in shutdown mode and resets (and therefore wakes up) the MCU after the configured time. Another way to disable the IWDG in shutdown (and standby) mode is clearing the IWDG_STDBY bit in option bytes.

 

for (;;) {
	__DSB();
	__WFI();
}

4. If a debug request or interrupt happens at the same time the WFI instruction is executed, the WFI instruction acts as a NOP instruction. That means the CPU doesn't enter a (deep) sleep mode but instead continues executing the code. If the code relies on the idea that the CPU will not execute the instructions beyond the WFI instruction, it's a bug and the consequences can be unpredictable. As the standby and shutdown modes are designed to stop the code execution after the WFI instruction anyway, the issue can be solved by just putting the WFI instruction in an endless loop. Also before the WFI instruction HAL code uses __force_stores() intrinsic only for ARM compiler, but the __DSB() macro is universal for all compilers and is required by ARM architecture.

 

I can confirm this behavior on L43x devices, but it seems that it is the same at least for many other parts also. For example, this topic confirms the same for a L0 series device.

 

Tasks for ST to fix:

  • 1, 3b - Potential hardware bugs and could be closely related. Must be checked/confirmed and, depending on that, should be documented in erratas and/or reference manuals.
  • 2, 3a, 4 - Should be documented (reminded) in reference manuals.
  • 2, 4 - HAL functions HAL_PWR_EnterSTANDBYMode() and HAL_PWREx_EnterSHUTDOWNMode() should be fixed.
  • 1, 3 - Code examples should be fixed.
20 REPLIES 20
Kmax18
Senior II

Hello @Piranha, thank you fI or posting your solution. We are experiencing have the  That is exactly the problem we experience with the STM32G0B1RET MCU:

During normal operation pin PC4 is used in a UART1 configuration (with an external pull-up resistor). In Standby Mode pin PC4 must be LOW in order to shut down the external device. Initially I used the function HAL_PWR_EnterSTANDBYMode(). However, when called it consistently pulls the output UP, not down.

I adopted your code listed in this post and adjusted it for Wakeup pin 6 and for output pin PC4. See below:

The output PC4 is reconfigured as a GPIO pin. also is pulled HI. However, the output PC4 is also pulled HI when entering Standby Mode. (Only in Debug mode PC4 remains at LOW, although that may have different reasons.)

Please comment. What else can we try? Thank you!!

/* Convert UART1 IO to a GPIO */
GPIO_InitTypeDef GPIO_InitStruct = {0};
HAL_GPIO_WritePin(UART1_GPIO_Port, UART1_Pin, GPIO_PIN_RESET);

/*Configure GPIO pin : UART1_Pin */
GPIO_InitStruct.Pin = UART1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(UART1_GPIO_Port, &GPIO_InitStruct);

PWR->PDCRC = PWR_PDCRC_PD4;   // Set pull-ups for standby modes
PWR->CR4 = PWR_CR4_WP6;       // Set wakeup pin 6 to low level
PWR->CR3 = PWR_CR3_APC | PWR_CR3_EWUP6; // Enable pin pull configurations and wakeup pins
PWR->SCR = PWR_SCR_CWUF; // Clear wakeup flags

// Configure MCU low-power mode for CPU deep sleep mode
PWR->CR1 |= PWR_LOWPOWERMODE_STANDBY;
(void)PWR->CR1; // Ensure that the previous PWR register operations have been completed

// Configure CPU core
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; // Enable CPU deep sleep mode

// Enter low-power mode
for (;;) {
    __DSB();
    __WFI();
}