【STM32 + CubeMX】低功耗 -- SLEEP 睡眠模式

STM32 低功耗共有三种模式:

SLEEP 睡眠模式 、STOP 停止模式、STANDBY 待机模式。

三种模式的 操作图解 地址:

本篇,重点拆解 SLEEP 睡眠模式 的代码实现。

本篇,资源参考:

目录

[一、 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 */ 区域声明:
cpp 复制代码
uint8_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回调函数、网口接收回调函数,等等。
  • 在回调函数的末行,增加 唤醒标志置位。
cpp 复制代码
void 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;                                                             // 设置唤醒标志,通知主循环执行任务
   
}

重要提醒:

  1. 闹钟默认为单次触发,循环触发必须重新配置,以配合实现循环触发+唤醒。
  2. 禁止在中断回调函数里调用低功耗函数,极易造成程序卡死或死循环。
  3. 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所有操作:标志、进入、唤醒、执行唤醒任务,都已处理好了。


四、验证&测试

编译、下载后,串口输出顺序如下(理解低功耗的核心):

  1. printf:① 即将进入 SLEEP 模式!
  2. 系统进入睡眠,CPU 暂停
  3. 5 秒后 闹钟触发中断
  4. printf:闹钟 B 中断触发!!
  5. printf:② 已成功退出 SLEEP 模式!
  6. printf:③ 执行唤醒后任务...
  7. 任务完成,无标志 → 再次进入睡眠

这个执行顺序,是理解 STM32 低功耗流程的关键。


文毕。欢迎指正、交流。

相关推荐
芯芯点灯2 小时前
LIS2DW12驱动,功耗,数据可视化
驱动开发·单片机
Nice__J2 小时前
Mcu架构以及原理——2.Cortex-M流水线与指令集
单片机·嵌入式硬件·架构
小白橘颂2 小时前
【C语言】基础概念梳理(一)
c语言·开发语言·stm32·单片机·mcu·物联网·51单片机
aini_lovee3 小时前
SIM7600模块STM32控制程序
stm32·单片机·嵌入式硬件
是翔仔呐3 小时前
第13章 超声波测距传感器驱动:HC-SR04底层原理与C语言实现
c语言·开发语言·单片机·嵌入式硬件·gitee
鲨辣椒100864 小时前
51单片机核心钉子户——温度采集模块
单片机·嵌入式硬件·51单片机
xiangw@GZ5 小时前
CapSense底层逻辑:硬件设计规范
单片机·嵌入式硬件·设计规范
Nice__J5 小时前
Mcu架构以及原理——3.存储器架构
单片机·嵌入式硬件·架构
weiyvyy5 小时前
嵌入式硬件接口的定义与作用
单片机·嵌入式硬件·信息与通信·信息化系统