【零基础勇闯嵌入式岗】从单片机低功耗中获得的启发

今天投了一个嵌入式岗,面试官小哥哥问我怎么实现单片机低功耗

嗯,单片机是低功耗了,我CPU干烧了,感觉自己当场就红温了。😭

其实感觉这篇可以写一个《十分钟速通0offer,让面试官无话可说》,但这样显得我很菜,很没有自己的思考,万一以后真的有面试官点开我的简历上的小小的链接,看我的博客呢,哈哈。😳

所以干脆学习了一下单片机低功耗的设计,然后简单讲一下自己的启发。💪


首先了解一下电源控制的结构


STM32 的工作电压通过VDD引脚输入,一般为2.0~3.6V

STM32通过内置的电压调节器提供所需的1.8V电源。

STM32ADC提供了一个独立的电源供电,这样可以过滤和屏蔽来自印刷电路板上的毛刺干扰,提高转换的精确度。ADC系统的电源引脚为VDDA,接地引脚为VSSA

为了确保模拟输入为低电压时,能够获得更好的精度,用户可以连接一个独立的外部参考电压ADCVREF+VREF-引脚上。在VREF+的电压范围为0V~VDDAVREF-引脚必须连接到VSSA

并不是所有的封装都有VREF+VREF-引脚,对于一些少引脚的封装,ADC的电源和地的引脚与内部的ADC参考电压的引脚相连。

STM32 还提供了备份电池的功能,当主电源VDD掉电后,通过VBAT引脚为实时时钟(RTC)和备份寄存器提供电源,可以保存备份寄存器的内容。

切换到 VBAT 供电由复位模块中的掉电复位功能控制。

如果应用中没有使用外部电池,VBAT必须连接到VDD引脚上。

STM32内部提供了电压调节器,复位后电压调节器总是使能的。根据应用方式它以如下三种不同的模式工作。

  • 运转模式:电压调节器以正常功耗模式提供1.8V电源,供内核,内存和外设使用。
  • 停止模式:电压调节器以低功耗模式提供1.8V电源,以保存STM32 内部寄存器和SRAM的内容。
  • 待机模式:电压调节器停止供电。除了STM32内部的备用电路和备份领域以外,STM32寄存器和SRAM的内容全部丢失。

1. (VDDA) 模拟电源域

  • 模块A/D converterADC,模数转换器)、Temp.sensor(温度传感器)、Reset block(复位模块)、PLL(锁相环)。
  • 电源与参考 :由 V_DDA(模拟电源)和 V_SSA(模拟地)供电,参考电压 V_REF+/V_REF- 范围覆盖 0V 到 VDDA(适配不同模拟输入范围)。
  • 设计意图 :模拟电路对噪声敏感,独立电源域可减少数字电路的噪声干扰,保证ADC精度、PLL稳定性等。

PLL 锁相环是嵌入式系统中 时钟管理的核心组件 ,其作用可概括为:
频率变换 :倍频或分频,满足不同模块的时钟需求;
相位同步 :确保时钟信号的稳定性和低抖动;
系统优化:结合电源管理,平衡性能与功耗。

2. (VDD) 主电源域(典型 3.3V

  • 模块
    • I/O RingI/O接口电路,负责外部信号收发。
    • STANDBY circuitry :待机电路(唤醒逻辑、独立看门狗 IWDG),实现低功耗待机和异常唤醒。
    • Voltage Regulator :电压调节器,将 VDD3.3V)转换为 1.8V,给下一级数字域供电。
    • Low voltage detector :低电压检测器,监测 VDD 电压,过低时触发待机/复位,保护系统。

3. 1.8V 数字核心域

  • 模块Core(处理器核心)、Memories(存储器)、digital peripherals(数字外设,如定时器、通信接口等)。
  • 设计意图 :数字电路(尤其高频核心)采用更低电压(1.8V),降低功耗和发热,提升能效;由主域的调节器供电,简化电源管理。

4. Backup domain(备份域)

  • 模块
    • LSE crystal 32K osc32.768kHz 低速外部振荡器(给RTC提供时钟)。
    • BKP registers:备份寄存器(掉电时保存关键数据)。
    • RCC BDCR register:复位与时钟控制的备份域寄存器(保存时钟配置)。
    • RTC:实时时钟(持续计时,不受主电源掉电影响)。
  • 电源 :可切换为 V_BAT(电池)或 V_DD(主电源)。主电源正常时由 VDD 供电,主电源掉电时自动切换到电池 ,保证备份域持续运行(如RTC计时、数据保存)。

设计逻辑

  1. 噪声隔离 :模拟域(VDDA)与数字域(VDD/1.8V)分开,避免数字噪声干扰模拟信号(如ADC采样)。
  2. 功耗分层 :核心域用低压(1.8V)降功耗,主域负责I/O和电源转换,备份域仅保留关键功能(低功耗待机)。
  3. 掉电保护:备份域通过电池备份,确保RTC、关键寄存器在主电源中断时不丢失数据,实现"断电记忆"。

低功耗模式

STM32复位以后,微控制器处于运行状态,此时HCLKCPU提供时钟,内核开始执行程序代码。

CPU不需继续运行任务时,可以设置为低功耗模式来节省功耗。STM32的低功耗模式如下:
1. 睡眠模式(SLEEP-NOW 或 SLEEP-ON-EXIT)

  • 进入操作
    • WFIWait For Interrupt,等待中断):CPU等待中断触发,进入睡眠。
    • WFEWait For Event,等待事件):CPU等待"唤醒事件"(如外部信号、定时器触发等),进入睡眠。
  • 唤醒方式
    • WFI 模式:任一中断 均可唤醒(如外部中断、定时器中断等)。
    • WFE 模式:需特定 唤醒事件 (由硬件或软件标记的事件,如GPIO电平变化)。
  • 时钟影响
    • 1.8V区域时钟CPU时钟关闭,但 其他外设时钟(如ADC、定时器)仍运行 (仅CPU暂停)。
    • VDD区域时钟:无特殊处理。
  • 电压调节器 :保持 开启(功耗中等,因为电源未深度关闭)。

2. 停机模式(Stop Mode)

  • 进入条件 :需同时设置
    • PDDSPower Down Deep Sleep,深度睡眠电源-down)+ LPDSLow Power Deep Sleep,低功耗深度睡眠)位,
    • 配合 SLEEPDEEP 位(启用深度睡眠),
    • 再通过 WFIWFE 触发进入。
  • 唤醒方式任一外部中断(需在外部中断寄存器中预先配置允许的中断源)。
  • 时钟影响
    • 1.8V区域时钟 :所有1.8V域的时钟(含CPU、外设)完全关闭 ,且 HSI(高速内部时钟)、HSE(高速外部时钟)振荡器停止(彻底断供时钟)。
    • VDD区域时钟:无影响。
  • 电压调节器 :可 配置为"开/关" (通过 PWR_CR 寄存器设置),灵活控制功耗(关时功耗更低,但唤醒后需要重新稳定电源)。

3. 待机模式(Standby Mode)

  • 进入条件 :需设置
    • PDDS 位 + SLEEPDEEP 位,
    • 配合 WFIWFE 触发(无需LPDS位,与停机模式的核心差异)。
  • 唤醒方式 :仅支持 特定高优先级事件
    • WKUP 引脚的上升沿(唤醒引脚),
    • RTC警告事件(实时时钟的闹钟/警告),
    • NRST 引脚的外部复位,
    • IWDG(独立看门狗)复位。
  • 时钟影响 :同停机模式(1.8V域时钟全关,HSIHSE振荡器停止)。
  • 电压调节器强制关闭(功耗最低,因为电源彻底断供,唤醒后需重新初始化电源)。

模式对比总结

维度 睡眠模式 停机模式 待机模式
功耗等级 中等(仅CPU暂停) 低(时钟全关,电源可配) 极低(电源彻底关闭)
唤醒速度 最快(无需重启时钟/电源) 中等(需恢复时钟,电源可选) 最慢(需重启电源+时钟)
应用场景 短时暂停,快速响应 中度低功耗,需保留部分功能 长时休眠,极致省电

电源控制(PWR)的编程方法

STM32微处理器提高了强大的电源控制能力,可用于电源管理和低功耗模式选择。

通过相应的寄存器可以实现灵活多变的功能配置。

ST公司还提供了完善的电源控制(PWR)接口库函数,其位于stm32f10x_pwr.c,对应的头文件为stm32f10x_pwr.h

  1. PWR_DeInit函数:用于将PWR外围寄存器复位为默认值。
  2. PWR_BackupAccessCmd函数:用于使能或者禁用RTC和备份寄存器的访问。
  3. PWR_PVDCmd函数:用于使能或者禁用电源电压探测器(PVD)。
  4. PWR_PVDLevelConfig函数:用于配置由电源电压探测器检测的电压门限值。
  5. PWR_WakeUpPinCmd函数:用于使能或者禁用唤醒引脚的功能。
  6. PWR_EnterSTOPMode函数:用于进入STOP模式。
  7. PWR_EnterSTANDBYMode函数:用于进入STANDBY模式。
  8. PWR_GetFlagStatus函数:用于获取指定PWR标志位的状态。
  9. PWR_ClearFlag函数:用于清除PWR挂起标志位。

GPIO寄存器结构

STM32的固件开发中,PWR_TypeDef 结构体用于定义电源控制(PWR)模块的寄存器映射。

c 复制代码
typedef struct
{
  vu32 CR;  // 电源控制寄存器(Power Control Register)
  vu32 CSR; // 电源控制状态寄存器(Power Control Status Register)
} PWR_TypeDef;
  • vu32 类型说明
    vu32 是固件库中定义的类型别名,等价于 volatile uint32_t

    • volatile:确保编译器不会优化对寄存器的访问,保证每次操作都直接读写硬件寄存器。
    • uint32_t:表示32位无符号整数,与STM32寄存器宽度一致。
  • 成员功能概述

    成员 寄存器全称 功能描述
    CR 电源控制寄存器 配置低功耗模式(STOP/STANDBY)、备份区域访问、可编程电压检测(PVD)等。
    CSR 电源控制状态寄存器 读取电源状态标志(如唤醒标志、PVD触发状态、低功耗模式进入状态等)。

关键寄存器位详解

1. 电源控制寄存器(CR,地址偏移:0x00)

位段 名称 描述
CR.LPDS 位1 STOP模式下调压器配置: 0 = 正常模式(调压器开启) 1 = 低功耗模式(调压器关闭)
CR.PDDS 位2 STANDBY模式下调压器配置: 0 = 正常模式 1 = 关闭调压器(最低功耗)
CR.DBP 位8 备份区域访问使能: 0 = 禁止访问备份寄存器(BKP)和RTC 1 = 允许访问
CR.PVDE 位4 可编程电压检测(PVD)使能: 0 = 禁止 1 = 使能
CR.PLS[7:5] 位7-5 PVD阈值等级配置(共5级,如PWR_PVDLevel_2对应约2.9V阈值)

2. 电源控制状态寄存器(CSR,地址偏移:0x04)

位段 名称 描述
CSR.WUF 位2 唤醒标志: 1 = 系统从STANDBY模式唤醒(需软件清零)
CSR.SB 位3 STANDBY模式标志: 1 = 系统处于STANDBY模式
CSR.PVDO 位5 PVD输出状态: 1 = 电源电压低于设定阈值

PWR_DeInit() 函数用于复位电源控制(PWR)模块,将其寄存器恢复为默认值。

实现原理

STM32的外设复位通过 RCC(复位与时钟控制)模块 实现。PWR模块挂载在 APB1总线 上,因此可通过RCC的外设复位功能对其进行复位:

  1. 使能复位信号ENABLE):向PWR模块发送复位脉冲,强制寄存器清零。
  2. 禁用复位信号DISABLE):移除复位脉冲,使PWR模块恢复正常工作状态。
c 复制代码
void PWR_DeInit(void)
{
  // 步骤1:使能PWR模块的复位信号
  RCC_APB1PeriphResetCmd(RCC_APB1Periph_PWR, ENABLE);
  
  // 步骤2:禁用PWR模块的复位信号
  RCC_APB1PeriphResetCmd(RCC_APB1Periph_PWR, DISABLE);
}

关键函数 RCC_APB1PeriphResetCmd

  • 功能 :控制APB1总线上外设的复位状态。
  • 参数
    • RCC_APB1Periph_PWR:指定复位对象为PWR模块。
    • ENABLE/DISABLE:控制复位信号的开启或关闭。
  • 执行流程
    1. ENABLE 时,RCCPWR模块发送复位信号,PWR寄存器被强制清零。
    2. DISABLE 时,复位信号停止,PWR模块退出复位状态,寄存器保持默认值。

PWR模块默认寄存器值

1. 电源控制寄存器(CR)

位段 默认值 描述
CR.LPDS 0 STOP模式下调压器为正常模式(调压器开启)
CR.PDDS 0 STANDBY模式下调压器为正常模式
CR.DBP 0 禁止访问备份寄存器(BKP)和RTC
CR.PVDE 0 可编程电压检测(PVD)功能禁用

2. 电源控制状态寄存器(CSR)

位段 默认值 描述
CSR.WUF 0 唤醒标志未置位(系统未从STANDBY模式唤醒)
CSR.SB 0 系统未处于STANDBY模式
CSR.PVDO 0 电源电压正常(未低于PVD阈值)

PWR_BackupAccessCmd 函数用于控制备份区域(Backup Domain)的访问权限。

备份区域通常包括实时时钟(RTC)和备份寄存器(BKP),这些资源在系统复位或进入低功耗模式时仍需保留数据,因此需要独立的权限控制。

  • 当需要配置RTC或操作备份寄存器时,必须先调用 ENABLE 开启访问权限。
  • 操作完成后,建议调用 DISABLE 关闭权限,以防止意外修改。

关键寄存器位

  • PWR_CR寄存器的DBP位(位8)
    • DBP = 1:允许访问备份区域(RTCBKP寄存器)。
    • DBP = 0:禁止访问(默认状态),此时对备份区域的写操作会被忽略。
c 复制代码
void PWR_BackupAccessCmd(FunctionalState NewState)
{
  // 步骤1:参数合法性检查
  assert(IS_FUNCTIONAL_STATE(NewState));  // 确保NewState为ENABLE或DISABLE

  // 步骤2:通过位带操作直接修改DBP位
  *(vu32 *) CR_DBP_BB = (u32)NewState;
}

1. 参数检查 assert(IS_FUNCTIONAL_STATE)

  • 目的 :确保输入参数 NewState 是有效值(ENABLEDISABLE),防止非法参数导致寄存器错误配置。

  • 实现 :通过固件库中的宏 IS_FUNCTIONAL_STATE 检查参数,该宏通常定义为:

    c 复制代码
    #define IS_FUNCTIONAL_STATE(STATE) ((STATE == DISABLE) || (STATE == ENABLE))

2. 位带操作 *(vu32 *) CR_DBP_BB = ...

  • 位带操作(Bit-Banding)原理
    STM32的位带技术允许将外设寄存器的某一位映射到一个唯一的内存地址,通过访问该地址即可直接操作对应位,无需读写整个寄存器。

    • 优点:代码更简洁,操作具有原子性(避免中断干扰)。
  • CR_DBP_BB 的定义
    CR_DBP_BB 是一个宏,用于计算PWR_CR寄存器中DBP位(位8)的位带地址。假设:

    • PWR_CR的物理地址为 0x40007000APB1外设基址 + PWR模块偏移)。
    • 位带区基址为 0x42000000(外设位带区,不同芯片可能不同,需参考《STM32参考手册》)。
      CR_DBP_BB 的计算方式为:
    c 复制代码
    #define PWR_CR_ADDR  ((uint32_t)0x40007000)  // PWR_CR物理地址
    #define BITBAND_PERIPH_BASE  ((uint32_t)0x42000000)  // 外设位带基址
    #define CR_DBP_POS  8  // DBP位位于PWR_CR的第8位
    #define CR_DBP_BB  (BITBAND_PERIPH_BASE + (PWR_CR_ADDR - PERIPH_BASE) * 32 + CR_DBP_POS * 4)

    其中,PERIPH_BASE 为APB1外设基址(通常为 0x40000000)。

  • 指针强制类型转换
    (vu32 *) CR_DBP_BB 将位带地址转换为 volatile uint32_t 类型的指针,确保直接操作内存时的类型安全和volatile特性(防止编译器优化)。

  • 赋值操作
    (u32)NewStateENABLE/DISABLE(本质是 1/0)转换为32位无符号整数,写入位带地址,等效于直接操作PWR_CRDBP位。

PWR_PVDCmd 函数用于控制可编程电压检测器(PVD)的使能状态。

PVD 用于监测电源电压(VDD),当电压低于设定阈值时触发中断,常用于系统低电压保护或电源状态监控。

  • 启用或禁用PVD功能
    • ENABLE:使能PVD,开始监测电源电压,当电压低于阈值时触发中断(通过EXTI线16)。
    • DISABLE:禁用PVD,停止电压监测。

关键寄存器位

  • PWR_CR寄存器的PVDE位(位4)
    • PVDE = 1:使能PVD,允许电压检测。
    • PVDE = 0:禁用PVD(默认状态)。
c 复制代码
void PWR_PVDCmd(FunctionalState NewState)
{
  // 步骤1:参数合法性检查
  assert(IS_FUNCTIONAL_STATE(NewState));  // 确保NewState为ENABLE或DISABLE

  // 步骤2:通过位带操作直接修改PVDE位
  *(vu32 *) CR_PVDE_BB = (u32)NewState;
}

PWR_PVDLevelConfig 函数用于配置电源电压检测(PVD)的触发等级

c 复制代码
void PWR_PVDLevelConfig(u32 PWR_PVDLevel)
{
  u32 tmpreg = 0;                          // 定义临时寄存器变量
  assert(IS_PWR_PVD_LEVEL(PWR_PVDLevel));   // 检查输入参数是否合法
  tmpreg = PWR->CR;                        // 读取PWR控制寄存器(CR)的值
  tmpreg &= CR_PLS_Mask;                   // 清除CR寄存器中的PLS位(电压级别配置位)
  tmpreg |= PWR_PVDLevel;                  // 设置新的电压级别到PLS位
  PWR->CR = tmpreg;                        // 将更新后的值写回CR寄存器
}
  1. 参数检查
    assert(IS_PWR_PVD_LEVEL(PWR_PVDLevel)) 用于确保输入的 PWR_PVDLevel 是合法值。
    • 合法值范围 :通常对应芯片手册中定义的电压级别枚举值(如 PWR_PVDLevel_0PWR_PVDLevel_1 等,每个级别对应不同的电压阈值,例如STM32中常见阈值有2.0V2.2V2.5V等)。
  2. 寄存器操作
    • PWR->CRPWR控制寄存器,其中 PLS位(第7-5位) 用于配置PVD的电压检测级别。
    • 清除PLS位tmpreg &= CR_PLS_Mask 使用掩码(如 0x1F800000)清除CR寄存器中的PLS位,避免旧配置影响新值。
    • 设置新级别tmpreg |= PWR_PVDLevel 将输入的电压级别值写入PLS位,完成配置。

关键知识点

  1. PVD(电源电压检测)

    • 作用:监测系统电源电压(VDD),当电压低于设定阈值时,触发中断或复位,防止系统在低电压下异常工作。

    • 阈值配置:通过PLS位选择不同的电压阈值,具体阈值需参考芯片数据手册(如STM32F10x系列中,PLS位与电压的对应关系如下):

      PLS值 电压阈值(典型值)
      PWR_PVDLevel_0 2.0V
      PWR_PVDLevel_1 2.2V
      PWR_PVDLevel_2 2.5V
      PWR_PVDLevel_3 2.7V
      PWR_PVDLevel_4 2.9V
      PWR_PVDLevel_5 3.0V
  2. 寄存器操作原理

    • 通过"读-改-写"操作修改寄存器,确保其他位不受影响。
    • CR_PLS_Mask 是一个宏定义,用于屏蔽PLS位以外的位(如 #define CR_PLS_Mask ((uint32_t)0x1F800000))。

PWR_WakeUpPinCmd 函数用于控制唤醒引脚(Wake-Up Pin)的使能状态

该功能主要用于从 STANDBY模式STOP模式 等低功耗模式中唤醒系统。

  • 启用或禁用唤醒引脚功能
    • ENABLE:使能指定的唤醒引脚(如WKUP1WKUP2),允许其通过上升沿信号唤醒处于低功耗模式的芯片。
    • DISABLE:禁用唤醒引脚(默认状态),此时引脚信号无法触发唤醒。

关键寄存器位

  • PWR控制与状态寄存器(PWR_CSR)的EWUPx
    • 不同STM32型号支持的唤醒引脚数量不同(如STM32F103支持1个唤醒引脚WKUP1,对应EWUP位;STM32F407支持2个引脚WKUP1WKUP2,对应EWUP1EWUP2位)。
    • STM32F103为例,EWUP位(PWR_CSR8)控制WKUP1引脚的使能:
      • EWUP = 1:使能WKUP1引脚的唤醒功能。
      • EWUP = 0:禁用(默认状态)。
c 复制代码
void PWR_WakeUpPinCmd(FunctionalState NewState)
{
  // 步骤1:参数合法性检查
  assert(IS_FUNCTIONAL_STATE(NewState));  // 确保NewState为ENABLE或DISABLE

  // 步骤2:通过位带操作直接修改EWUP位
  *(vu32 *) CSR_EWUP_BB = (u32)NewState;
}

PWR_EnterSTOPMode 函数用于使芯片进入STOP模式

通过配置电源调节器(Regulator)和进入方式(WFI/WFE)实现不同等级的低功耗优化。
STOP模式特性

  • 功耗优化
    • 关闭CPU和所有外设时钟,但保留SRAM、寄存器内容及备份域(Backup Domain)。
    • 通过配置电源调节器(PWR_Regulator)可进一步降低功耗:
      • 正常模式(PWR_Regulator_Normal):调节器保持全速运行,唤醒速度快,功耗较高。
      • 低功耗模式(PWR_Regulator_LowPower) :调节器进入低功耗状态,唤醒时需重新调整,功耗更低(仅部分型号支持,如STM32F4/F7)。
  • 唤醒方式
    • 通过 EXTI中断/事件 (如按键、定时器触发)或 唤醒引脚(WKUP) 的上升沿退出STOP模式,系统时钟自动恢复。

关键寄存器

  1. PWR控制寄存器(PWR_CR)
    • LPDS位(位21) :设置调节器模式(仅STM32F4/F7等支持低功耗调节器的型号):
      • LPDS=0:正常模式(默认)。
      • LPDS=1:低功耗模式。
    • PDDS位(位22) :控制深度睡眠时的电源状态(与SLEEPDEEP配合):
      • PDDS=0:睡眠模式下调节器保持运行。
      • PDDS=1STOP模式下调节器根据LPDS配置工作。
  2. 系统控制寄存器(SCB_SysCtrl,属于Cortex-M内核寄存器)
    • SLEEPDEEP位(位1)
      • SLEEPDEEP=0:进入睡眠模式(SLEEP)。
      • SLEEPDEEP=1:进入深度睡眠模式(STOP/STANDBY)。
c 复制代码
void PWR_EnterSTOPMode(u32 PWR_Regulator, u8 PWR_STOPEntry)
{
  u32 tmpreg = 0;
  // 步骤1:参数合法性检查
  assert(IS_PWR_REGULATOR(PWR_Regulator));  // 检查调节器模式(Normal/LowPower)
  assert(IS_PWR_STOP_ENTRY(PWR_STOPEntry)); // 检查进入方式(WFI/WFE)

  // 步骤2:配置PWR_CR寄存器的PDDS和LPDS位
  tmpreg = PWR->CR;
  tmpreg &= CR_DS_Mask;  // 清除PDDS(位22)和LPDS(位21)
  tmpreg |= PWR_Regulator;  // 写入调节器模式(LPDS位)
  PWR->CR = tmpreg;        // 保存配置

  // 步骤3:设置Cortex-M内核进入深度睡眠模式
  *(vu32 *) SCB_SysCtrl |= SysCtrl_SLEEPDEEP_Set;  // 置位SLEEPDEEP位

  // 步骤4:根据进入方式触发STOP模式
  if (PWR_STOPEntry == PWR_STOPEntry_WFI)
  {
    __WFI();  // 等待中断(WFI指令触发STOP模式)
  }
  else
  {
    __WFE();  // 等待事件(WFE指令触发STOP模式)
  }
}

1. 参数检查宏定义

  • IS_PWR_REGULATOR
    确保PWR_Regulator为有效值,如:

    c 复制代码
    #define IS_PWR_REGULATOR(REGULATOR) \
      ((REGULATOR == PWR_Regulator_Normal) || (REGULATOR == PWR_Regulator_LowPower))
  • IS_PWR_STOP_ENTRY
    确保进入方式为PWR_STOPEntry_WFIPWR_STOPEntry_WFE

2. CR_DS_Mask 的作用

  • 定义示例

    c 复制代码
    #define CR_DS_Mask    ((uint32_t)~(PWR_CR_PDDS | PWR_CR_LPDS))  // 0xFFFFFF3F(假设PDDS=22,LPDS=21)
    • 清除PDDSLPDS位,避免干扰其他位(如PVD配置)。
    • 通过PWR_Regulator参数写入新的LPDS值(若支持低功耗调节器)。

3. 深度睡眠位(SLEEPDEEP)的设置

  • 位带操作解析
    SCB_SysCtrl对应Cortex-M内核的系统控制寄存器(地址0xE000ED10),SLEEPDEEP位(位1)通过位带操作直接置1,使能深度睡眠模式。
    • 等效于传统操作:

      c 复制代码
      SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;  // SCB_SCR_SLEEPDEEP_Msk = 0x00000002

4. __WFI() vs __WFE()

  • __WFI()(Wait For Interrupt)
    • 进入STOP模式后,仅当 中断(IRQ) 触发时唤醒(需提前使能对应EXTI中断并配置NVIC)。
  • __WFE()(Wait For Event)
    • 通过 事件(Event) 唤醒(如EXTI事件,无需中断控制器参与),功耗更低(无需清除中断标志)。

PWR_EnterSTANDBYMode 函数使芯片进入STANDBY模式(待机模式)

这是STM32功耗最低的工作模式。

STANDBY模式特性

  • 极致低功耗

    • 关闭所有时钟(包括HSI/HSE/RTC时钟,除非配置为保持运行)、内核、外设及SRAM
    • 仅保留 备份域寄存器(Backup Domain)可选的RTC时钟 (需额外配置),功耗可低至数微安(μA)级别。
  • 唤醒方式

    • 仅能通过 外部唤醒事件 触发:
      1. WKUP引脚上升沿WKUP1/WKUP2,需提前使能)。
      2. RTC闹钟事件 (需配置RTCSTANDBY模式下运行)。
      3. NRST引脚复位(硬复位)。
  • 关键差异(与STOP模式对比)

    特性 STOP模式 STANDBY模式
    SRAM/寄存器保留 否(仅备份域保留)
    唤醒后状态 恢复运行(非复位) 系统复位(从启动代码运行)
    典型功耗(STM32F4) 约1.5mA(正常调节器) 约1.5μA
c 复制代码
void PWR_EnterSTANDBYMode(void)
{
  // 步骤1:清除唤醒标志(CWUF)
  PWR->CR |= CR_CWUF_Set;  
  // 步骤2:设置PDDS位,选择STANDBY模式
  PWR->CR |= CR_PDDS_Set;  
  // 步骤3:使能深度睡眠模式(Cortex-M内核控制)
  *(vu32 *) SCB_SysCtrl |= SysCtrl_SLEEPDEEP_Set;  
  // 步骤4:执行WFI指令进入STANDBY模式
  __WFI();  
}

1. 清除唤醒标志(CR_CWUF_Set

  • 寄存器位解析

    • PWR_CR的CWUF位(位23):唤醒标志,进入STANDBY模式前需手动清除,否则可能导致模式进入失败。
    • 操作方式:通过写1清除(STM32寄存器特性:某些标志位需写1清除)。
  • 代码等效操作

    c 复制代码
    PWR->CR |= (1 << 23);  // 等价于宏定义CR_CWUF_Set

2. 设置PDDS位(CR_PDDS_Set

  • PDDS位(位22)的作用

    • PDDS=1:当SLEEPDEEP=1(深度睡眠)时,进入 STANDBY模式(而非STOP模式)。
    • PDDS=0:深度睡眠时进入STOP模式(由PWR_Regulator配置调节器模式)。
  • 宏定义示例

    c 复制代码
    #define CR_PDDS_Set    PWR_CR_PDDS  // PWR_CR_PDDS = 0x00004000(位22)

3. 使能深度睡眠模式(SLEEPDEEP

  • Cortex-M内核控制
    • SCB_SysCtrl寄存器(系统控制寄存器)的SLEEPDEEP位(位1)

      • SLEEPDEEP=0:普通睡眠模式(SLEEP)。
      • SLEEPDEEP=1:深度睡眠模式(STOP/STANDBY)。
    • 位带操作解析
      通过指针直接操作SCB_SysCtrlSLEEPDEEP位,等效于:

      c 复制代码
      SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;  // SCB_SCR_SLEEPDEEP_Msk = 0x00000002

4. 执行WFI指令(__WFI()

  • 指令作用
    • WFI(Wait For Interrupt) :等待中断触发,触发后若SLEEPDEEP=1PDDS=1,则进入STANDBY模式。
    • 替代方案 :若使用事件唤醒,可调用__WFE()Wait For Event),但STANDBY模式通常通过中断唤醒。

PWR_GetFlagStatus 函数用于读取电源控制(PWR)模块的标志位状态

主要用于判断低功耗模式(如STANDBYSTOP)的进入或唤醒事件是否发生。

c 复制代码
FlagStatus PWR_GetFlagStatus(u32 PWR_FLAG)
{
  FlagStatus bitstatus = RESET;
  // 步骤1:参数合法性检查
  assert(IS_PWR_GET_FLAG(PWR_FLAG));  // 确保PWR_FLAG为有效标志位

  // 步骤2:读取PWR_CSR寄存器并判断目标标志位
  if ((PWR->CSR & PWR_FLAG) != (u32)RESET)
  {
    bitstatus = SET;  // 标志位已置位
  }
  else
  {
    bitstatus = RESET;  // 标志位未置位
  }
  return bitstatus;
}

1. 参数检查宏 IS_PWR_GET_FLAG

  • 作用 :确保输入的PWR_FLAG为有效值,避免访问无效寄存器位。

  • 宏定义示例

    c 复制代码
    #define IS_PWR_GET_FLAG(FLAG) \
      ((FLAG == PWR_FLAG_SB) || (FLAG == PWR_FLAG_WU) || (FLAG == PWR_FLAG_CWUF))

    不同STM32型号支持的标志位可能不同(如F1系列与F4系列略有差异),需以芯片手册为准。

2. 寄存器读取与标志位判断

  • 核心逻辑

    通过位运算(PWR->CSR & PWR_FLAG)检查目标标志位是否为1:

    • 若结果非零(SET),说明标志位被置位(如系统刚从STANDBY模式唤醒)。
    • 若结果为零(RESET),说明标志位未激活。
  • 示例场景

    检查是否从STANDBY模式唤醒:

    c 复制代码
    if (PWR_GetFlagStatus(PWR_FLAG_SB) == SET)
    {
      PWR_ClearFlag(PWR_FLAG_SB);  // 清除STANDBY标志(部分型号需通过此函数清除)
      // 执行唤醒后初始化操作
    }

PWR_ClearFlag 函数用于清除电源控制(PWR)模块的标志位

这些标志位通常与低功耗模式(如STANDBYSTOP)的唤醒事件相关。

c 复制代码
void PWR_ClearFlag(u32 PWR_FLAG)
{
  assert(IS_PWR_CLEAR_FLAG(PWR_FLAG));  // 检查参数有效性
  PWR->CR |=  PWR_FLAG << 2;             // 向PWR_CR寄存器写入标志位(左移2位)
}

1. 参数检查宏 IS_PWR_CLEAR_FLAG

  • 作用 :确保输入的PWR_FLAG为支持的标志位,例如:

    c 复制代码
    #define IS_PWR_CLEAR_FLAG(FLAG) \
      ((FLAG == PWR_FLAG_WU) || (FLAG == PWR_FLAG_SB) || (FLAG == PWR_FLAG_CWUF))

2. PWR->CR |= PWR_FLAG << 2 的疑问

  • 寄存器选择争议

    • 常规设计
      唤醒标志(如PWR_FLAG_WU)通常存储在 PWR状态寄存器(PWR_CSR 中,而非控制寄存器(PWR_CR)。清除操作应针对PWR_CSR寄存器。
    • 代码潜在问题
      若代码中直接操作PWR_CR,可能存在寄存器选择错误。例如:
      • PWR_FLAG_WU对应PWR_CSR的位2(WU位),清除时需向PWR_CSR的该位写1
      • PWR_CR的位23(CWUF)用于清除唤醒标志的确认,但与PWR_FLAG_WU无直接关联。
  • 移位逻辑分析
    PWR_FLAG << 2 假设标志位对应PWR_CR的位2及以上,但根据STM32标准外设库,正确的清除操作通常无需移位。例如,清除PWR_FLAG_WU应直接写1到PWR_CSRWU位(位2):

    c 复制代码
    PWR->CSR |= PWR_FLAG_WU;  // 直接操作CSR寄存器,无需移位

    因此,代码中的移位操作可能是 错误的针对非标准寄存器映射的定制化实现

好了,现在谈谈低功耗单片机给我的启发

单片机思维 1

  • 不用的外设断电;
  • 单片机通过寄存器精准控制外设电源开关。

C++开发启示 1

  • 服务器中若客户端断开连接,立即用close(sockfd)释放套接字资源。
  • 文件读写后用fclose()RAII机制(智能指针)释放句柄。
  • std::unique_ptr替代裸指针,避免内存泄漏(类似单片机用寄存器精准控制单个外设的电源)。
  • 服务器连接池场景:用std::shared_ptr配合引用计数,当连接数降为0时自动释放资源,类似单片机检测到外设无任务时自动断电。

单片机思维 2

运行模式→睡眠模式→待机模式,通过中断触发状态切换,减少无效功耗。

C++开发启示 2

  • 服务器处理完客户端请求后,若客户端暂时无数据发送,将连接设为「休眠状态」(如设置SO_KEEPALIVE心跳包,而非直接关闭连接),类似单片机进入睡眠模式但保留中断响应能力。
  • 对空闲连接(超过30秒无操作)进入「待机状态」:暂停定时器、关闭非必要线程,但保留EPOLL事件监听(类似单片机保留RTC时钟用于唤醒)。
  • 当任务队列空时,让工作线程进入pthread_cond_wait休眠(类似单片机进入睡眠模式),有任务时通过pthread_cond_signal唤醒,避免线程空转消耗CPU(类似外设无效运行耗电)。

单片机思维 3

降频到低频时钟减少功耗,攒够数据再批量处理(如UART攒够一帧再发送)。

C++开发启示 3

  • 服务器高并发场景下,避免每条请求都实时写入日志(高频操作耗IO),用std::queue缓存日志条目,达到100条或500ms时批量写入磁盘,类似单片机攒够数据再通过UART发送:
  • 大文件上传时,若网络波动,可降低传输频率(如从100ms发包一次降到500ms),避免频繁重传消耗带宽(类似单片机降频后用精准延时保证任务完成),可用std::chrono::steady_clock控制发包间隔。

单片机思维 4

外部中断(如按键)高优先级,串口接收低优先级,避免低优先级任务阻塞CPU

C++开发启示 4

  • 服务器中用epoll监听事件时,将客户端连接断开(EPOLLRDHUP)设为高优先级(必须立即处理,释放资源),普通数据接收设为中优先级,心跳包接收设为低优先级,类似单片机中断优先级管理:

单片机思维 5

唤醒时重新初始化外设耗时耗电,尽量减少唤醒次数,攒够任务再处理。

C++开发启示 5

  • 服务器若用多线程处理请求,避免为每个请求创建新线程(线程创建/销毁开销大,类似单片机唤醒时的初始化成本),用线程池复用线程
  • Redis若频繁失效(唤醒缓存重建),可设置「软过期」+「惰性更新」:缓存过期后不立即重建,而是等请求到来时再更新(类似单片机等任务攒够再唤醒),减少无效唤醒开销。
相关推荐
aerror34 分钟前
使用mpu6500/6050, PID,互补滤波实现一个简单的飞行自稳控制系统
单片机·飞控
WIZnet 中国社区官方博客1 小时前
【第二十三章 IAP】
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·单片机外设·iap简介·iap程序设计
嵌入式@秋刀鱼2 小时前
《 第三章-招式初成》 C++修炼生涯笔记(基础篇)程序流程结构
linux·开发语言·数据结构·c++·笔记·visual studio code
HaiQinyanAN3 小时前
【学习笔记】重载和重写的注意事项
c++·笔记·学习
西北大程序猿3 小时前
服务器代码知识点补充
服务器·开发语言·网络·c++·网络协议
打不了嗝 ᥬ᭄5 小时前
进程控制
linux·运维·服务器·c++
tyl21105 小时前
凌科芯安国产安全MCU简介
单片机·嵌入式硬件·安全
yxc_inspire5 小时前
基于Qt的app开发第十四天
前端·c++·qt·app·面向对象·qss
郦7776 小时前
国产入门级32位单片机PY32F002A开发板
单片机·嵌入式硬件
电院工程师6 小时前
轻量级密码算法CHAM的python实现
python·嵌入式硬件·算法·安全·密码学