STM32 低功耗共有三种模式:
SLEEP 睡眠模式 、STOP 停止模式、STANDBY 待机模式。
三种模式的 操作图解 地址:
- 【STM32 + CubeMX】低功耗 -- SLEEP 睡眠模式
- 【STM32 + CubeMX】低功耗 -- STOP 停止模式 【待更】
- 【STM32 + CubeMX】低功耗 -- STANDBY 待机模式 【待更】
本篇,重点拆解 SLEEP 睡眠模式 的代码实现。
本篇,资源参考:
- Keil 工程链接 -- 【STM32F407 低功耗 --- SLEEP 睡眠模式】 待更中...
- 实验所用硬件 --【STM32F407VE 开发板】
目录
[一、 STM32 低功耗模式 概述](#一、 STM32 低功耗模式 概述)
[三、SLEEP模式 代码实现](#三、SLEEP模式 代码实现)
一、 STM32 三种低功耗模式 区别
1、睡眠模式 Sleep Mode
- 功耗:芯片工作电流约 10~20mA (参考值)
- 特点:CPU暂停在SLEEP这一行;外设正常工作;唤醒时间极快;
- 注意1:能被所有中断事件唤醒;若无法进入 SLEEP,优先检查是否未关闭 SysTick 等频繁中断。
- 注意2:被中断唤醒后,先进入对应中断函数执行,完成后再运行 SLEEP 下一行代码。
- 注意3:SLEEP 模式无硬件唤醒标志,唤醒后无需额外处理;通常自定义软件标志配合业务逻辑。
|------|------------------------------------------------------|
| 停止资源 | CPU内核停止运行、内核时钟关闭、程序暂停运行 |
| 保持资源 | 系统时钟、所有外设时钟保持运行 GPIO 口状态保持运行时的电平 内存、寄存器 数据保持不变 |
| 唤醒方式 | 任意中断 或唤醒事件均可唤醒 (如 GPIO中断、Systick、UART、RTC 等) |
| 唤醒后 | 程序正常运行,无需重新初始化时钟 / 外设。 |
| 适合场景 | 短时间无需 CPU 运算, 但需要外设(如串口、ADC、定时器)持续工作的场景 |
2、停止模式 Stop Mode
- 功耗:芯片工作电流约 1mA (参考值)
- 特点:系统时钟关闭;CPU和外设停止;内存数据保持;唤醒后仅需恢复系统时钟
|------|--------------------------------------------------------------------|
| 停止资源 | CPU 与所有外设停止工作 内核时钟、外设时钟,主时钟(HSE/HSI)被关闭 外设停止工作 |
| 保持资源 | 低速时钟(LSI/LSE,用于 RTC)继续运行 内核1.8V供电保持 内存、寄存器 数据保持不变; GPIO 口状态保持运行时电平 |
| 唤醒方式 | EXTI 外部中断 RTC 事件 IWDG 独立看门狗 |
| 唤醒后 | 不需要重新初始化 UART、SPI、ADC等外设 必须重新配置系统时钟(如启用 HSE/PLL) |
| 适合场景 | 需要低功耗、同时要求快速唤醒、数据不丢失的场合。 |
3、待机模式 Standby Mode
- 功耗:芯片工作电流约 4uA (参考值)
- 特点:几乎断电,数据丢失,唤醒后相当于复位
|------|--------------------------------------------------------------------------------------|
| 停止资源 | 内核1.8V电源关闭 内核与外设时钟关闭,但 LSI/LSE 可在备份域继续给 RTC 供电 内存、寄存器数据全部丢失 IO 口变为高阻态(模拟输入/浮空输入) |
| 保持资源 | 备份域(Backup Domain):备份寄存器、RTC 备份区域; 唤醒引脚(PA0) RTC 相关引脚 复位引脚 |
| 唤醒方式 | WAKEUP 引脚(PA0)上升沿 RTC 闹钟 |
| 唤醒之后 | 系统完全复位 程序从 main 函数从头开始执行。 可通过检查 PWR_CSR 的 SBF / WUF 标志判断是待机唤醒还是普通复位 |
| 适合场景 | 长时间休眠、对功耗要求极致的电池供电设备、遥测终端等 |
注意:
WAKEUP 引脚 (PA0) 的上升沿自动唤醒功能, 仅适用于 Standby 待机模式;
虽然同为 PA0,睡眠、停机模式下,PA0 无自动唤醒功能。但可配置为普通 EXTI 中断以唤醒。
二、工程准备
三种低功耗模式均支持 RTC 闹钟唤醒,因此统一使用 RTC 闹钟作为工程模板,添加低功耗测试。
不熟悉 RTC 闹钟的朋友,参考教程:
本文所用工程模板:【 Keil 工程文件 -- RTC 闹钟 】
当然,你可以使用自己的工程模板、需要的唤醒方式:
- SLEEP 睡眠模式:任意中断事件唤醒。
- STOP 停止模式:外部 EXTI 、RTC闹钟、IWDG 独立看门狗
- STANDBY模式:WAKEUP 引脚 PA0 的上升沿 、 RTC 闹钟
因此,SLEEP的唤醒方式不限哪种中断,按需选择即可。
三、SLEEP模式 代码实现
进入低功耗模式本身很简单,调用 HAL 库函数即可。
真正的重点在于:进入前的准备、唤醒后的处理、以及低功耗进入时机。
这些流程的配合,是低功耗入门最容易卡住的地方。
本节以RTC 闹钟 B 周期性唤醒 为例,演示常用的 SLEEP 进入、唤醒、任务处理流程。
1、声明自定义唤醒标志
SLEEP模式没有硬件唤醒标志位,唤醒后也无需清零任何硬件标志。
实际工程中,我们通常自定义一个软件标志变量,用于在中断和主循环之间传递 "已唤醒" 状态,属于状态机思路。
具体操作
- main.c文件的 /* USER CODE BEGIN PV */ 区域声明:
cppuint8_t wakeUpFlag = 0;
2、进入 SLEEP 模式
进入 SLEEP 只需要一行核心函数,但必须做好前后处理,否则无法正常休眠。
cpp
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); // 能被所有中断唤醒,包括Systick、UART、RTC闹钟等; 唤醒后,先进入所唤醒的中断,然后继续执行下一行
由于 SLEEP 可以被任何中断唤醒,特别是 SysTick,它 默认 1ms 中断一次,会导致芯片无法真正休眠。
因此需要封装一个函数,在进入 SLEEP 前必须关闭无关中断,唤醒后再恢复。
关键点再次提醒:
- SLEEP 模式能被任意中断唤醒,因此进入前要先关闭无关中断。
- SysTick 默认定时 1ms 中断一次,会持续唤醒 CPU,必须关闭!
- 唤醒后,也必须恢复 SysTick,否则 HAL 延时函数会失效。
具体操作:
- 在 main.c 的 /* USER CODE BEGIN 0 */ 区域添加函数: EnterSleepMode ()
cpp/****************************************************************************** * 函 数: EnterSleepMode * 功 能: 进入 SLEEP 低功耗模式 * 说 明: 1. CPU 暂停运行 * 2. 外设、时钟、内存、寄存器数据全部保持 * 3. 可被任意中断唤醒(RTC、UART、GPIO、EXTI 等) * 4. 唤醒后先执行中断服务函数,再继续执行休眠后的代码 * 5. 无法进入休眠时,优先检查是否未关闭 SysTick 等频繁中断 * 参 数: 无 * 返回值: 无 * 备 注: 最后修改_2026年03月24日 ******************************************************************************/ void EnterSleepMode(void) { printf("\r\n\r\n"); // 打印 提示 串口助手换行换行 printf("① 即将进入 SLEEP 模式!========== \r\n"); // 打印 提示进入SLEEP模式 /* 进入前:关闭可能频繁唤醒的中断源 */ HAL_SuspendTick(); // 关闭Systick中断。注意,SLEEP会被任何一个中断唤醒,如Systick中断、UART收发中断、CAN收发中断等等 /* 进入 SLEEP 模式:CPU暂停在此处 */ HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); // 能被所有中断唤醒,包括Systick、UART、RTC闹钟等; 唤醒后,先进入所唤醒的中断,然后继续执行下一行 /* 唤醒后:恢复中断源 */ HAL_ResumeTick(); // 恢复Systick中断 /* 被唤醒后,先进入所唤醒的中断,然后继续执行这里 */ printf("② 已退出 SLEEP 模式!\r\n"); // 打印 提示 退出SLEEP模式 }
这个函数,可以由特定条件触发进入SLEEP,而方案而定。
3、唤醒 SLEEP 模式 (以 RTC闹钟B 为例)
SLEEP 模式的唤醒规则非常简单:
只要产生任何中断,系统就会立即退出睡眠,继续运行代码。
唤醒流程:进入睡眠 → 中断触发 → 执行回调函数 → 继续执行SLEEP时的下一行
从 SLEEP 唤醒后,不用做额外工作(STOP、STANDBY 需要做额外处理, 稍后详述)。
这里以 RTC 闹钟B 的中断为例 :在 RTC 闹钟 B 回调函数中,设置唤醒标志即可。
注意:其它外设的中断,同理,如UART收发中断、网口中断等。
具体操作:
- 打开 RTC 闹钟B 的中断回调函数,
- 也可以通过其它回调函数操作,如UART回调函数、网口接收回调函数,等等。
- 在回调函数的末行,增加 唤醒标志置位。
cppvoid HAL_RTCEx_AlarmBEventCallback(RTC_HandleTypeDef *hrtc) { /* 闹钟触发提示 */ printf("闹钟B 中断触发!! \r\n"); /* 读取 RTC 时间日期 */ RTC_TimeTypeDef time = { 0 }; // 时间 时-分-秒 RTC_DateTypeDef data = { 0 }; // 日期 年-月-日 HAL_RTC_GetTime(hrtc, &time, RTC_FORMAT_BIN); // 读取时间数据; 必须先读时间后读日期; 写操作时,也必须先写时间后写日期,否则数据可能错乱 HAL_RTC_GetDate(hrtc, &data, RTC_FORMAT_BIN); // 读取日期数据; 必须先读时间后读日期; 写操作时,也必须先写时间后写日期,否则数据可能错乱 /* 打印 时间日期,便于观察调试 */ printf("当前时间: "); printf("%04d年%02d月%02d日 ", 2000 + data.Year, data.Month, data.Date); // 打印 日期 printf("%02d:%02d:%02d \r\n", time.Hours, time.Minutes, time.Seconds); // 打印 时间 /* 重新配置 5 秒后再次触发 */ RTC_SetAlarmB(5); // 设置下一个5秒再次触发 /* 【关键】设置唤醒标志,通知主循环执行任务 */ wakeUpFlag = 1; // 设置唤醒标志,通知主循环执行任务 }
重要提醒:
- 闹钟默认为单次触发,循环触发必须重新配置,以配合实现循环触发+唤醒。
- 禁止在中断回调函数里调用低功耗函数,极易造成程序卡死或死循环。
- UART 接收、GPIO 外部中断等,都可以用来唤醒 SLEEP。
编写完成后,参考如下:

4、主循环:唤醒后处理任务
在 while(1) 主循环中判断唤醒标志,执行业务逻辑,完成后再次进入睡眠。
具体操作:
- 在 while(1) 循环的 /* USER CODE BEGIN 3 */ 内添加:
cpp/** 检查是否被唤醒 **/ if(wakeUpFlag == 1) { /* ===== 唤醒后执行的任务 ===== */ printf("③ 唤醒后执行任务...!"); // 执行各种任务,如, 读取温度, 电机动作 等 /* 任务完成,清除标志 */ wakeUpFlag = 0; // 唤醒标志 置0,等待下一次唤醒 } else { /* 无任务时进入睡眠,降低功耗 */ EnterSleepMode(); // 进入 SLEEP 睡眠模式 }
说明:
这里的示范,else 分支会让系统上电后立即进入 Sleep;你可以改为由特定条件触发,以更灵活。
添加完成后,位置参考下图:

现在,SLEEP所有操作:标志、进入、唤醒、执行唤醒任务,都已处理好了。
四、验证&测试
编译、下载后,串口输出顺序如下(理解低功耗的核心):
- printf:① 即将进入 SLEEP 模式!
- 系统进入睡眠,CPU 暂停
- 5 秒后 闹钟触发中断
- printf:闹钟 B 中断触发!!
- printf:② 已成功退出 SLEEP 模式!
- printf:③ 执行唤醒后任务...
- 任务完成,无标志 → 再次进入睡眠
这个执行顺序,是理解 STM32 低功耗流程的关键。

文毕。欢迎指正、交流。