2. 进入睡眠模式
任务定义
本案例目标:在STM32L0系列MCU上实现通过RTC闹钟事件周期性唤醒的STOP模式低功耗运行。具体要求:MCU进入STOP模式后电流降至µA级,RTC闹钟每10秒触发一次唤醒,唤醒后翻转一次LED状态并重新进入睡眠,形成可观测的周期性唤醒循环。
硬件连接与测量拓扑
实现代码
以下代码基于STM32L0系列HAL库,使用RTC唤醒定时器(WakeUp Timer)实现周期唤醒,无需外部中断引脚即可完成定时唤醒功能。
// main.c — STM32L052 STOP模式RTC周期唤醒
#include "stm32l0xx_hal.h"
/* 私有变量 */
static RTC_HandleTypeDef hrtc;
/* LED控制引脚 — PB5 */
#define LED_PIN GPIO_PIN_5
#define LED_PORT GPIOB
/* 函数声明 */
static void SystemClock_Config(void);
static void RTC_WakeUp_Init(void);
static void GPIO_LED_Init(void);
int main(void)
{
/* 复位所有外设,初始化Flash接口和SysTick */
HAL_Init();
SystemClock_Config();
GPIO_LED_Init();
RTC_WakeUp_Init();
/* 初始LED状态:灭 */
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET);
while (1)
{
/* 进入STOP模式,RTC闹钟唤醒 */
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
/* 系统时钟从MSI重新启动 */
SystemClock_Config();
/* 唤醒后翻转LED */
HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
}
}
/**
* RTC初始化:配置为唤醒定时器模式,周期10秒
* 使用LSI时钟(37kHz)作为RTC时钟源
*/
static void RTC_WakeUp_Init(void)
{
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_RCC_RTC_ENABLE();
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 0x7F; /* 128分频 */
hrtc.Init.SynchPrediv = 0x00; /* 1分频 */
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
/* 使能唤醒定时器,周期 = (0xFFFF - 0x0F) / 128 ≈ 10秒 */
/* 实际计算:LSI≈37kHz,128分频后≈289Hz */
/* 10秒 ≈ 2890个计数,取0x0B4A = 2890 */
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0x0B4A, RTC_WAKEUPCLOCK_RTCCLK_DIV128);
}
/**
* GPIO初始化:PB5配置为推挽输出,驱动LED
*/
static void GPIO_LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);
}
/**
* 系统时钟配置:MSI 2.097MHz作为系统时钟
* 进入STOP模式前调用,唤醒后重新配置
*/
static void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/* MSI 2.097MHz */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_5; /* 2.097MHz */
RCC_OscInitStruct.MSICalibrationValue = 0;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/* AHB/APB时钟配置 */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK |
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;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/**
* RTC唤醒中断回调 — 空实现,仅用于清除中断标志
*/
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
/* 中断标志由HAL库自动清除 */
}
/**
* 错误处理
*/
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
关键配置说明
| 参数 | 值 | 说明 |
|---|---|---|
| RTC时钟源 | LSI (~37kHz) | STOP模式下保持运行,无需外部晶振 |
| 异步预分频 | 128 | RTC时钟 = 37kHz / 128 ≈ 289Hz |
| 唤醒计数值 | 0x0B4A (2890) | 唤醒周期 ≈ 2890 / 289 ≈ 10秒 |
| 系统时钟 | MSI 2.097MHz | 唤醒后快速启动,无需PLL稳定时间 |
| 低功耗模式 | STOP | 内核停止,SRAM和寄存器内容保持 |
编译与烧录
# 使用STM32CubeIDE或Makefile工程编译
# 关键编译选项(以arm-none-eabi-gcc为例)
arm-none-eabi-gcc -mcpu=cortex-m0plus -mthumb \
-DSTM32L052xx -IInc \
-c main.c -o main.o
# 链接生成固件
arm-none-eabi-ld -T stm32l052.ld main.o ... -o firmware.elf
# 通过ST-Link烧录
st-flash write firmware.bin 0x08000000
验证方法与判定标准
电流测量:将µA级电流表串联在MCU的VDD供电回路中(参考上方SVG拓扑),观察STOP模式下的稳态电流。判定标准:电流读数应小于5µA(典型值1-3µA)。若电流偏高,检查是否有GPIO悬空、未使用的外设未关闭或调试接口仍处于激活状态。
唤醒周期验证:用示波器探头连接PB5引脚,观察LED驱动波形。判定标准:应看到周期约10秒的方波翻转,高电平持续到下一次唤醒翻转。若周期偏差超过±2秒,需校准LSI频率或调整唤醒计数值。
功能完整性:程序运行后LED以10秒间隔持续翻转,说明MCU成功进入STOP模式并被RTC唤醒。若LED不翻转,检查RTC中断是否使能(NVIC)、唤醒定时器是否启动。
注意:以上验证方法描述的是预期操作流程与判定条件,实际测量数据需在目标硬件上执行后获得。