如何在 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。任何想法缺少什么
我找到了可行的解决方案。它由两部分组成:
- 添加一个调用“__WFI()”的空闲线程。在我的例子中,我使用 Zephyr 的主线程并将其优先级设置为最低的系统线程。如果对系统无事可做,则此线程处于活动状态并且除了休眠之外什么都不做。
- 设置一个启用 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();
}
}
我喜欢让我的 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。任何想法缺少什么
我找到了可行的解决方案。它由两部分组成:
- 添加一个调用“__WFI()”的空闲线程。在我的例子中,我使用 Zephyr 的主线程并将其优先级设置为最低的系统线程。如果对系统无事可做,则此线程处于活动状态并且除了休眠之外什么都不做。
- 设置一个启用 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();
}
}