今天投了一个嵌入式岗,面试官小哥哥问我怎么实现单片机低功耗
嗯,单片机是低功耗了,我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
)模块的寄存器映射。
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
模块恢复正常工作状态。
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
:禁止访问(默认状态),此时对备份区域的写操作会被忽略。
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
检查参数,该宏通常定义为:#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
的计算方式为:
其中,#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
(默认状态)。
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
)的触发等级
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
:禁用(默认状态)。
- 不同
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):
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
为有效值,如:#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
的作用
- 定义示例:
#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,使能深度睡眠模式。- 等效于传统操作:
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
)级别。
- 关闭所有时钟(包括
- 唤醒方式:
- 仅能通过 外部唤醒事件 触发:
- WKUP引脚上升沿(
WKUP1
/WKUP2
,需提前使能)。 - RTC闹钟事件(需配置
RTC
在STANDBY
模式下运行)。 - NRST引脚复位(硬复位)。
- WKUP引脚上升沿(
- 仅能通过 外部唤醒事件 触发:
- 关键差异(与STOP模式对比):
特性 STOP模式 STANDBY模式 SRAM/寄存器保留 是 否(仅备份域保留) 唤醒后状态 恢复运行(非复位) 系统复位(从启动代码运行) 典型功耗(STM32F4) 约1.5mA(正常调节器) 约1.5μA
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清除)。
- 代码等效操作:
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
配置调节器模式)。
- 宏定义示例:
#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
位,等效于:SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; // SCB_SCR_SLEEPDEEP_Msk = 0x00000002
- SCB_SysCtrl寄存器(系统控制寄存器)的SLEEPDEEP位(位1):
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
)的进入或唤醒事件是否发生。
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
为有效值,避免访问无效寄存器位。 - 宏定义示例:
不同#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模式唤醒:if (PWR_GetFlagStatus(PWR_FLAG_SB) == SET) { PWR_ClearFlag(PWR_FLAG_SB); // 清除STANDBY标志(部分型号需通过此函数清除) // 执行唤醒后初始化操作 }
PWR_ClearFlag
函数用于清除电源控制(PWR
)模块的标志位
这些标志位通常与低功耗模式(如STANDBY
、STOP
)的唤醒事件相关。
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
为支持的标志位,例如:#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
):
因此,代码中的移位操作可能是 错误的 或 针对非标准寄存器映射的定制化实现。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
若频繁失效(唤醒缓存重建),可设置「软过期」+「惰性更新」:缓存过期后不立即重建,而是等请求到来时再更新(类似单片机等任务攒够再唤醒),减少无效唤醒开销。