如何在 STM32F4 MCU 运行 RTOS 上设置待机模式并在之后唤醒它?

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

我喜欢让我的 STM32F412 进入深度睡眠模式,然后按下按钮将其唤醒。此代码应 运行 与 RTOS(Zephyr) 一起。所以在执行代码时,让设备进入深度睡眠,其他任务等都处于活动状态。

所以我正在寻找一种防弹方法,确保 STM32F412 进入待机状态并在之后唤醒。

到目前为止我的(不是工作代码):

#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();

它使用的是 stm32 LL HAL。任何想法缺少什么

我找到了可行的解决方案。它由两部分组成:

  1. 添加一个调用“__WFI()”的空闲线程。在我的例子中,我使用 Zephyr 的主线程并将其优先级设置为最低的系统线程。如果对系统无事可做,则此线程处于活动状态并且除了休眠之外什么都不做。
  2. 设置一个启用 RTC 的功能,该功能会在一段时间后(在我的例子中为 1 秒)触发唤醒事件。使用 将 MCU 置于睡眠模式。通过 RTC 调用唤醒电路。在唤醒事件检查之后,检查唤醒条件。在我的例子中,我检查了唤醒引脚和另一个引脚的状态。如果条件已满,则生成重置。

以下是 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();
    }
}