今天投了一个嵌入式岗,面试官小哥哥问我怎么实现单片机低功耗
嗯,单片机是低功耗了,我CPU干烧了,感觉自己当场就红温了。😭
其实感觉这篇可以写一个《十分钟速通0offer,让面试官无话可说》,但这样显得我很菜,很没有自己的思考,万一以后真的有面试官点开我的简历上的小小的链接,看我的博客呢,哈哈。😳
所以干脆学习了一下单片机低功耗的设计,然后简单讲一下自己的启发。💪
首先了解一下电源控制的结构

STM32 的工作电压通过VDD引脚输入,一般为2.0~3.6V。
STM32通过内置的电压调节器提供所需的1.8V电源。
STM32为ADC提供了一个独立的电源供电,这样可以过滤和屏蔽来自印刷电路板上的毛刺干扰,提高转换的精确度。ADC系统的电源引脚为VDDA,接地引脚为VSSA。
为了确保模拟输入为低电压时,能够获得更好的精度,用户可以连接一个独立的外部参考电压ADC到VREF+和VREF-引脚上。在VREF+的电压范围为0V~VDDA,VREF-引脚必须连接到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 converter(ADC,模数转换器)、Temp.sensor(温度传感器)、Reset block(复位模块)、PLL(锁相环)。 - 电源与参考 :由
V_DDA(模拟电源)和V_SSA(模拟地)供电,参考电压V_REF+/V_REF-范围覆盖 0V 到 VDDA(适配不同模拟输入范围)。 - 设计意图 :模拟电路对噪声敏感,独立电源域可减少数字电路的噪声干扰,保证
ADC精度、PLL稳定性等。
PLL锁相环是嵌入式系统中 时钟管理的核心组件 ,其作用可概括为:
频率变换 :倍频或分频,满足不同模块的时钟需求;
相位同步 :确保时钟信号的稳定性和低抖动;
系统优化:结合电源管理,平衡性能与功耗。
2. (VDD) 主电源域(典型 3.3V)
- 模块 :
- I/O Ring :
I/O接口电路,负责外部信号收发。 - STANDBY circuitry :待机电路(唤醒逻辑、独立看门狗
IWDG),实现低功耗待机和异常唤醒。 - Voltage Regulator :电压调节器,将 VDD(
3.3V)转换为1.8V,给下一级数字域供电。 - Low voltage detector :低电压检测器,监测 VDD 电压,过低时触发待机/复位,保护系统。
- I/O Ring :
3. 1.8V 数字核心域
- 模块 :
Core(处理器核心)、Memories(存储器)、digital peripherals(数字外设,如定时器、通信接口等)。 - 设计意图 :数字电路(尤其高频核心)采用更低电压(
1.8V),降低功耗和发热,提升能效;由主域的调节器供电,简化电源管理。
4. Backup domain(备份域)
- 模块 :
- LSE crystal 32K osc :
32.768kHz低速外部振荡器(给RTC提供时钟)。 - BKP registers:备份寄存器(掉电时保存关键数据)。
- RCC BDCR register:复位与时钟控制的备份域寄存器(保存时钟配置)。
- RTC:实时时钟(持续计时,不受主电源掉电影响)。
- LSE crystal 32K osc :
- 电源 :可切换为
V_BAT(电池)或V_DD(主电源)。主电源正常时由 VDD 供电,主电源掉电时自动切换到电池 ,保证备份域持续运行(如RTC计时、数据保存)。
设计逻辑
- 噪声隔离 :模拟域(VDDA)与数字域(VDD/1.8V)分开,避免数字噪声干扰模拟信号(如
ADC采样)。 - 功耗分层 :核心域用低压(
1.8V)降功耗,主域负责I/O和电源转换,备份域仅保留关键功能(低功耗待机)。 - 掉电保护:备份域通过电池备份,确保RTC、关键寄存器在主电源中断时不丢失数据,实现"断电记忆"。
低功耗模式
在STM32复位以后,微控制器处于运行状态,此时HCLK为CPU提供时钟,内核开始执行程序代码。
当CPU不需继续运行任务时,可以设置为低功耗模式来节省功耗。STM32的低功耗模式如下:
1. 睡眠模式(SLEEP-NOW 或 SLEEP-ON-EXIT)
- 进入操作 :
WFI(Wait For Interrupt,等待中断):CPU等待中断触发,进入睡眠。WFE(Wait For Event,等待事件):CPU等待"唤醒事件"(如外部信号、定时器触发等),进入睡眠。
- 唤醒方式 :
WFI模式:任一中断 均可唤醒(如外部中断、定时器中断等)。WFE模式:需特定 唤醒事件 (由硬件或软件标记的事件,如GPIO电平变化)。
- 时钟影响 :
- 1.8V区域时钟 :
CPU时钟关闭,但 其他外设时钟(如ADC、定时器)仍运行 (仅CPU暂停)。 - VDD区域时钟:无特殊处理。
- 1.8V区域时钟 :
- 电压调节器 :保持 开启(功耗中等,因为电源未深度关闭)。
2. 停机模式(Stop Mode)
- 进入条件 :需同时设置
PDDS(Power Down Deep Sleep,深度睡眠电源-down)+LPDS(Low Power Deep Sleep,低功耗深度睡眠)位,- 配合
SLEEPDEEP位(启用深度睡眠), - 再通过
WFI或WFE触发进入。
- 唤醒方式 :任一外部中断(需在外部中断寄存器中预先配置允许的中断源)。
- 时钟影响 :
- 1.8V区域时钟 :所有
1.8V域的时钟(含CPU、外设)完全关闭 ,且 HSI(高速内部时钟)、HSE(高速外部时钟)振荡器停止(彻底断供时钟)。 - VDD区域时钟:无影响。
- 1.8V区域时钟 :所有
- 电压调节器 :可 配置为"开/关" (通过
PWR_CR寄存器设置),灵活控制功耗(关时功耗更低,但唤醒后需要重新稳定电源)。
3. 待机模式(Standby Mode)
- 进入条件 :需设置
PDDS位 +SLEEPDEEP位,- 配合
WFI或WFE触发(无需LPDS位,与停机模式的核心差异)。
- 唤醒方式 :仅支持 特定高优先级事件 :
WKUP引脚的上升沿(唤醒引脚),RTC警告事件(实时时钟的闹钟/警告),NRST引脚的外部复位,IWDG(独立看门狗)复位。
- 时钟影响 :同停机模式(
1.8V域时钟全关,HSI、HSE振荡器停止)。 - 电压调节器 :强制关闭(功耗最低,因为电源彻底断供,唤醒后需重新初始化电源)。
模式对比总结
| 维度 | 睡眠模式 | 停机模式 | 待机模式 |
|---|---|---|---|
| 功耗等级 | 中等(仅CPU暂停) | 低(时钟全关,电源可配) | 极低(电源彻底关闭) |
| 唤醒速度 | 最快(无需重启时钟/电源) | 中等(需恢复时钟,电源可选) | 最慢(需重启电源+时钟) |
| 应用场景 | 短时暂停,快速响应 | 中度低功耗,需保留部分功能 | 长时休眠,极致省电 |
电源控制(PWR)的编程方法
STM32微处理器提高了强大的电源控制能力,可用于电源管理和低功耗模式选择。
通过相应的寄存器可以实现灵活多变的功能配置。
ST公司还提供了完善的电源控制(PWR)接口库函数,其位于stm32f10x_pwr.c,对应的头文件为stm32f10x_pwr.h。
PWR_DeInit函数:用于将PWR外围寄存器复位为默认值。PWR_BackupAccessCmd函数:用于使能或者禁用RTC和备份寄存器的访问。PWR_PVDCmd函数:用于使能或者禁用电源电压探测器(PVD)。PWR_PVDLevelConfig函数:用于配置由电源电压探测器检测的电压门限值。PWR_WakeUpPinCmd函数:用于使能或者禁用唤醒引脚的功能。PWR_EnterSTOPMode函数:用于进入STOP模式。PWR_EnterSTANDBYMode函数:用于进入STANDBY模式。PWR_GetFlagStatus函数:用于获取指定PWR标志位的状态。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的外设复位功能对其进行复位:
- 使能复位信号 (
ENABLE):向PWR模块发送复位脉冲,强制寄存器清零。 - 禁用复位信号 (
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:控制复位信号的开启或关闭。
- 执行流程 :
- 当
ENABLE时,RCC向PWR模块发送复位信号,PWR寄存器被强制清零。 - 当
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:允许访问备份区域(RTC和BKP寄存器)。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是有效值(ENABLE或DISABLE),防止非法参数导致寄存器错误配置。 -
实现 :通过固件库中的宏
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的物理地址为0x40007000(APB1外设基址 +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)NewState将ENABLE/DISABLE(本质是1/0)转换为32位无符号整数,写入位带地址,等效于直接操作PWR_CR的DBP位。
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寄存器
}
- 参数检查
assert(IS_PWR_PVD_LEVEL(PWR_PVDLevel))用于确保输入的PWR_PVDLevel是合法值。- 合法值范围 :通常对应芯片手册中定义的电压级别枚举值(如
PWR_PVDLevel_0、PWR_PVDLevel_1等,每个级别对应不同的电压阈值,例如STM32中常见阈值有2.0V、2.2V、2.5V等)。
- 合法值范围 :通常对应芯片手册中定义的电压级别枚举值(如
- 寄存器操作
PWR->CR:PWR控制寄存器,其中 PLS位(第7-5位) 用于配置PVD的电压检测级别。- 清除PLS位 :
tmpreg &= CR_PLS_Mask使用掩码(如0x1F800000)清除CR寄存器中的PLS位,避免旧配置影响新值。 - 设置新级别 :
tmpreg |= PWR_PVDLevel将输入的电压级别值写入PLS位,完成配置。
关键知识点
-
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
-
-
寄存器操作原理
- 通过"读-改-写"操作修改寄存器,确保其他位不受影响。
CR_PLS_Mask是一个宏定义,用于屏蔽PLS位以外的位(如#define CR_PLS_Mask ((uint32_t)0x1F800000))。
PWR_WakeUpPinCmd 函数用于控制唤醒引脚(Wake-Up Pin)的使能状态
该功能主要用于从 STANDBY模式 或 STOP模式 等低功耗模式中唤醒系统。
- 启用或禁用唤醒引脚功能 :
ENABLE:使能指定的唤醒引脚(如WKUP1或WKUP2),允许其通过上升沿信号唤醒处于低功耗模式的芯片。DISABLE:禁用唤醒引脚(默认状态),此时引脚信号无法触发唤醒。
关键寄存器位
- PWR控制与状态寄存器(PWR_CSR)的
EWUPx位 :- 不同
STM32型号支持的唤醒引脚数量不同(如STM32F103支持1个唤醒引脚WKUP1,对应EWUP位;STM32F407支持2个引脚WKUP1和WKUP2,对应EWUP1和EWUP2位)。 - 以
STM32F103为例,EWUP位(PWR_CSR位8)控制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模式,系统时钟自动恢复。
- 通过 EXTI中断/事件 (如按键、定时器触发)或 唤醒引脚(WKUP) 的上升沿退出
关键寄存器
- PWR控制寄存器(PWR_CR) :
- LPDS位(位21) :设置调节器模式(仅
STM32F4/F7等支持低功耗调节器的型号):LPDS=0:正常模式(默认)。LPDS=1:低功耗模式。
- PDDS位(位22) :控制深度睡眠时的电源状态(与
SLEEPDEEP配合):PDDS=0:睡眠模式下调节器保持运行。PDDS=1:STOP模式下调节器根据LPDS配置工作。
- LPDS位(位21) :设置调节器模式(仅
- 系统控制寄存器(SCB_SysCtrl,属于Cortex-M内核寄存器) :
- SLEEPDEEP位(位1) :
SLEEPDEEP=0:进入睡眠模式(SLEEP)。SLEEPDEEP=1:进入深度睡眠模式(STOP/STANDBY)。
- SLEEPDEEP位(位1) :
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_WFI或PWR_STOPEntry_WFE。
2. CR_DS_Mask 的作用
-
定义示例 :
c#define CR_DS_Mask ((uint32_t)~(PWR_CR_PDDS | PWR_CR_LPDS)) // 0xFFFFFF3F(假设PDDS=22,LPDS=21)- 清除
PDDS和LPDS位,避免干扰其他位(如PVD配置)。 - 通过
PWR_Regulator参数写入新的LPDS值(若支持低功耗调节器)。
- 清除
3. 深度睡眠位(SLEEPDEEP)的设置
- 位带操作解析 :
SCB_SysCtrl对应Cortex-M内核的系统控制寄存器(地址0xE000ED10),SLEEPDEEP位(位1)通过位带操作直接置1,使能深度睡眠模式。-
等效于传统操作:
cSCB->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)级别。
- 关闭所有时钟(包括
-
唤醒方式 :
- 仅能通过 外部唤醒事件 触发:
- WKUP引脚上升沿 (
WKUP1/WKUP2,需提前使能)。 - RTC闹钟事件 (需配置
RTC在STANDBY模式下运行)。 - NRST引脚复位(硬复位)。
- WKUP引脚上升沿 (
- 仅能通过 外部唤醒事件 触发:
-
关键差异(与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清除)。
-
代码等效操作 :
cPWR->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_SysCtrl的SLEEPDEEP位,等效于:cSCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; // SCB_SCR_SLEEPDEEP_Msk = 0x00000002
-
4. 执行WFI指令(__WFI())
- 指令作用 :
- WFI(Wait For Interrupt) :等待中断触发,触发后若
SLEEPDEEP=1且PDDS=1,则进入STANDBY模式。 - 替代方案 :若使用事件唤醒,可调用
__WFE()(Wait For Event),但STANDBY模式通常通过中断唤醒。
- WFI(Wait For Interrupt) :等待中断触发,触发后若
PWR_GetFlagStatus 函数用于读取电源控制(PWR)模块的标志位状态
主要用于判断低功耗模式(如STANDBY、STOP)的进入或唤醒事件是否发生。
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模式唤醒:
cif (PWR_GetFlagStatus(PWR_FLAG_SB) == SET) { PWR_ClearFlag(PWR_FLAG_SB); // 清除STANDBY标志(部分型号需通过此函数清除) // 执行唤醒后初始化操作 }
PWR_ClearFlag 函数用于清除电源控制(PWR)模块的标志位
这些标志位通常与低功耗模式(如STANDBY、STOP)的唤醒事件相关。
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_CSR的WU位(位2):cPWR->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若频繁失效(唤醒缓存重建),可设置「软过期」+「惰性更新」:缓存过期后不立即重建,而是等请求到来时再更新(类似单片机等任务攒够再唤醒),减少无效唤醒开销。