3. 进入深度睡眠模式
任务定义
本案例要求在STM32L4系列MCU上实现深度睡眠模式(Stop 2模式)的进入与唤醒。目标是通过RTC闹钟事件在10秒后唤醒MCU,验证系统能够从深度睡眠状态正确恢复并继续执行主程序。需要完成的工作包括:配置RTC闹钟中断、进入Stop 2模式、在唤醒后确认系统时钟恢复且程序流程正确衔接。
硬件连接与前置条件
使用NUCLEO-L476RG开发板,板载LED2(PB2引脚)用于指示运行状态。系统时钟配置为MSI 4MHz,RTC使用LSE外部32.768kHz晶振。需要确保开发板上LSE晶振的焊桥SB存在(默认已连接)。
代码实现
#include "stm32l4xx_hal.h"
/* 唤醒标志 */
volatile uint8_t wakeup_flag = 0;
/* RTC闹钟回调 */
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
wakeup_flag = 1;
}
/* RTC初始化 */
static void MX_RTC_Init(void)
{
RTC_HandleTypeDef hrtc = {0};
RTC_AlarmTypeDef sAlarm = {0};
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_RCC_RTC_ENABLE();
__HAL_RCC_LSE_CONFIG(RCC_LSE_ON);
while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == RESET) {}
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
HAL_RTC_Init(&hrtc);
/* 设置闹钟A:10秒后触发 */
sAlarm.AlarmTime.Hours = 0;
sAlarm.AlarmTime.Minutes = 0;
sAlarm.AlarmTime.Seconds = 10;
sAlarm.AlarmTime.SubSeconds = 0;
sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY;
sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
sAlarm.Alarm = RTC_ALARM_A;
HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);
}
/* GPIO初始化 */
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
/* LED2 - PB2 */
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* 进入Stop 2模式 */
static void Enter_Stop2_Mode(void)
{
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
/* 唤醒后重新配置系统时钟 */
SystemClock_Config();
}
/* 系统时钟配置(MSI 4MHz) */
static void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_4;
RCC_OscInitStruct.MSICalibrationValue = 0;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK |
RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_RTC_Init();
/* 主循环 */
while (1)
{
/* LED亮表示运行态 */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
HAL_Delay(100);
/* LED灭后进入深度睡眠 */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_Delay(100);
/* 进入Stop 2模式 */
Enter_Stop2_Mode();
/* 唤醒后执行 */
if (wakeup_flag)
{
wakeup_flag = 0;
/* LED闪烁3次表示唤醒成功 */
for (int i = 0; i < 3; i++)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
HAL_Delay(200);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_Delay(200);
}
}
}
}
代码说明
上述代码实现了完整的深度睡眠进入与唤醒流程。主程序先点亮LED 100ms表示运行状态,然后熄灭LED并调用HAL_PWREx_EnterSTOP2Mode()进入Stop 2模式。RTC闹钟在10秒后触发中断,将wakeup_flag置1并唤醒MCU。唤醒后系统时钟需要重新配置(因为Stop 2模式会丢失时钟配置),随后检测到唤醒标志后LED闪烁3次作为成功指示。
验证方法
将代码编译后烧录至NUCLEO-L476RG开发板,观察以下现象:
- 上电后LED2先亮100ms后熄灭;
- LED熄灭后约10秒(RTC闹钟周期),LED2开始闪烁3次(每次亮200ms、灭200ms);
- 闪烁结束后,程序回到主循环开头,LED再次亮100ms后熄灭,进入下一轮深度睡眠周期。
判定标准:若LED按照上述时序周期性动作,说明MCU成功进入Stop 2模式并在RTC闹钟触发后正确唤醒,系统时钟恢复且程序流程无异常。若LED无反应或闪烁时序不符,则检查RTC配置、LSE晶振是否起振以及中断回调是否正确注册。
功耗观测点
如需测量深度睡眠电流,可在LED熄灭后的10秒窗口内,使用万用表串联在开发板的IDD测量跳线处(J6),预期电流应降至微安级(典型值约1.1μA @ 3.3V,Stop 2模式)。注意测量时需断开调试器连接,否则调试接口会额外消耗电流。