【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 低功耗流程的关键。


文毕。欢迎指正、交流。

相关推荐
国科安芯18 分钟前
航天器多路并联大功率电源系统设计与ASP4644均流特性分析
单片机·嵌入式硬件·fpga开发·安全性测试
梁朝辉5 小时前
STM32H750VBT6中ADCINP与INN什么区别
stm32·单片机·嵌入式硬件
崇山峻岭之间7 小时前
单片机无感FOC驱动之ADC
单片机·嵌入式硬件
Ligocious7 小时前
1.点亮一颗小小的LED
单片机·嵌入式硬件
KaifuZeng11 小时前
通信与接口协议面试八、CAN通信
单片机·嵌入式硬件·面试·通信与接口协议
智源单片机设计12 小时前
基于单片机的直流电机双闭环调速系统设计
单片机·嵌入式硬件
kebidaixu12 小时前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片详解
stm32
kebidaixu13 小时前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片调试实录:四个关键问题与解决方案
stm32
wuyk5551 天前
24. C 语言模块化:不是拆几个.c 文件那么简单
c语言·开发语言·stm32·单片机