STM32的PWR

发布于:2025-08-07 ⋅ 阅读:(17) ⋅ 点赞:(0)

我们使用手机的时候屏幕亮起,我们页面进入操作不同APP。而当我们不需要使用时,如果手机屏幕一直维持亮起状态,那么手机电量是不是用的很快。所以一般情况下,我们使用手机后,它会自动息屏,或者我们把它关机掉。PWR也是这一相似作用,为了 在性能与功耗之间做出平衡,提供电源管理手段,使系统在满足运行需求的同时,尽可能节能、稳定、安全运行。

从上电到运行:STM32 的供电和启动流程

当 VDD 上电时:

步骤 模块/行为 说明
1 电源电压上升 VDD 通常为 3.3V,逐步达到稳态
2 复位电路监控 VDD(POR/PDR) POR(上电复位)和 PDR(掉电复位)确保电压稳定前不让系统启动
3 内部复位激活,MCU 初始化 时钟未启用,CPU处于复位状态
4 等待 VDD 稳定后,释放复位 系统从复位状态进入主程序启动流程
5 程序执行 Reset_Handler → main() 启动汇编初始化,跳转到主函数

掉电检测器(PDR)与上电复位(POR)

机制 触发条件 作用
POR(Power-On Reset) VDD 从 0V 上升超过阈值(约 1.8V) 确保芯片只在电压稳定后开始运行
PDR(Power-Down Reset) VDD 跌落低于阈值(~2.0V) 在掉电或电压不稳时强制系统复位
BOR(Brown-Out Reset) 更精细的电压跌落监控 有些 STM32 系列才有(F1 没有)

POR/PDR 是硬件级别电压检测电路,在 MCU 最开始的电源稳定过程中发挥作用,不依赖软件配置,也无法屏蔽。

PVD(Programmable Voltage Detector)详解

PVD 是一个可编程门限的电压比较器,作用是:

在 VDD 跌落之前发出中断预警

比 PDR 反应更早,允许用户软件自行保存数据/进入低功耗模式

时钟 & PVD 初始化函数

void PVD_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);       // 打开 PWR 时钟

    PWR_PVDLevelConfig(PWR_PVDLevel_2V9);                      // 设置门限电压为 2.9V
    PWR_PVDCmd(ENABLE);                                        // 使能 PVD

    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_ClearITPendingBit(EXTI_Line16);                       // 清除 EXTI16 的挂起标志
    EXTI_InitStructure.EXTI_Line = EXTI_Line16;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;  // 电压上升/下降都触发
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = PVD_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

中断服务函数(显示 VDD 状态)

void PVD_IRQHandler(void)
{
    if (PWR_GetFlagStatus(PWR_FLAG_PVDO) == SET)
    {
        OLED_ShowString(2, 1, "LOW VDD!");     // 电压低于门限
    }
    else
    {
        OLED_ShowString(2, 1, "VDD OK");       // 电压恢复
    }

    EXTI_ClearITPendingBit(EXTI_Line16);       // 清除中断标志位
}

主函数调用

int main(void)
{
    SystemInit();
    OLED_Init();
    OLED_ShowString(1, 1, "PVD Watchdog");

    PVD_Init();   // 初始化 PVD 监测

    while (1)
    {
        // 其他逻辑处理……
    }
}
功能 库函数 说明
打开 PWR RCC_APB1PeriphClockCmd() 启动 PWR 模块
开启 PVD PWR_PVDCmd(ENABLE) 启用电压检测
设置门限 PWR_PVDLevelConfig(PWR_PVDLevel_2V9) 设置比较器参考电压
判断是否电压低 PWR_GetFlagStatus(PWR_FLAG_PVDO) 返回比较结果
清除中断 EXTI_ClearITPendingBit(EXTI_Line16) 清除 EXTI16 中断标志

硬件说明

项目 说明
PVD 内部连接 连接到 EXTI Line16,不能更换引脚
比较参考电压 内部固定,有 8 个档位(2.2V ~ 2.9V)
比较器功能 检测 VDD 与参考电压比较结果
响应时间 非常快,硬件触发中断
唤醒功能 可以作为唤醒源用于唤醒 Stop 模式下的 MCU(⚠️ 不是 Standby)

PWR 相关供电结构(STM32F103 为例)

STM32F1 系列包含以下供电模块:

模块 说明
VDD 主供电(通常 3.3V)
VDDA 模拟电源(ADC/Comparator),需滤波
VSS 数字地
VBAT 实时时钟供电(RTC/备份寄存器),用于掉电后保持数据
VCAP1 稳压输出电容(仅部分 STM32 有)

低功耗模式

睡眠模式

执行完WFI/WFE指令后,STM32进入睡眠模式,程序暂停运行,唤醒后程序从暂停的地方继续运行
SLEEPONEXIT位决定STM32执行完WFI或WFE后,是立刻进入睡眠,还是等STM32从最低优先级的中断处理程序中退出时进入睡眠
在睡眠模式下,所有的I/O引脚都保持它们在运行模式时的状态
WFI指令进入睡眠模式,可被任意一个NVIC响应的中断唤醒
WFE指令进入睡眠模式,可被唤醒事件唤醒

停止模式

执行完WFI/WFE指令后,STM32进入停止模式,程序暂停运行,唤醒后程序从暂停的地方继续运行
1.8V供电区域的所有时钟都被停止,PLL、HSI和HSE被禁止,SRAM和寄存器内容被保留下来
在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态
当一个中断或唤醒事件导致退出停止模式时,HSI被选为系统时钟
当电压调节器处于低功耗模式下,系统从停止模式退出时,会有一段额外的启动延时
WFI指令进入停止模式,可被任意一个EXTI中断唤醒
WFE指令进入停止模式,可被任意一个EXTI事件唤醒

待机模式

执行完WFI/WFE指令后,STM32进入待机模式,唤醒后程序从头开始运行
整个1.8V供电区域被断电,PLL、HSI和HSE也被断电,SRAM和寄存器内容丢失,只有备份的寄存器和待机电路维持供电
在待机模式下,所有的I/O引脚变为高阻态(浮空输入)
WKUP引脚的上升沿、RTC闹钟事件的上升沿、NRST引脚上外部复位、IWDG复位退出待机模式

举例一个工程包含低功耗所有关键功能

main()
│
├── 初始化外设(OLED、按键)
├── 显示当前状态
├── 判断是否从 Standby 唤醒
├── 初始化 PVD
├── 等待按键触发不同低功耗模式
│   ├── KEY1 → 进入 Sleep 模式
│   ├── KEY2 → 进入 Stop 模式(5秒后 RTC 唤醒)
│   └── KEY3 → 进入 Standby 模式(PA0 唤醒)
└── PVD 检测中断持续监控电压状态
#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_pwr.h"
#include "stm32f10x_exti.h"
#include "misc.h"
#include "OLED.h"
#include "Delay.h"
#include "Key.h"

// 初始化 PVD 电压检测器,使用你库里的函数
void PVD_Init(void)
{
    // 1. 开启 PWR 模块时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);

    // 2. 设置 PVD 检测电压门限为 2.9V
    PWR_PVDLevelConfig(PWR_PVDLevel_2V9);

    // 3. 使能 PVD 功能
    PWR_PVDCmd(ENABLE);

    // 4. 配置 EXTI Line16(PVD 专用中断线)
    EXTI_ClearITPendingBit(EXTI_Line16);
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line = EXTI_Line16;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; // 电压上下沿触发
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    // 5. 配置 PVD 中断 NVIC
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = PVD_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

// 进入 Sleep 模式
void Enter_SleepMode(void)
{
    __WFI();  // 等待中断进入 Sleep,使用库函数无须额外操作
}

// 进入 Stop 模式(低功耗调压模式)
void Enter_StopMode(void)
{
    // 使用你库里的函数进入 Stop 模式
    PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);

    // Stop 模式后,时钟需重设
    SystemInit();
}

// 进入 Standby 模式(极限低功耗,掉电重启)
void Enter_StandbyMode(void)
{
    // 允许唤醒引脚 PA0 唤醒功能
    PWR_WakeUpPinCmd(ENABLE);

    // 进入 Standby 模式
    PWR_EnterSTANDBYMode();
}

// PVD 中断处理函数(在 stm32f10x_it.c 中)
void PVD_IRQHandler(void)
{
    if (PWR_GetFlagStatus(PWR_FLAG_PVDO) == SET)
    {
        OLED_ShowString(4, 1, "LOW VDD!  ");
    }
    else
    {
        OLED_ShowString(4, 1, "VDD OK    ");
    }

    EXTI_ClearITPendingBit(EXTI_Line16);
}int main(void)
{
    SystemInit();
    OLED_Init();
    Key_Init();
    PVD_Init();

    OLED_ShowString(1, 1, "Power Mode Demo");

    if (PWR_GetFlagStatus(PWR_FLAG_SB) == SET)
    {
        PWR_ClearFlag(PWR_FLAG_SB);
        OLED_ShowString(2, 1, "Wake from STANDBY");
    }
    else
    {
        OLED_ShowString(2, 1, "Normal Power On");
    }

    while (1)
    {
        uint8_t key = Key_GetNum();
        if (key == 1)
        {
            OLED_ShowString(3, 1, "Enter SLEEP Mode ");
            Delay_ms(500);
            Enter_SleepMode();
            OLED_ShowString(3, 1, "Woke from SLEEP   ");
        }
        else if (key == 2)
        {
            OLED_ShowString(3, 1, "Enter STOP Mode  ");
            Delay_ms(500);
            Enter_StopMode();
            OLED_ShowString(3, 1, "Woke from STOP   ");
        }
        else if (key == 3)
        {
            OLED_ShowString(3, 1, "Enter STANDBY... ");
            Delay_ms(1000);
            Enter_StandbyMode();
        }
    }
}

库函数 功能
PWR_BackupAccessCmd() 开启对 RTC 和 BKP 寄存器的访问权限(修改 RTC 必须)
PWR_PVDCmd() 开启/关闭 PVD 电压检测器
PWR_PVDLevelConfig() 设置电压阈值(2.2V~2.9V)
PWR_WakeUpPinCmd() 启用 PA0 唤醒引脚
PWR_EnterSTOPMode() 进入 Stop 模式(可选低功耗 LDO)
PWR_EnterSTANDBYMode() 进入 Standby 模式(极低功耗,掉电重启)
PWR_GetFlagStatus() 查询 PWR 相关标志位(SB、WU、PVDO)
PWR_ClearFlag() 清除 WakeUp 或 Standby 标志位


网站公告

今日签到

点亮在社区的每一天
去签到