3. 时钟系统低功耗配置
任务定义
本案例面向STM32L0系列MCU,完成时钟系统低功耗配置。目标是将系统从默认的MSI时钟(2.097MHz)切换至LSE外部32.768kHz晶振驱动的LSI/LSE低速时钟域,并配置RTC每秒唤醒一次,其余时间MCU进入STOP模式,实现平均功耗低于µA级。需要交付可直接编译烧录的完整工程代码,并给出功耗验证方法。
硬件连接与时钟拓扑
硬件前提:PC14/PC15引脚外接32.768kHz晶振(负载电容6pF),NRST引脚保持高电平。
代码实现
以下代码基于STM32L0xx HAL库,使用LSE驱动RTC,配置唤醒定时器周期为1秒。系统初始化后立即进入STOP模式,每次唤醒翻转一次LED状态后重新进入STOP。
/* main.c - STM32L0 时钟低功耗配置 */
#include "stm32l0xx_hal.h"
/* RTC 句柄 */
RTC_HandleTypeDef hrtc;
/* LED 引脚 - PA5 */
#define LED_PIN GPIO_PIN_5
#define LED_PORT GPIOA
/* 系统时钟配置:MSI 2.097MHz (默认) */
static void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/* MSI 使能,中速档位 */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_5; /* 2.097 MHz */
RCC_OscInitStruct.MSICalibrationValue = 0;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
/* 时钟源选择 MSI,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 初始化:LSE 作为时钟源,1秒唤醒周期 */
static void RTC_Init(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RTC_WakeUpTypeDef wakeup_param = {0};
/* 使能 LSE 外部晶振 */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
/* 使能 RTC 时钟域 */
__HAL_RCC_RTC_ENABLE();
/* RTC 时钟源选择 LSE */
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC;
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) {
Error_Handler();
}
/* RTC 初始化 */
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127; /* 32768 / (127+1) = 256 Hz */
hrtc.Init.SynchPrediv = 255; /* 256 / (255+1) = 1 Hz */
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
if (HAL_RTC_Init(&hrtc) != HAL_OK) {
Error_Handler();
}
/* 配置唤醒定时器:周期 1 秒 */
wakeup_param.WakeUpClock = RTC_WAKEUPCLOCK_CK_SPRE_16BITS; /* 1Hz 时钟 */
wakeup_param.WakeUpCounter = 1; /* 计数 1 个 1Hz 周期 = 1 秒 */
if (HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, &wakeup_param) != HAL_OK) {
Error_Handler();
}
}
/* GPIO 初始化:PA5 推挽输出 */
static void GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_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);
}
/* RTC 唤醒中断回调 */
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
/* 翻转 LED 状态 */
HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
}
int main(void)
{
/* 复位所有外设,初始化 Flash 接口 */
HAL_Init();
/* 配置系统时钟为 MSI 2.097MHz */
SystemClock_Config();
/* 初始化 GPIO */
GPIO_Init();
/* 初始化 RTC(LSE 驱动,1秒唤醒) */
RTC_Init();
/* 使能 RTC 唤醒中断 */
HAL_NVIC_SetPriority(RTC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(RTC_IRQn);
/* 主循环 */
while (1)
{
/* 进入 STOP 模式,LSE 保持运行 */
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
/* 唤醒后重新配置系统时钟(STOP 模式会丢失 MSI 配置) */
SystemClock_Config();
}
}
/* RTC 中断服务函数 */
void RTC_IRQHandler(void)
{
HAL_RTCEx_WakeUpTimerIRQHandler(&hrtc);
}
/* 错误处理 */
void Error_Handler(void)
{
while (1) { }
}
关键配置说明
- LSE 使能:通过
RCC_OscInitStruct.LSEState = RCC_LSE_ON开启外部低速晶振,为RTC提供独立于系统时钟的计时源。 - 预分频配置:异步预分频127 + 同步预分频255,将32.768kHz分频至1Hz,作为唤醒定时器的计数时钟。
- 唤醒周期:
WakeUpCounter = 1,即每1秒产生一次唤醒中断。 - STOP模式进入:使用
HAL_PWR_EnterSTOPMode,选择低功耗稳压器模式,WFI指令等待唤醒事件。 - 唤醒后时钟恢复:STOP模式会关闭MSI,因此唤醒后必须重新调用
SystemClock_Config()恢复系统时钟。
工程文件与编译
需要以下文件构成完整工程:
main.c— 上述代码stm32l0xx_hal_conf.h— HAL库配置文件,使能RTC、PWR、GPIO模块stm32l0xx_it.c— 中断服务函数(包含RTC_IRQHandler)- 链接脚本及启动文件 — 由STM32CubeMX生成或手动添加
编译命令(使用arm-none-eabi-gcc + Makefile):
make clean && make
烧录:
st-flash write build/firmware.bin 0x08000000
验证方法与判定标准
功能验证:
- 将PA5连接LED(串联330Ω电阻到地),烧录程序后观察LED以1秒周期闪烁。
- 使用逻辑分析仪或示波器测量PA5波形,确认高电平持续时间为微秒级(唤醒执行时间),低电平持续约1秒(STOP模式时间)。
功耗验证:
- 断开调试器,使用万用表(µA档)串联在MCU供电回路中。
- 测量稳定后的平均电流,预期值应小于5µA(典型值2-3µA,取决于LSE和RTC功耗)。
- 判定标准:平均电流低于5µA且LED正常闪烁,即通过本案例验证。
判定标准:LED以1秒周期稳定闪烁,且供电电流≤5µA,即可判定时钟低功耗配置成功。