cancel
Showing results for 
Search instead for 
Did you mean: 

Watchdog not wakeup from the STOP mode occasionally

guojing
Associate

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);
}

 

 

2 REPLIES 2
Sarra.S
ST Employee

Hello @guojing

I'm suspecting this limitation: Exiting Stop mode on a reset event is not possible when HSI16 is the clock system and it is selected as wakeup clock in the errata sheet as you're using HSI16 as wakeup source clock. 

The suggested workaround is to switch to MSI instead.

Please let me know your feedback! Thank you 

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.

Sarra.S
ST Employee

Hello @guojing

Any updates on this issue?

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.