STM32 低功耗共有三种模式:

Sleep 睡眠模式 、Stop 停止模式、Standb 待机模式。

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

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

资源参考:

     

目录

一、 STM32 低功耗模式 概述

二、工程准备

三、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 事件

唤醒后

不需要重新初始化 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 标志判断是待机唤醒还是普通复位

适合场景 长时间休眠、对功耗要求极致的电池供电设备、遥测终端等

注意 1 :WAKEUP 引脚(PA0)仅在 Standby 模式下具备硬件自动唤醒功能,Sleep / Stop 模式下 PA0 不具备此功能,必须配置为 EXTI 外部中断 才能唤醒。

注意 2 :IWDG 独立看门狗,只能复位芯片!不能唤醒!因为 IWDG 不会产生中断。

     


   

二、工程准备

  

1、基础工程

三种低功耗模式均支持 RTC 闹钟唤醒,因此统一使用 RTC 闹钟作为工程模板,添加低功耗测试。

不熟悉 RTC 闹钟的朋友,参考教程:

本文所用工程模板:【 Keil 工程文件 -- RTC 闹钟 】

当然,你可以使用自己的工程模板、需要的唤醒方式:

  • SLEEP 睡眠模式:任意中断事件唤醒。
  • STOP 停止模式:外部 EXTI 、RTC闹钟
  • STANDBY模式:WAKEUP 引脚 PA0 的上升沿 、 RTC 闹钟

因此,SLEEP的唤醒方式不限哪种中断,按需选择即可。

    

2、烧录问题

STM32 进入Stop、Standby 模式后,  芯片停止工作,无法正常响应烧录信号,可能导致烧录失败。    

  

STLink 仿真器的解决方法:

  • 方法1:按着板子复位按键,不放手,keil 点击 Load 后,马上放开复位按键。利用复位后的空档期识别烧录信号。
  • 方法2:BOOT0接线3.3V,按一次复位按键,烧录。拔掉BOOT0接线,按一次复位按键,即可。  

  

CMSIS-DAP 仿真器的解决方法:

  • Connect这一项:选择 under Reset,即可随时烧录。

      

JLink 仿真器:

  • Stop 模式,不影响,随时可烧录。
  • Standby 模式,方法1:按着板子复位按键,不放手,keil 点击 Load 后,马上放开复位按键。利用复位后的空档期识别烧录信号。
  • Standby 模式,方法2:BOOT0接线3.3V,按一次复位按键,烧录。拔掉BOOT0接线,按一次复位按键,即可。  

      


  

三、Sleep 睡眠模式 代码实现

   

进入低功耗模式本身很简单,调用 HAL 库函数即可。

真正的重点在于:进入前的准备、唤醒后的处理、以及低功耗进入时机。

这些流程的配合,是低功耗入门最容易卡住的地方。

本节以 RTC 闹钟 B 周期性唤醒 为例,演示常用的 Sleep 进入、唤醒、任务处理流程。

    

1、声明自定义唤醒标志

Sleep 模式没有硬件唤醒标志位,唤醒后也无需清零任何硬件标志。

实际工程中,我们通常自定义一个软件标志变量,用于在中断和主循环之间传递 “已唤醒” 状态,属于状态机思路。

具体操作

  • main.c文件的 /* USER CODE BEGIN PV */ 区域声明: 
uint8_t wakeUpFlag = 0;

       

2、进入 Sleep 模式

进入 Sleep 只需要一行核心函数,但必须做好前后处理,否则无法正常休眠。

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 ()
/******************************************************************************
 * 函  数: 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回调函数、网口接收回调函数,等等。
  • 在回调函数的末行,增加 唤醒标志置位。
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 */  内添加:
/** 检查是否被唤醒 **/
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 低功耗流程的关键。

      


       

文毕。欢迎指正、交流。

  

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐