Search code examples
stm32standby

How to setup the standby mode on a STM32F4 MCU running an RTOS and waking it up after?


I like to put my STM32F412 into deep sleep mode and wake it after by pressing a button. This code should run together with an RTOS(Zephyr). So when executing the code, to put the device into deep sleep, other tasks etc. are active.

So I am looking for a bullet proof approach, that makes it sure that the STM32F412 goes to standby and wakeup after.

so far my (not working code):

#define POWER_WAKEUP_PIN LL_PWR_WAKEUP_PIN2

// set PC0 as input gpio
LL_GPIO_SetPinPull(GPIOC, LL_GPIO_PIN_0, LL_GPIO_PULL_NO);
LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_0, LL_GPIO_MODE_INPUT);

// activate EXTI line 0
LL_EXTI_InitTypeDef EXTI_InitStruct = {0};

LL_EXTI_DisableIT_0_31(LL_EXTI_LINE_ALL_0_31);

EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_0;
EXTI_InitStruct.LineCommand = ENABLE;
EXTI_InitStruct.Mode = LL_EXTI_MODE_EVENT;
EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_RISING;
LL_EXTI_Init(&EXTI_InitStruct);

// put to standby
LL_PWR_DisableWakeUpPin(POWER_WAKEUP_PIN);
LL_PWR_ClearFlag_WU();
LL_PWR_EnableWakeUpPin(POWER_WAKEUP_PIN);

LL_PWR_SetPowerMode(LL_PWR_MODE_STANDBY);
LL_LPM_EnableDeepSleep();
__WFI();

Its using the stm32 LL HAL. Any ideas what is missing


Solution

  • I found a working solution. It consists of 2 parts:

    1. add an idle thread that calls "__WFI()". In my case I use the main thread of Zephyr and set its prio to the lowest of the system threads. If nothing to do for the system this thread is active and it does nothing else than sleeping.
    2. setup a function that enables the RTC which triggers a wake up event after some time (in my case 1sec). Put the MCU to sleep mode with . Clk the wakeup circut via RTC. After the wakeup event check the wakeup condition is checked. In my case I check the state of the wakeup pin and an other pin. If the condition is full filled, a reset is generated.

    Following some code snipes for the ZephyrRTOS:

    void rtc_setupDeepsleepWakeUp(bool on) {
        if (true == on) {
            /*
            Programming the wakeup timer
            The following sequence is required to configure or change the wakeup timer auto-reload
            value (WUT[15:0] in RTC_WUTR):
            1. Clear WUTE in RTC_CR to disable the wakeup timer.
            2. Poll WUTWF until it is set in RTC_ISR to make sure the access to wakeup auto-reload
            counter and to WUCKSEL[2:0] bits is allowed. It takes 1 to 2 RTCCLK clock cycles
            (due to clock synchronization).
            3. Program the wakeup auto-reload value WUT[15:0] and the wakeup clock selection
            (WUCKSEL[2:0] bits in RTC_CR).Set WUTE in RTC_CR to enable the timer again.
            The wakeup timer restarts down-counting. Due to clock synchronization, the WUTWF
            bit is cleared up to 2 RTCCLK clocks cycles after WUTE is cleared.
    
            note on step 3:
            32768Hz -> 32768 decrements per second
            now calc the value for the timer
            32768/ 16 = 0x800
            0x800 -> counter -> 1sec
            */
            LL_RCC_EnableRTC();
            HAL_RTCEx_SetWakeUpTimer_IT(&rtc.hrtc, 0x800, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
            irq_enable(RTC_WKUP_IRQn);
        } else {
            HAL_RTCEx_DeactivateWakeUpTimer(&rtc.hrtc);
            irq_disable(RTC_WKUP_IRQn);
        }
    }
    
    void power_sleep(void) {
        __WFI();
    }
    
    #define THREAD_PRIO_idle 12
    
    void power_deepSleep(void) {
        unsigned int key;
    
        LOG_INF("preparing device to deep sleep");
    
        power_disableAllPeriphals();
        power_clearAllInterrupts();
        gpio_enableWakeupButton();
    
        rtc_setupDeepsleepWakeUp(true);
        for (;;) {
            HAL_SuspendTick();
            HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
            HAL_ResumeTick();
            // break condition of low power mode
            // button pressed or usb insert
            if (
                (0 == gpio_get_powerButton_state()) ||
                (1 == gpio_get_vbus_state())
                ) {
                rtc_setupDeepsleepWakeUp(false);
                sys_reboot(SYS_REBOOT_COLD);
            }
        }
        rtc_setupDeepsleepWakeUp(false);
    
        sys_reboot(SYS_REBOOT_COLD);
    
        LOG_ERR("ahhhh something went wrong");
    }
    
    
    void main(void)
    {
        power_recoverFromDeepSleep();
        LOG_INF("start");
    ...
        LOG_INF("start completed");
    
        LOG_DBG("set main prio to lowest(idle)");
        k_thread_priority_set(k_current_get(), THREAD_PRIO_idle);
    
        while(1) {
            power_sleep();
        }
    }