目录
一:PWR
1:简历
2:电源框图
3:低功耗模式
4:模式选择
5:低功耗模式注意事项
A:睡眠模式
B:停止模式
C:待机模式
二 : 案例
A:修改主频
1:连接图
2:代码
B:睡眠模式+串口发送+接收
1:连接图
2:代码
C:停止模式+对射式红外传感器计次
1:连接图
2:函数介绍
3:代码
D: 待机模式+实时时钟
1:连接图
2:代码
一:PWR
1:简历
PWR(Power Control)电源控制
PWR负责管理STM32内部的电源供电部分,可以实现可编程电压监测器和低功耗模式的功能
可编程电压监测器(PVD)可以监控VDD电源电压,当VDD下降到PVD阀值以下或上升到PVD阀值之上时,PVD会触发中断,用于执行紧急关闭任务
低功耗模式包括睡眠模式(Sleep)、停机模式(Stop)和待机模式(Standby),可在系统空闲时,降低STM32的功耗,延长设备使用时间
2:电源框图
VDD : 右边部分是VDD通过电压调节器,降压到1.8V ; 电压调节器,它的作用是给1.8V区域供电
3:低功耗模式
HSL和HSE : HSI内部高速时钟和HSE外部高速时钟
关闭电路通常有两个做法 : 一个是关闭时钟,另一个是关闭电源
关闭时钟-------所有的运算和涉及时序的操作都会暂停, 但是寄存器和存储器里面保存的数据还可以维持,不会消失
关闭电源--------就是电路直接断电 , 电路的操作和数据都会直接丢失 . 所以关闭电源,比关闭时钟更省电
电压调节器: 电压调节器是把VDD的电压降低到了1.8V, 然后电压调节器给1.8V区域的供电 ; 电压调节器相当于1.8V供电区域的电池; 关闭电压调节器 , 1.8V供电区域的都不能使用;
睡眠 : WFI--------任何外设发生任何中断时,芯片都会立刻醒来 , 因为中断发生了,所以醒来之后的第一件事一般就是处理中断函数
WFE------等待事件 , 对应的唤醒条件是: 唤醒事件 , 这个事件可以是外部中断配置为事件模式 ; 也可以是使能到中断,但是没有配置NVIC , 调用WFE进入的睡眠模式 ,产生唤醒事件时,会立刻醒来 , 醒来之后,一般不需要进中断函数 , 直接从睡的地方继续运行
睡眠模式只是把1.8V区域的CPU时钟关闭
4:模式选择
5:低功耗模式注意事项
A:睡眠模式
执行完WFI/WFE指令后,STM32进入睡眠模式,程序暂停运行,唤醒后程序从暂停的地方继续运行
SLEEPONEXIT位决定STM32执行完WFI或WFE后,是立刻进入睡眠,还是等STM32从最低优先级的中断处理程序中退出时进入睡眠
在睡眠模式下,所有的I/O引脚都保持它们在运行模式时的状态
WFI指令进入睡眠模式,可被任意一个NVIC响应的中断唤醒
WFE指令进入睡眠模式,可被唤醒事件唤醒
B:停止模式
执行完WFI/WFE指令后,STM32进入停止模式,程序暂停运行,唤醒后程序从暂停的地方继续运行
HSL和HSE : HSI内部高速时钟和HSE外部高速时钟
1.8V供电区域的所有时钟都被停止,PLL、HSI和HSE被禁止,SRAM和寄存器内容被保留下来
在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态
当一个中断或唤醒事件导致退出停止模式时,HSI被选为系统时钟 -------- 你程序刚上电,是72MHz的主频 , 但是进入停止模式,再唤醒之后 , 就变成8MHz的主频了; 所以,我们一般在停止模式唤醒后 , 第一时间就是重新启动HSE,配置主频为72MHz , 我们只需要再调用一下Systemlnit就行
当电压调节器处于低功耗模式下,系统从停止模式退出时,会有一段额外的启动延时
WFI指令进入停止模式,可被任意一个EXTI中断唤醒
WFE指令进入停止模式,可被任意一个EXTI事件唤醒
C:待机模式
执行完WFI/WFE指令后,STM32进入待机模式,唤醒后程序从头开始运行
整个1.8V供电区域被断电,PLL、HSI和HSE也被断电,SRAM和寄存器内容丢失,只有备份的寄存器和待机电路维持供电
在待机模式下,所有的I/O引脚变为高阻态(浮空输入)
WKUP引脚的上升沿、RTC闹钟事件的上升沿、NRST引脚上外部复位、IWDG复位退出待机模式
它并不会主动关闭LSI和LSE两个低速时钟 , 因为这两个时钟还要维持RTC和独立看门狗的运行
二 : 案例
A:修改主频
1:连接图
2:代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
/*STM32默认的是72KH的主频
#define SYSCLK_FREQ_72MHz 72000000*/
int main(void)
{
OLED_Init();
OLED_ShowString(1, 1, "SystemCoreClock:");
OLED_ShowSignedNum(2,1,SystemCoreClock,8);
while (1)
{
OLED_ShowString(3, 1, "Running:");
Delay_ms(500);
OLED_ShowString(3, 1, " ");
Delay_ms(500);
}
}
主要是在 : 2个文件中操作 , ( 如果文件上面有🔑的话 , 需要线解除只读模式), 文件主要给我们提供了一个可以调用的全局变量, 和2个函数 ;
修改主频 : 默认使用的是72000000HZ的主频 ,
注意修改主频要谨慎, 一些计算都和主频相关 , 主频修改后计算也要进行更改
选择外部8M晶振作为锁相环输入 , 锁相环执行9倍频输出的72M,选择为SYSCLK
SystemCoreClock------系统的全局变量 , 用于显示系统的主频
B:睡眠模式+串口发送+接收
1:连接图
2:代码
在串口发送+接收的代码上进行改进的. 详情参考 : 09:STM32-------USART串口通信+串口数据包----B:串口发送+接收----4:串口发送+接收 -----中断代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
uint8_t RxData;
int main(void)
{
OLED_Init();
OLED_ShowString(1, 1, "RxData:");
serial_init();
while (1)
{
if (Serial_GetRxFlag() == 1)
{
RxData = Serial_GetRxData();
Serial_SendByte(RxData);
OLED_ShowHexNum(1, 8, RxData, 3);
}
OLED_ShowString(2, 1, "Running:");
Delay_ms(500);
OLED_ShowString(2, 1, " ");
Delay_ms(500);
__WFI();
}
}
WFI--------任何外设发生任何中断时,芯片都会立刻醒来 , 因为中断发生了,所以醒来之后的第一件事一般就是处理中断函数
首先,睡眠模式,这是可以的 , CPU时钟关闭,程序不再执行 . 但是外设的时钟不会 关 . USART便件电路还是可以接收数据的 , USART收到数据后,产生中断,唤醒CPU
停机模式 : 这个模式下,所有1.8V区域的时钟都关了 , CPU和外设都不能运行 , 那自然USART也收不到数据,产生不了中断 ,所以这个模式不行
C:停止模式+对射式红外传感器计次
1:连接图
2:函数介绍
睡眠模式其实都只是内核的操作, 睡眠模式涉及的几个寄存器 , 也都在内核里 跟PWR外设关系不大.
停止模式 : 涉及到内核之外的电路操作, 这就需要用到PWR外设了
在stm32f10x_pwr.h文件中----启用或禁用对RTC和备份寄存器的访问。
void PWR_BackupAccessCmd(FunctionalState NewState);
使能后备区域的访问
在stm32f10x_pwr.h文件中----启用或禁用唤醒引脚功能
void PWR_WakeUpPinCmd(FunctionalState NewState);
使能位于PAO位置的WKUP引脚
在stm32f10x_pwr.h文件中----进入STOP模式
void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry);
进入停止模式
在stm32f10x_pwr.h文件中----待机模式
void PWR_EnterSTANDBYMode(void)
在stm32f10x_pwr.h文件中----关于标志位的函数
FlagStatus PWR_GetFlagStatus(uint32_t PWR_FLAG);
void PWR_ClearFlag(uint32_t PWR_FLAG);
PWR_GetFlagStatus : 获取标志位
PWR_ClearFlag : 清除标志位
3:代码
在对射式红外传感器计次的代码上进行更改 . 详情参考 : 02:STM32--EXTI外部中断----A:对外式红外传感计数器
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"
int main(void)
{
OLED_Init();
CountSensor_Init();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
OLED_ShowString(1, 1, "Count:");
while (1)
{
OLED_ShowNum(1, 7, CountSensor_Get(), 5);
OLED_ShowString(2, 1, "Running:");
Delay_ms(500);
OLED_ShowString(2, 1, " ");
Delay_ms(500);
PWR_EnterSTOPMode(PWR_Regulator_ON,PWR_STOPEntry_WFI);//进入停止模式
/*
当一个中断或唤醒事件导致退出停止模式时,HSI被选为系统时钟
你程序刚上电,是72MHz的主频 ,但是进入停止模式,再唤醒之后
就变成8MHz的主频了; 所以,我们一般在停止模式唤醒后
第一时间就是重新启动HSE,配置主频为72MHz ,我们只需要再调用一下Systemlnit就行
*/
SystemInit();
}
}
当一个中断或唤醒事件导致退出停止模式时,HSI被选为系统时钟 -------- 你程序刚上电,是72MHz的主频 , 但是进入停止模式,再唤醒之后 , 就变成8MHz的主频了; 所以,我们一般在停止模式唤醒后 , 第一时间就是重新启动HSE,配置主频为72MHz , 我们只需要再调用一下Systemlnit就行
这个代码可以使用外部中断触发唤醒 , 所以我们可以让它进入更为省电的停止模式 . 在停止模式下1.8V区域的时钟关闭 , CPU和外设都没有时钟了 , 但是外部中断的工作是不需要时钟的
D: 待机模式+实时时钟
1:连接图
2:代码
对实时时钟的代码进行改进 详情参考 : 12:STM32---RTC实时时钟----B:实时时钟
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MYRTC.h"
int main(void)
{
OLED_Init();
MYRTC_init();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
OLED_ShowString(1, 1, "CNT :");
OLED_ShowString(2, 1, "ALR :");
OLED_ShowString(3, 1, "ALRF:");
//开启WAUP--PA0的引脚触发
PWR_WakeUpPinCmd(ENABLE);
uint32_t Alarm = RTC_GetCounter() + 10; //写RTC计数器
RTC_SetAlarm(Alarm); //设置RTC的闹钟
OLED_ShowNum(2, 6, Alarm, 10);
while (1)
{
OLED_ShowNum(1, 6, RTC_GetCounter(), 10);
/*
之后,随着CNT的增大 , CNT会和ALR(RTC_SetAlarm)相等 , 然后触发闹钟标志位置1
*/
OLED_ShowNum(3, 6, RTC_GetFlagStatus(RTC_FLAG_ALR), 1); //闹钟
OLED_ShowString(4, 1, "Running");
Delay_ms(100);
OLED_ShowString(4, 1, " ");
Delay_ms(100);
OLED_ShowString(4, 9, "STANDBY");
Delay_ms(1000);
OLED_ShowString(4, 9, " ");
Delay_ms(100);
OLED_Clear();
//进入待机模式
PWR_EnterSTANDBYMode();
}
}