本文的大部分内容来自B站up主 江协科技, 此文只供本人学习记录用途, 侵删
一、PWR
- PWR(Power Control)电源控制PWR负责管理STM32内部的电源供电部分,可以实现可编程电压监测器和低功耗模式的功能
- 可编程电压监测器(PVD)可以监控VDD电源电压,当VDD下降到PVD阀值以下或上升到PVD阀值之上时,PVD会触发中断,用于执行紧急关闭任务 (本节用不到 了解即可)
- 低功耗模式包括睡眠模式(Sleep)、停机模式(Stop)和待机模式(Standby),可在系统空闲时,降低STM32的功耗,延长设备使用时间
二、电源框图
stm32的电源有四个区域
- VDDA供电区域 一般这里直接接VDD了
- VDD供电区域
- 1.8V供电区域 通过VDD区域里的的电压调节器去到CPU, 存储器, 外设等核心电路
- 后备供电区域
三、低功耗模式
-
随眠模式关闭CPU时钟 可被任意中断唤醒
-
停机模式关闭整个1.8V供电区域 只能被外部中断唤醒
这四个借道外部中断的通道, 就可以用于唤醒停机模式
-
待机模式关闭几乎所有电路 只能被WKUP(PA0)的上升延, RTC闹钟, 复位, 看门狗唤醒
四、示例代码
1 修改主频
虽然能达到低功耗的效果, 但是以降低性能为代价, 而且类似Delay这样依赖系统主频计时的代码也会混乱,不建议使用
这是System_stm32f10x.c/h中外部可调用的三个函数/变量, 系统默认是用HSI(8Mhz)为主频, 不过复位时, 启动函数会调用SystemInit 把系统主频改成HSE经过锁相环倍频之后的频率 一般9倍频就是72Mhz
extern uint32_t SystemCoreClock; //该变量存放系统主频
extern void SystemInit(void); //初始化时钟
extern void SystemCoreClockUpdate(void); //更新SystemCoreClock的值
本博客使用的不是超值系列产品, 所以只需改System_stm32f10x.c中 这段代码#else下的define 就能改变系统主频了
(记得把这个文件取消只读)
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
int main(void)
{
OLED_Init();
SystemCoreClockUpdate(); //更新SystemCoreClock的值
OLED_ShowNum(1,1,SystemCoreClock,8); //显示系统主频
//如果把系统主频改低, 而不修改Delay函数, 就能发现hello闪的慢了
while (1)
{
OLED_ShowString(2,1,"hello");
Delay_ms(500);
OLED_ShowString(2,1," ");
Delay_ms(500);
}
}
2 睡眠模式
这是一段串口接收数据的程序, 在无信号时进入睡眠模式, 等待中断的唤醒
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "OLED.H"
#include "usart.h"
int main(void){
OLED_Init();
UART_Init();
uint8_t result;
while(1){
if(UART_Flag==1){
result=UART_Data;
OLED_ShowHexNum(1,1,result,2);
UART_Flag=0;
}
OLED_ShowString(2,1,"Runing");
Delay_ms(1000);
OLED_ShowString(2,1," ");
Delay_ms(1000); //用Runing的闪动标志程序的运行
__WFI(); //进入睡眠模式(中断唤醒)
}
}
3 停机模式
这是一段传感器触发外部中断计次的代码, 在传感器无信号时进入停机模式, 等待外部中断的唤醒
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "OLED.H"
#include "sensorCounter.h"
int main(void){
OLED_Init();
SensorCount_Init();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE); //开启PWR时钟
uint8_t Num=0;
OLED_ShowNum(1,1,Num,3);
while(1){
if(IPflag){
Num++;
OLED_ShowNum(1,1,Num,3);
IPflag=0;
}
OLED_ShowString(2,1,"Runing");
Delay_ms(1000);
OLED_ShowString(2,1," ");
Delay_ms(1000); //用Runing的闪动标志程序的运行
PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);
/*
进入停止模式
PWR_Regulator_ON 电压调节器不开启低功耗模式(开了唤醒有一定延迟)
PWR_STOPEntry_WFI 中断唤醒(而不是事件唤醒)
*/
SystemInit(); //每次停止模式被唤醒 系统主频默认是HSI 8Mhz,这里手动设置时钟主频为HSE的倍频
}
}
4 待机模式
待机模式能由WakeUp的上升沿, RTC的闹钟唤醒, 以下代码的现象表现为:
- 复位
- 重新初始化一切
- 设置闹钟值
- Runing闪动
- 进入待机模式(等待闹钟响, 或者WakeUp的上升沿, 重新复位)
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "OLED.H"
#include "MYRTC.h"
int main(void){
OLED_Init();
MYRTC_Init();
RCC_APB1PeriphClockCmd(RCC_APB1ENR_PWREN,ENABLE); //开启PWR时钟
PWR_WakeUpPinCmd(ENABLE); //使能WakeUp引脚
uint32_t alarm = RTC_GetCounter() + 5;
RTC_SetAlarm(alarm); //设置闹钟为5秒后
while(1){
OLED_ShowNum(1,1,RTC_GetCounter(),10); //RTC_CNT
OLED_ShowNum(2,1,alarm,10); //闹钟值
OLED_ShowNum(3,1,RTC_GetFlagStatus(RTC_FLAG_ALR),1); //闹钟唤醒状态位
OLED_ShowString(4,1,"Runing");
Delay_ms(500);
OLED_ShowString(4,1," ");
Delay_ms(500); //用Runing的闪动标志程序的运行
OLED_Clear();//关闭所有外设
PWR_EnterSTANDBYMode(); //进入待机模式
}
}
五、总结
- 修改主频最好别用
- 睡眠模式和停机模式, 适合大部分时间不工作, 只在少部分时间工作的监测项目使用, 两者的区别在于你的中断是不是来自EXTI
- 停机模式 适合满足间隔一段时间做固定的事情, 且要求低功耗的项目