Watchdog not wakeup from the STOP mode occasionally
My STM32L051C8T6 board encountered a very strange phenomenon. It occasionally failed to wake up from the STOP mode, although the IWDG was enabled.
I am using Zephyr RTOS, but do not enable Zephyr's power management, and use the following peripherals:
1. I2C1
2.USART1 and USART2
3.SPI1
4.ADC1
In order to reduce power consumption, the MCU will periodically enter the STOP mode and wake up the MCU through RTC. The RTC timeout range is from 500ms to 25 seconds. The watchdog timeout is the maximum value. In addition, the MCU can also be woken up through button1 (PB3) and button2 (PB9).
I'm surprised, it works fine in most cases, but occasionally it can't wake up through RTC, including two buttons. Every time the MCU stucked after enter the low_power_mode_enter.
Because IWDG is enabled, IWDG should be able to reset the MCU even if the program runs abnormally, but this does not happen.
Below is the code to enter the STOP mode.
/* Init early when enter the main function */
int watchdog_init(void)
{
LL_RCC_LSI_Enable();
LL_IWDG_Enable(IWDG);
LL_IWDG_EnableWriteAccess(IWDG);
LL_IWDG_SetPrescaler(IWDG, LL_IWDG_PRESCALER_256);
LL_IWDG_SetReloadCounter(IWDG, 0xFFF);
while (LL_IWDG_IsReady(IWDG) != 1)
{
}
LL_IWDG_ReloadCounter(IWDG);
return 0;
}
static inline void watchdog_feed(void)
{
LL_IWDG_ReloadCounter(IWDG);
}
static inline void low_power_usart_disable(USART_TypeDef *USARTx)
{
USARTx->CR1 &= ~(USART_CR1_RE | USART_CR1_TE | USART_CR1_RXNEIE | USART_CR1_TCIE);
while (LL_USART_IsActiveFlag_TC(USARTx) == 0)
{
__NOP();
}
LL_USART_Disable(USARTx);
}
#define GPIO_MODE_SET(pin, mode) (~((3 - (mode)) << ((pin) << 1)))
void lower_power_peripheral_disable(void)
{
LL_ADC_Disable(ADC1);
low_power_usart_disable(USART1);
low_power_usart_disable(USART2);
/**
* Disable PE before enter the STOP mode
* @see section 2.12.2 in STM32L05xxx/L06xxx device errat
*/
LL_I2C_Disable(I2C1);
LL_I2C_Disable(I2C2);
/* Save GPIO mode */
power_config.gpioa_mode = GPIOA->MODER;
power_config.gpiob_mode = GPIOB->MODER;
power_config.gpioc_mode = GPIOC->MODER;
power_config.gpioh_mode = GPIOH->MODER;
/* External interrupt */
GPIOA->MODER = 0xFFFFFFFF & GPIO_MODE_SET(LL_GPIO_PIN_12, LL_GPIO_MODE_INPUT);
/**
* LL_GPIO_PIN_9 and LL_GPIO_PIN_3 is buttons which use to wakeup from the STOP mode
* LL_GPIO_PIN_4 supplies power to the MCU
*/
GPIOB->MODER = 0xFFFFFFFF &
GPIO_MODE_SET(LL_GPIO_PIN_9, LL_GPIO_MODE_INPUT) &
GPIO_MODE_SET(LL_GPIO_PIN_4, LL_GPIO_MODE_OUTPUT) &
GPIO_MODE_SET(LL_GPIO_PIN_3, LL_GPIO_MODE_INPUT);
GPIOC->MODER = 0xFFFFFFFF;
GPIOH->MODER = 0xFFFFFFFF;
LL_IOP_GRP1_DisableClock(LL_IOP_GRP1_PERIPH_GPIOA | LL_IOP_GRP1_PERIPH_GPIOB |
LL_IOP_GRP1_PERIPH_GPIOC | LL_IOP_GRP1_PERIPH_GPIOH);
}
void lower_power_peripheral_enable(void)
{
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA | LL_IOP_GRP1_PERIPH_GPIOB |
LL_IOP_GRP1_PERIPH_GPIOC | LL_IOP_GRP1_PERIPH_GPIOH);
/* dummy write */
GPIOA->MODER = power_config.gpioa_mode;
GPIOA->MODER = power_config.gpioa_mode;
GPIOB->MODER = power_config.gpiob_mode;
GPIOC->MODER = power_config.gpioc_mode;
GPIOH->MODER = power_config.gpioh_mode;
USART1->CR1 |= (USART_CR1_RE | USART_CR1_TE | USART_CR1_RXNEIE);
USART2->CR1 |= (USART_CR1_RE | USART_CR1_TE | USART_CR1_RXNEIE);
LL_USART_Enable(USART1);
LL_USART_Enable(USART2);
LL_I2C_Enable(I2C1);
LL_I2C_Enable(I2C2);
}
void low_power_mode_enter(void)
{
int key;
watchdog_feed();
key = irq_lock();
lower_power_peripheral_disable();
/* HSI16 oscillator is wake-up from stop clock */
LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI);
/* Clear Wake-up Flags */
LL_PWR_ClearFlag_WU();
/* Enable ultra low power mode */
LL_PWR_EnableUltraLowPower();
/* Enable the fast wake up from Ultra low power mode */
LL_PWR_EnableFastWakeUp();
LL_PWR_SetRegulModeLP(LL_PWR_REGU_LPMODES_LOW_POWER);
/* Set STOP mode when CPU enters deepsleep */
LL_PWR_SetPowerMode(LL_PWR_MODE_STOP);
/* Set SLEEPDEEP bit of Cortex System Control Register */
LL_LPM_EnableDeepSleep();
/* Request Wait For Interrupt */
__WFI();
LL_LPM_DisableSleepOnExit();
/* Clear SLEEPDEEP bit of Cortex System Control Register */
LL_LPM_EnableSleep();
/* Set the voltage regulator back to main mode (PWR_CR_LPSDSR) */
LL_PWR_SetRegulModeLP(LL_PWR_REGU_LPMODES_MAIN);
/* Reset system clock */
stm32_clock_control_init(NULL);
lower_power_peripheral_enable();
irq_unlock(key);
}
