--- tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(扩展篇)V1.0.0 — 第1章:电源控制 + 配套代码15~20" author: "尚硅谷研究院" date: 2026-07-16 created: 2026-07-15 --- # 电源管理与低功耗模式 > **用生活理解**:STM32 有多种低功耗模式就像你家里的电器——**运行模式**是所有电器全开(费电),**睡眠模式**是关灯但空调继续开(CPU 停,外设继续),**停止模式**是只留冰箱(维持寄存器),**待机模式**是拉总闸(只剩下 Vbat 供 RTC)。越省电的模式唤醒越慢。 --- ## 电源框图 STM32 的电源系统分为多个供电区域: | 区域 | 电压 | 供电对象 | 特点 | |------|------|---------|------| | **VDD 区域** | 3.3V | I/O 电路、待机电路、电压调节器 | 数字部分主电源 | | **VDDA 区域** | 3.3V | ADC、温度传感器、复位模块、PLL | 模拟部分独立电源(去耦更好) | | **1.8V 区域** | 1.8V | CPU 内核、SRAM、内部数字外设 | 由电压调节器从 3.3V 降压得到 | | **备份区域 (VBAT)** | 3.3V/电池 | RTC、LSE 振荡器、BKP 寄存器、PC13~15 | VDD 断电时由 VBAT 维持 | > **参考**:参考手册 §5(电源控制 PWR 寄存器)、原理图电源部分 > **参考**:AMS1117 数据手册(板上 5V→3.3V LDO) ### 电压调节器 电压调节器将 VDD(3.3V) 转换为内核所需的 1.8V,有 3 种工作模式: | 调节器模式 | 输出状态 | 对应的 MCU 模式 | |-----------|---------|----------------| | **运转模式 (MR)** | 正常输出 1.8V | 运行/睡眠 | | **停止模式 (LPR)** | 低功耗输出 1.8V(电流小) | 停止 | | **待机模式** | 关闭(无输出) | 待机(内核掉电) | --- ## 上电复位(POR)与掉电复位(PDR) 内部复位电路监控 VDD/VDDA 电压: ``` 电压升高到 VPOR(~1.8V) → 解除复位 → MCU 启动 电压下降到 VPDR(~1.5V) → 进入复位 → MCU 停止 ``` 两者之间有 **40mV 的迟滞**,防止电压在阈值附近抖动时频繁复位。 > **参考**:参考手册 §5.2(上电/掉电复位特性) --- ## 可编程电压检测器(PVD) PVD(Programmable Voltage Detector)监控 VDD 电压,当电压低于可编程阈值时,可以产生 **中断**(不是复位)。 | PVD 阈值选择 (PLS[2:0]) | 阈值电压 | |------------------------|---------| | 000 | 2.2V | | 001 | 2.3V | | 010 | 2.4V | | 011 | 2.5V | | 100 | 2.6V | | 101 | 2.7V | | 110 | 2.8V | | 111 | 2.9V | ```c // PVD 配置示例:阈值 2.7V,下降沿中断 PWR->CR |= PWR_CR_PLS_2 | PWR_CR_PLS_1; // PLS[2:0]=110 → 2.8V PWR->CR |= PWR_CR_PVDE; // PVDE=1 使能PVD // PVD 中断: EXTI16 线 EXTI->RTSR &= ~EXTI_RTSR_TR16; // 下降沿触发(电压下降) EXTI->FTSR |= EXTI_FTSR_TR16; EXTI->IMR |= EXTI_IMR_MR16; NVIC_EnableIRQ(PVD_IRQn); // PVD 中断处理 void PVD_IRQHandler(void) { if (EXTI->PR & EXTI_PR_PR16) { SaveEmergencyData(); EXTI->PR |= EXTI_PR_PR16; } } ``` --- ## 三种低功耗模式对比 | 特性 | **睡眠 (Sleep)** | **停止 (Stop)** | **待机 (Standby)** | |------|----------------|----------------|--------------------| | **CPU 内核** | 停止 | 停止 | **断电** | | **SRAM/寄存器** | 保持 | **保持** | **丢失** | | **备份域** | 保持 | 保持 | 保持 | | **电压调节器** | 运转模式 | **低功耗模式** | **关闭** | | **典型功耗** | ~10mA | ~30μA | **~2μA** | | **唤醒源** | 任意中断/事件 | EXTI 中断 | RTC 闹钟、WKUP 引脚、NRST | | **唤醒时间** | ~0.9μs | ~5μs | ~50ms(需重新初始化) | ### 睡眠模式(Sleep) CPU 停止,外设继续工作。任何中断或事件都能唤醒。 **进入方式**: ```c __WFI(); // 等待中断唤醒 __WFE(); // 等待事件唤醒 ``` **SLEEP-NOW vs SLEEP-ON-EXIT**: ```c SCB->SCR &= ~SCB_SCR_SLEEPONEXIT_Msk; // SLEEPDEEP=0, SLEEPONEXIT=0 // SLEEP-NOW: 执行 WFI 立即进睡眠 SCB->SCR |= SCB_SCR_SLEEPONEXIT_Msk; // SLEEPONEXIT=1 // SLEEP-ON-EXIT: 执行 WFI → 最低优先级 ISR 退出后进睡眠 ``` ### 停止模式(Stop) CPU + 所有外设时钟都停止,但 **SRAM 和寄存器内容保留**。电压调节器切到低功耗模式。 ```c void enter_stop_mode(void) { SCB->SCR |= SCB_SCR_SLEEPDEEP; // 深度睡眠 RCC->APB1ENR |= RCC_APB1ENR_PWREN; PWR->CR &= ~PWR_CR_PDDS; // PDDS=0 表示停止模式 PWR->CR |= PWR_CR_LPDS; // 电压调节器低功耗模式 __WFI(); } ``` **唤醒后**:触发 EXTI 中断后,系统从停止模式恢复,执行 ISR。 ### 待机模式(Standby) 最省电模式。1.8V 内核域完全断电(SRAM 和寄存器内容丢失),仅备份域(VBAT)和待机电路保持。 ```c void enter_standby_mode(void) { SCB->SCR |= SCB_SCR_SLEEPDEEP; // 深度睡眠 PWR->CR |= PWR_CR_PDDS; // PDDS=1 表示待机模式 PWR->CSR |= PWR_CSR_EWUP; // 使能 PA0 唤醒功能 __WFI(); } // 唤醒后相当于系统复位!从 startup 重新执行。 ``` --- ## 实验:睡眠/停止/待机模式 所有 6 个项目均来自 `stm32/15~20_lowpower_xxx`,覆盖寄存器版和 HAL 库版三种低功耗模式。 ### 睡眠模式(Sleep) **项目 `15_lowpower_sleep_register`(寄存器版)** **文件:`stm32/15_lowpower_sleep_register/User/main.c`** ```c #include "usart.h" #include "delay.h" #include "led.h" void enter_sleep_mode(void); int main(void) { USART_Init(); LED_Init(); printf("尚硅谷低功耗实验:睡眠模式...\n"); LED_On(LED1); Delay_s(2); while (1) { printf("正常运行执行完毕,3s后进入睡眠模式...\n"); Delay_s(3); printf("进入睡眠模式"); enter_sleep_mode(); printf("从睡眠模式唤醒...\n"); Delay_s(2); } } void enter_sleep_mode(void) { // SLEEP-NOW: SLEEPDEEP=0, 执行 WFI SCB->SCR &= ~SCB_SCR_SLEEPDEEP; __WFI(); } ``` **项目 `16_lowpower_sleep_hal`(HAL 库版)** **文件:`stm32/16_lowpower_sleep_hal/Core/Src/main.c`** ```c #include "main.h" #include "usart.h" #include "gpio.h" uint8_t ch; int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); printf("尚硅谷低功耗实验:睡眠模式...\n"); HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); HAL_Delay(2000); HAL_UART_Receive_IT(&huart1, &ch, 1); while (1) { printf("正常运行执行完毕,3s后进入睡眠模式...\n"); HAL_Delay(3000); printf("进入睡眠模式\n"); HAL_SuspendTick(); // 暂停 Systick HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); // 进睡眠 HAL_ResumeTick(); // 恢复 Systick printf("从睡眠模式唤醒...\n"); HAL_Delay(2000); } } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { printf("%c", ch); HAL_UART_Receive_IT(&huart1, &ch, 1); } } ``` ### 停止模式(Stop) **项目 `17_lowpower_stop_register`(寄存器版)** **文件:`stm32/17_lowpower_stop_register/User/main.c`** ```c #include "usart.h" #include "delay.h" #include "led.h" #include "key.h" void enter_stop_mode(void); void get_clock_freq(uint32_t *sys_clock, uint32_t *AHB_clock, uint32_t *APB2_clock, uint32_t *APB1_clock); int main(void) { USART_Init(); LED_Init(); Key_Init(); printf("尚硅谷低功耗实验:停止模式...\n"); LED_On(LED_2); Delay_s(2); while (1) { printf("正常运行执行完毕,3s后进入停止模式...\n"); Delay_s(3); printf("进入停止模式...\n"); Delay_ms(1); enter_stop_mode(); uint32_t sys_clock, ahb_clock, apb1_clock, apb2_clock; get_clock_freq(&sys_clock, &ahb_clock, &apb2_clock, &apb1_clock); // 停止模式唤醒后 HSE/PLL 已关闭,需重新配置时钟 SystemInit(); printf("唤醒前的时钟频率:\nsys_clock = %d, ahb_clock = %d, ...\n\n", sys_clock, ahb_clock, apb1_clock, apb2_clock); get_clock_freq(&sys_clock, &ahb_clock, &apb2_clock, &apb1_clock); printf("唤醒后的时钟频率:\nsys_clock = %d, ahb_clock = %d, ...\n\n", sys_clock, ahb_clock, apb1_clock, apb2_clock); printf("从停止模式唤醒...\n"); Delay_s(2); } } void enter_stop_mode(void) { SCB->SCR |= SCB_SCR_SLEEPDEEP; RCC->APB1ENR |= RCC_APB1ENR_PWREN; PWR->CR &= ~PWR_CR_PDDS; // 停止模式 PWR->CR |= PWR_CR_LPDS; // 低功耗调节器 __WFI(); } void get_clock_freq(uint32_t *sys_clock, uint32_t *AHB_clock, uint32_t *APB2_clock, uint32_t *APB1_clock) { uint32_t clock_src = RCC->CFGR & RCC_CFGR_SWS; if (clock_src == RCC_CFGR_SWS_HSE) *sys_clock = HSE_VALUE; else if (clock_src == RCC_CFGR_SWS_HSI) *sys_clock = HSI_VALUE; else if (clock_src == RCC_CFGR_SWS_PLL) { uint32_t mul = ((RCC->CFGR & RCC_CFGR_PLLMULL) >> 18) + 2; *sys_clock = HSE_VALUE * mul; } uint32_t hpre = (RCC->CFGR & RCC_CFGR_HPRE) >> 4; if (hpre & 0x8) { uint32_t n = hpre & 0x7; *AHB_clock = (n < 4) ? (*sys_clock >> (n+1)) : (*sys_clock >> (n+2)); } else *AHB_clock = *sys_clock; uint32_t ppre1 = (RCC->CFGR & RCC_CFGR_PPRE1) >> 8; *APB1_clock = (ppre1 & 0x4) ? (*AHB_clock >> ((ppre1 & 0x3) + 1)) : *AHB_clock; uint32_t ppre2 = (RCC->CFGR & RCC_CFGR_PPRE2) >> 11; *APB2_clock = (ppre2 & 0x4) ? (*AHB_clock >> ((ppre2 & 0x3) + 1)) : *AHB_clock; } ``` **项目 `18_lowpower_stop_hal`(HAL 库版)** **文件:`stm32/18_lowpower_stop_hal/Core/Src/main.c`** ```c #include "main.h" #include "usart.h" #include "gpio.h" uint8_t ch; int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); printf("尚硅谷低功耗实验:停止模式...\n"); HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); HAL_Delay(2000); while (1) { printf("正常运行执行完毕,3s后进入停止模式...\n"); HAL_Delay(3000); printf("进入停止模式...\n"); // HAL 停止模式:低功耗调节器 + WFI 进入 HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_SLEEPENTRY_WFI); // 唤醒后需重新配置系统时钟 SystemClock_Config(); printf("从停止模式唤醒...\n"); HAL_Delay(2000); } } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { printf("%c", ch); HAL_UART_Receive_IT(&huart1, &ch, 1); } } ``` ### 待机模式(Standby) **项目 `19_lowpower_standby_register`(寄存器版)** **文件:`stm32/19_lowpower_standby_register/User/main.c`** ```c #include "usart.h" #include "delay.h" #include "led.h" #include "key.h" void enter_standby_mode(void); int main(void) { USART_Init(); LED_Init(); Key_Init(); RCC->APB1ENR |= RCC_APB1ENR_PWREN; if (PWR->CSR & PWR_CSR_SBF) { printf("从待机模式唤醒:\n"); PWR->CR |= PWR_CR_CSBF; } if (PWR->CSR & PWR_CSR_WUF) { printf("产生了唤醒事件:\n"); PWR->CR |= PWR_CR_CWUF; } printf("尚硅谷低功耗实验:待机模式...\n"); LED_On(LED_2); Delay_s(2); while (1) { printf("正常运行执行完毕,3s后进入待机模式...\n"); Delay_s(3); printf("进入待机模式,按下KEY唤醒...\n"); Delay_ms(1); enter_standby_mode(); printf("从待机模式唤醒...\n"); Delay_s(2); } } void enter_standby_mode(void) { SCB->SCR |= SCB_SCR_SLEEPDEEP; PWR->CR |= PWR_CR_PDDS; // 待机模式 PWR->CSR |= PWR_CSR_EWUP; // 使能 PA0 唤醒 __WFI(); } ``` **项目 `20_lowpower_standby_hal`(HAL 库版)** **文件:`stm32/20_lowpower_standby_hal/Core/Src/main.c`** ```c #include "main.h" #include "usart.h" #include "gpio.h" uint8_t ch; int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); // 检测唤醒标志 if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB)) { printf("从待机模式唤醒:\n"); __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB); } if (__HAL_PWR_GET_FLAG(PWR_FLAG_WU)) { printf("产生了唤醒事件:\n"); __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); } printf("尚硅谷低功耗实验:待机模式...\n"); HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); HAL_Delay(2000); while (1) { printf("正常运行执行完毕,3s后进入待机模式...\n"); HAL_Delay(3000); printf("进入待机模式,按下KEY唤醒...\n"); // 使能 PA0 唤醒引脚 HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1); // 进入待机模式(唤醒后相当于复位) HAL_PWR_EnterSTANDBYMode(); printf("从待机模式唤醒...\n"); HAL_Delay(2000); } } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { printf("%c", ch); HAL_UART_Receive_IT(&huart1, &ch, 1); } } ``` --- ## 核心速查表 | 操作 | 寄存器操作 | HAL 库函数 | |------|----------|-----------| | 进入睡眠 | `SCB->SCR &= ~SLEEPDEEP;` + `__WFI()` | `HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI)` | | 进入停止 | `SLEEPDEEP=1` + `PDDS=0` + `LPDS=1` + `__WFI()` | `HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_SLEEPENTRY_WFI)` | | 进入待机 | `SLEEPDEEP=1` + `PDDS=1` + `EWUP=1` + `__WFI()` | `HAL_PWR_EnterSTANDBYMode()` | | 使能 PA0 唤醒 | `PWR->CSR |= PWR_CSR_EWUP` | `HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1)` | | 配置 PVD | `PWR->CR` 配置 PLS + PVDE + EXTI16 | `HAL_PWR_ConfigPVD()` | | 清除唤醒标志 | `PWR->CR |= PWR_CR_CSBF/CWUF` | `__HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB/WU)` | | 检测待机唤醒 | `PWR->CSR & PWR_CSR_SBF` | `__HAL_PWR_GET_FLAG(PWR_FLAG_SB)` | | 暂停/恢复 Systick | — | `HAL_SuspendTick()` / `HAL_ResumeTick()` | ## 常见问题与避坑 1. **停止模式功耗高** → 检查所有 GPIO 是否关闭上下拉、所有外设时钟是否关闭(`RCC->APBxENR=0`) 2. **无法唤醒** → 检查 EXTI 配置(必须配置为事件或中断)、NVIC 优先级 3. **待机后 GPIO 输出乱** → 待机模式 GPIO 复位为默认浮空输入,恢复后需重新初始化 4. **待机模式功耗还是很高** → 检查 VBAT 引脚是否接 VDD、WKUP 引脚是否有漏电路径 5. **多次进出停止模式后异常** → 检查唤醒标志是否清除(PWR.CSR.WUF) 6. **HAL 睡眠必须暂停 Systick** → `HAL_SuspendTick()` / `HAL_ResumeTick()` 配对使用,否则 SysTick 中断会立即唤醒 7. **停止模式唤醒后时钟异常** → 唤醒后 HSE/PLL 已关闭,需调用 `SystemInit()` 或 `HAL_RCC_ClockConfig()` 重新配置