【STM32】PWR電源控制

  • 一、PWR
  • 1.1 PWR簡介
  • 1.2 STM32電源框圖
  • 1.3 上電覆位和掉電覆位
  • 1.4 可編程電壓監測器
  • 二、低功耗模式
  • 2.1 睡眠模式
  • 2.2 停止模式
  • 2.3 待機模式

一、PWR

1.1 PWR簡介

  • PWR(Power Control)電源控制
  • PWR負責管理STM32內部的電源供電部分,可以實現可編程電壓監測器和低功耗模式的功能
  • 可編程電壓監測器(PVD)可以監控VDD電源電壓,當VDD下降到PVD閥值以下或上升到PVD閥值之上時,PVD會觸發中斷,用於執行緊急關閉任務
  • 低功耗模式包括睡眠模式(Sleep)、停機模式(Stop)和待機模式(Standby),可在系統空閒時,降低STM32的功耗,延長設備使用時間

1.2 STM32電源框圖

電源控制(PWR) - 天涼_#嵌入式硬件


1. 供電區域劃分

圖中把STM32的電源系統劃分為四個核心供電區域,分別對應不同的功能模塊,實現電源的分級管理與針對性供電:

  • VDDA供電區域
  • VDD供電區域
  • 1.8V供電區域
  • 後備供電區域(VBAT)

2. 各區域功能與模塊解析
1) VDDA供電區域

  • 供電對象:A/D轉換器、温度傳感器、復位模塊、PLL(鎖相環)。
  • 電源範圍VREF-(可與VSSA共地)到VREF+,電壓範圍從2.4V起至VDDA。
  • 設計邏輯:該區域為模擬/混合信號模塊供電,需保證電源的穩定性(如ADC對電源噪聲敏感),因此獨立劃分供電,避免數字電路的干擾。

2)VDD供電區域

  • 子模塊1:I/O電路
    負責CPU芯片的輸入輸出接口供電,是數字信號與外部設備交互的“門户”。
  • 子模塊2:待機電路(喚醒邏輯、IWDG)
  • 喚醒邏輯:支持芯片從低功耗待機模式快速喚醒,保障系統響應速度。
  • IWDG(獨立看門狗):是一種硬件監控機制,若程序運行異常(如死循環),IWDG會觸發復位,保證系統可靠性。
  • 子模塊3:電壓調節器
    是電源管理的核心單元之一,負責將VDD的輸入電壓轉換為1.8V,為“1.8V供電區域”供電。

3)1.8V供電區域

  • 供電對象:CPU核心、存儲器、內置數字外設。
  • 設計邏輯:CPU和高速數字模塊對電源電壓精度、功耗敏感,通過電壓調節器將VDD降壓到1.8V,既滿足性能需求,又能降低功耗。

4)後備供電區域(VBAT)

  • 供電對象:LSE 32K晶體振盪器、後備寄存器、RCC BDCR寄存器、RTC(實時時鐘)。
  • 設計邏輯:該區域由備用電池(VBAT)供電,即使主電源(VDD)掉電,也能維持RTC計時、後備寄存器數據存儲,保障系統的時間連續性和關鍵數據不丟失。

1.3 上電覆位和掉電覆位

電源控制(PWR) - 天涼_#嵌入式硬件_02

  • 復位信號(Reset):低電平表示芯片處於復位狀態,高電平表示覆位釋放,芯片開始運行。
  • 上電階段:電源電壓從 0 上升,當超過 POR 閾值並經過滯後時間後,復位信號釋放,芯片開始初始化運行。
  • 掉電階段:電源電壓下降,當低於 PDR 閾值時,復位信號拉低,芯片進入復位狀態,防止電壓不足時運行異常。
  • 上電時超過POR才不會復位,掉電時低於PDR就開始復位。
  • 通過硬件級的復位機制,保證芯片在 “上電時電源穩定後才啓動”“掉電時提前復位以保護數據”,提升系統可靠性。

1.4 可編程電壓監測器

電源控制(PWR) - 天涼_#c語言_03

  • 電壓上升階段:當 VDD/V DDA 電壓上升超過 PVD 閾值時,PVD 輸出電平翻轉。
  • 電壓下降階段:當 VDD/V DDA 電壓 下降低於(PVD 閾值 - 100mV 遲滯) 時,PVD 輸出電平再次翻轉。
  • 可用於提前檢測電源電壓異常(如掉電預警),讓系統有時間執行備份數據、進入低功耗等保護操作,提升系統可靠性。用户可通過配置寄存器選擇不同的 PVD 閾值,適配不同應用場景的電源監測需求。

二、低功耗模式

電源控制(PWR) - 天涼_#c語言_04

  • 睡眠模式:是“輕量級”低功耗模式,僅暫停CPU運行,外設和時鐘基本保持,用於短時間待機且需快速響應的場景。
  • 停機模式:深度降低功耗,關閉大部分時鐘和1.8V區域供電,需外部中斷喚醒,適用於對功耗敏感、可容忍稍慢喚醒速度的場景。
  • 待機模式:是最深的低功耗模式,幾乎關閉所有電源和時鐘(僅後備區域保持),需特定喚醒源(如WKUP引腳、RTC),用於極致功耗優化的場景。

低功耗模式

CPU狀態

內核外設/寄存器

系統時鐘

大部分外設狀態

特殊保持的模塊

睡眠模式(Sleep)

停止運行

保持(數據不丟)

保持(可配置)

正常運行(可配置關閉)

無(或僅關閉未使用外設)

停機模式(Stop)

停止運行

保持(數據不丟)

關閉

斷電(寄存器數據保持)

RTC、IWDG(若使能)

待機模式(Standby)

完全斷電

丟失(全部重置)

幾乎全部關閉

幾乎全部斷電

RTC、LSE、後備寄存器、IWDG(若使能)


電源控制(PWR) - 天涼_#c語言_05

電源控制(PWR) - 天涼_#stm32_06

2.1 睡眠模式

  • 執行完WFI/WFE指令後,STM32進入睡眠模式,程序暫停運行,喚醒後程序從暫停的地方繼續運行
  • SLEEPONEXIT位決定STM32執行完WFI或WFE後,是立刻進入睡眠,還是等STM32從最低優先級的中斷處理程序中退出時進入睡眠
  • 在睡眠模式下,所有的I/O引腳都保持它們在運行模式時的狀態
  • WFI指令進入睡眠模式,可被任意一個NVIC響應的中斷喚醒
  • WFE指令進入睡眠模式,可被喚醒事件喚醒

WFI 模式實現代碼

#include "stm32f10x.h"

// 初始化NVIC中斷(用於WFI喚醒)
void NVIC_Init_For_WFI(void) {
    NVIC_InitTypeDef NVIC_InitStructure;
    // 配置某中斷(如外部中斷EXTI0)
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

// 配置睡眠模式(SLEEP-ON-EXIT使能)
void Sleep_Mode_Config(void) {
    // 使能SLEEPONEXIT位:執行完中斷服務程序後進入睡眠
    SCB->SCR |= SCB_SCR_SLEEPONEXIT_Msk;
    // 選擇睡眠模式為“深度睡眠禁用”(即普通睡眠模式)
    PWR->CR &= ~PWR_CR_PDDS;
    PWR->CR |= PWR_CR_CWUF; // 清除喚醒標誌
}

int main(void) {
    // 系統初始化(時鐘、外設等)
    SystemInit();
    
    NVIC_Init_For_WFI();
    Sleep_Mode_Config();
    
    while(1) {
        // 執行WFI指令進入睡眠,被任意NVIC中斷喚醒後繼續執行
        __WFI();
        // 喚醒後從這裏繼續運行
    }
}

// 中斷服務程序(示例:EXTI0中斷)
void EXTI0_IRQHandler(void) {
    if(EXTI_GetITStatus(EXTI_Line0) != RESET) {
        // 中斷處理邏輯
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

2.2 停止模式

  • 執行完WFI/WFE指令後,STM32進入停止模式,程序暫停運行,喚醒後程序從暫停的地方繼續運行
  • 1.8V供電區域的所有時鐘都被停止,PLL、HSI和HSE被禁止,SRAM和寄存器內容被保留下來
  • 在停止模式下,所有的I/O引腳都保持它們在運行模式時的狀態
  • 當一箇中斷或喚醒事件導致退出停止模式時,HSI被選為系統時鐘
  • 當電壓調節器處於低功耗模式下,系統從停止模式退出時,會有一段額外的啓動延時
  • WFI指令進入停止模式,可被任意一個EXTI中斷喚醒
  • WFE指令進入停止模式,可被任意一個EXTI事件喚醒

停止模式(WFI 喚醒,EXTI 中斷觸發)

#include "stm32f10x.h"

// 初始化EXTI中斷(用於喚醒停止模式)
void EXTI_Init_For_Stop(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    // 使能GPIO和AFIO時鐘
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
    
    // 配置GPIO為輸入模式(如GPIOA0)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉輸入
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 配置EXTI中斷
    EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿觸發中斷
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
    
    // 配置NVIC中斷
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

// 停止模式配置
void Stop_Mode_Config(void) {
    // 使能深度睡眠
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
    // 選擇停止模式(PDDS=0,LPDS=1)
    PWR->CR |= PWR_CR_LPDS;
    PWR->CR &= ~PWR_CR_PDDS;
    PWR->CR |= PWR_CR_CWUF; // 清除喚醒標誌
}

int main(void) {
    // 系統初始化(時鐘、外設等)
    SystemInit();
    
    EXTI_Init_For_Stop();
    Stop_Mode_Config();
    
    while(1) {
        // 執行WFI指令進入停止模式,被EXTI0中斷喚醒後繼續執行
        __WFI();
        // 喚醒後從這裏繼續運行
    }
}

// EXTI0中斷服務程序
void EXTI0_IRQHandler(void) {
    if(EXTI_GetITStatus(EXTI_Line0) != RESET) {
        // 中斷處理邏輯
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

2.3 待機模式

  • 執行完WFI/WFE指令後,STM32進入待機模式,喚醒後程序從頭開始運行
  • 整個1.8V供電區域被斷電,PLL、HSI和HSE也被斷電,SRAM和寄存器內容丟失,只有備份的寄存器和待機電路維持供電
  • 在待機模式下,所有的I/O引腳變為高阻態(浮空輸入)
  • WKUP引腳的上升沿、RTC鬧鐘事件的上升沿、NRST引腳上外部復位、IWDG復位退出待機模式

待機模式WKUP引腳 實現代碼

#include "stm32f10x.h"

void Standby_Mode_Config(void) {
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
    PWR->CR |= PWR_CR_PDDS;       // 選擇待機模式
    PWR_WakeUpPinCmd(ENABLE);    // 使能WKUP引腳喚醒
    PWR->CR |= PWR_CR_CWUF;      // 清除喚醒標誌
    __WFI();                     // 進入待機模式
}

int main(void) {
    SystemInit();
    Standby_Mode_Config();
    // 喚醒後從頭執行,需重新初始化
    while(1) {
        // 應用邏輯
    }
}