江协科技STM32学习笔记(第12章 PWR电源控制)

第12章 PWR电源控制

12.1 PWR电源控制

12.1.1 PWR简介

芯片在3种低功耗模式下,是没法直接再下载程序的。这是因为芯片在睡眠,不会关注调试端口了。解决办法就是:1.按住复位键不动;2.点下载按钮;3.及时从开复位键。这样就能下载成功了。如果不小心禁用了调试端口,其实也可以这样来解决。

PWR(Power Control)电源控制;

PWR负责管理STM32内部的电源供电部分,可以实现可编程电压监测器和低功耗模式的功能;

可编程电压监测器(PVD)可以监控VDD电源电压,当VDD下降到PVD阀值以下或上升到PVD阀值之上时,PVD会触发中断,用于执行紧急关闭任务;

这个功能预想二点场景是使用电池供电,或者对安全要求比较高的设备,如果供电电压在逐渐下降,在电压过低的情况下,可能会导致内部或者外部电路发生不确定的错误。为了避免不确定的因素,在电源电压低于设定的阈值时,我们可以主动出击,提前发出警告,并且关闭比较危险的设备。

低功耗模式包括睡眠模式(Sleep)、停机模式(Stop)和待机模式(Standby),可在系统空闲时,降低STM32的功耗,延长设备使用时间。

正常运行的状态下,程序永远都不会停下来,所以主程序的最后,一般都是个死循环,即使需要空闲,让程序停下来,也得来个空循环让程序一直转圈卡住,但是程序运行就会耗电,空循环的耗电量是很大的,比如遥控器,如果不用它的时候,程序会一直空循环,那么用不了几天,电池就没电了。所以说对于这些设备,需要低功耗模式,在空闲状态时,关闭不必要的硬件,比如直接把CPU断电或者关闭时钟,这样程序就不会运行了,但是在低功耗模式下,我们也需要保留必要的唤醒电路,比如串口接收数据的中断唤醒、外部中断唤醒、RTC闹钟唤醒等等,在需要设备工作时,STM32能够立刻重新投入工作,这样才行。所以低功耗模式,我们要考虑管壁哪些硬件、保留哪些硬件,以及如何去唤醒,当然关闭越多的硬件,设备约省电,唤醒就越麻烦。

12.1.2 电源框图

整体上看,这个图可以分为3个部分,最上面是模拟部分供电,叫VDDA(VDD Analog);中间是数字部分供电,包括两块区域,VDD供电区域和1.8V供电区域;下面是后备供电,叫做VBAT(V Battery)。

VDDA供电区域主要负责模拟部分的供电,其中包括AD转换器、温度传感器、复位模块、PLL锁相环。这些电路的供电正极是VDDA,负极是VSSA。其中AD转换器,还有两根参考电压的供电脚,叫做VREF+和VREF-,这两个脚在引脚多的型号里会单独引出来,在引脚上的型号,比如我们这个C8T6,VREF+和VREF-在内部已经分别接到了VDDA和VSSA了。

中间部分供电由两部分组成,左边部分是VDD供电区域,其中包括IO电路、待机电路(唤醒逻辑和独立看门狗),右边部分是VDD通过电压调节器,降压到1.8V,提供给后面这一块的1.8V供电区域。1.8V供电区域包括CPU核心、存储器和内置数字外设。可以看出,STM32内部的大部分关键电路,CPU、存储器和外设,其实都是以1.8V的低电压运行的。当这些外设需要与外界进行交流时,才会通过IO电路转换到3.3V。所以我们从外部看好像STM32内部全是3.3V。实际上,它内部的CPU、外设等,都是以1.8V供电运行,使用低电压运行的最主要目的是降低功耗。电压越低,内部电路运行的功耗就相对越低。

 VBAT和后备供电区域:RCC BDCR是RCC的寄存器,叫备份域控制寄存器。也是和后备域有关的寄存器,所以也可以由VBAT供电。低电压检测器可以控制开关,VDD有电啥时,由VDD供电,VDD没电时,由VBAT供电。

12.1.3 上电复位和掉电复位

当VDD或者VDDA电压过低时,内部电路直接产生复位,让STM32复位住,不要乱操作。 这个复位和不复位的界限之间,设置了一个40mV的迟滞电压,大于上限POR(Power On Reset)时解除复位。小于下辖PDR(Power Down Reset)时复位。这是一个典型的迟滞比较器,设置两个阈值的作用,就是防止电压在某个阈值附近波动时,造成输出也来回抖动。下面的复位信号Reset是低电平有效的,所以在前面和后面,电压过低时,是复位的,中间电压不正常的时候,不复位。那这个电压上限和下限具体是多少值呢?还有接触复位的滞后时间,是多久呢?

12.1.4 可编程电压监测器

PVD触发滞后,芯片还是能正常工作的, 只不过是电源电压过低,该提醒一下用户了。下面那的PVD输出,这个是正逻辑,电压过低时为1,电压正常时为0,这个信号可以去申请中断,在上升沿或者下降沿时,触发中断,以此提醒程序进行适当的处理。PVD的中断申请是通过外部中断实现的,如下图所示,所以要配置PVD的话,记得要配置外部中断。 

RTC的闹钟信号也有借道外部中断, 其实RTC自己是有中断的,因为低功耗模式设计的是,只有外部中断可以唤醒停止模式。其它这些设备,也想唤醒停止模式的话,就可以通过借道外部中断来实现。USB和ETH也都有它们的WeakUp唤醒信号接过来了,目的也是为了唤醒停止模式。 

12.1.5  低功耗模式

低功耗模式有三种,这三种模式从上到下关闭的电路越来越多,从上到下越来越省电,同时,从上到下,越来越难唤醒。

睡眠模式:这是浅睡眠,就相当于打了个盹。直接调用WFI或者WFE即可进入,这两个东西是内核的指令,对应库函数里,有对应的函数,直接调用函数即可。

其中WFI(Wait For Interrupt)、等待中断,意思就是我先睡了,如果有中断发生的话,再叫我起来,所以对应的唤醒条件是任一中断,调用WFI进入的睡眠模式,任何外设发生任何中断时,芯片都会立刻醒来,因为中断发生了,所以醒来之后的第一件事,一般就是处理中断函数。下面WFE(Wait For Event)等待事件,唤醒事件可以唤醒,对应的唤醒条件是唤醒事件,这个事件可以是外部中断配置为事件模式,也可以是使能了中断,但是没有配置NVIC。调用WFE进入的睡眠模式,产生唤醒事件时,会立刻醒来。醒来之后一般不需要进行中断函数,直接从睡得地方继续进行。

停机模式:如何进入停机模式,首先SLEEPDEEP位设置为1,告诉CPU,你可以放心的睡,进入深度睡眠模式,PDDS这一位用来区分它是停机模式还是待机模式,PDDS=0进入停机模式,PDDS=1进入待机模式。所以想要进入停机模式,PDDS要先设置为0,之后LPDS用来设置最后的电压调节器,是开启还是进入低功耗模式,LPDS=0,电压调节器开启,LPDS=1,电压调节器进入低功耗,最后,当我们把这些位提前设置好了,最后再调用WFI或者WFE,芯片就可以进入停止模式了。这个模式下,芯片睡的更深,关的东西更多,唤醒条件更苛刻一些,是任一外部中断。

待机模式:和停机模式差不多,首先SLEEPDEEP也是置1,即将深度睡眠,然后PDDS置1,表示即将进入待机模式,最后调用WFI或WFE就可以进入待机模式了。1.8V的电源关闭,内部的存储器和寄存器的数据全部丢失,但是和停止模式一样,并不会主动关闭LSI和LSE这两个低速时钟,因为这两个时钟还要维持RTC和独立看门狗的运行,所以不会关闭。

12.1.6 模式选择

执行WFIWait For Interrupt)或者WFEWait For Event)指令后,STM32进入低功耗模式。

12.1.7  睡眠模式

执行完WFI/WFE指令后,STM32进入睡眠模式,程序暂停运行,唤醒后程序从暂停的地方继续运行;

SLEEPONEXIT位决定STM32执行完WFIWFE后,是立刻进入睡眠,还是等STM32从最低优先级的中断处理程序中退出时进入睡眠;

在睡眠模式下,所有的I/O引脚都保持它们在运行模式时的状态;

WFI指令进入睡眠模式,可被任意一个NVIC响应的中断唤醒;

WFE指令进入睡眠模式,可被唤醒事件唤醒。

12.1.8 停止模式

执行完WFI/WFE指令后,STM32进入停止模式,程序暂停运行,唤醒后程序从暂停的地方继续运行; 

1.8V供电区域的所有时钟都被停止,PLLHSIHSE被禁止,SRAM和寄存器内容被保留下来;

在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态;

当一个中断或唤醒事件导致退出停止模式时,HSI被选为系统时钟;

我们的程序,默认再SystemInit函数里的配置,是使用的HSE外部高速时钟,通过PLL倍频,得到72MHz主频,但是进入停止模式后,PLL和HSE都停止了,而且在退出停止模式时,它并不会再自动帮我们开启PLL和HSE,而是默认用HSI的8MHz,直接作为主频,所以说如果忽略了这个问题,那么就会出现一个现象,程序刚上电是72MHz的主频,但是进入停止模式,再唤醒之后,就变成8MHz的主频了。所以我们一般在唤醒后,第一时间就是重启HSE,配置主频为72MHz,这个操作也不麻烦,配置函数已经写好了,我们只需要再调用SystemInit就行。

当电压调节器处于低功耗模式下,系统从停止模式退出时,会有一段额外的启动延时;

WFI指令进入停止模式,可被任意一个EXTI中断唤醒;

WFE指令进入停止模式,可被任意一个EXTI事件唤醒;

12.1.9 待机模式

执行完WFI/WFE指令后,STM32进入待机模式,唤醒后程序从头开始运行;

整个1.8V供电区域被断电,PLLHSIHSE也被断电,SRAM和寄存器内容丢失,只有备份的寄存器和待机电路维持供电;

在待机模式下,所有的I/O引脚变为高阻态(浮空输入);

对于输出来说,既不输出高电平,也不输出低电平,呈现高阻态;对于输入来说,不上拉也不下拉,呈现浮空输入状态。实际上GPIO的配置里,没有高阻态这个配置,它其实就是浮空输入配置,浮空输入,对于输出而言,就是高阻态。所以说如果提前点了个灯,进入待机模式后,无论这个灯是高电平点亮,还是低电平点亮,它都会熄灭,GPIO对外不输出高低电平,也不流过电流。

WKUP引脚的上升沿、RTC闹钟事件的上升沿、NRST引脚上外部复位、IWDG复位退出待机模式。

12.2 修改主频

12.2.1 硬件电路

12.2.2 软件部分

(1)复制《OLED显示屏》工程并改名为《修改主频》

(2)修改“system_stm32f10x.c”文件权限

有关主频的参数在“system_stm32f10x.c”里,但这个文件默认是只读的,因此要写修改其权限。

(3)修改“system_stm32f10x.c”文件频率参数

(4)main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"                      // 调用延时头文件
#include "OLED.h"

uint8_t KeyNum;

int main(void)
{
	OLED_Init();                        // 初始化OLED屏幕
	OLED_ShowString(1,1,"SYSCLK:");
	OLED_ShowNum(1,8,SystemCoreClock,8);	
	while(1)
	{	
        OLED_ShowString(2,1,"Running:");
		Delay_ms(500);                  //注意,主频修改后Delay函数的时钟发生变换,所以主频修改后这里也应该进行相应修改
		OLED_ShowString(2,1,"        ");
		Delay_ms(500);
	}
}

主频修改后相应得延时函数也得跟着更改才能保证准确性,所以一般不建议修改主频。 

12.3 睡眠模式+串口发送+接收

12.3.1 硬件电路

12.3.2 软件部分

(1)复制《串口发送+接收》并改名为《睡眠模式+串口发送+接收》

在《串口发送+接收》的基础上,我们要为它加入低功耗的代码,假设我们目前要用STM32在做一个下位机,下位机接收到电脑发过来的指令,然后执行相应的功能,电脑随时都可能通过串口发送指令,当然也可以几个小时、几天都不发送指令,为了随时能响应指令,STM32就得时刻准备着,比如以下代码就一直在不断地检查标志位,但是如果一直不发指令,这些操作都没啥意义,还比较费电。当然你可能说把这段代码放在中断里就行了,但是即使主循环是空的,它CPU也是在不断耗电的,所以对于这种靠中断触发,就没什么事的代码,我们就可以给它加入低功耗模式,没事的时候就低功耗,中断来了,再醒来干活就行了。对于当前这个代码可以加入哪一种低功耗模式呢?

首先,睡眠模式,CPU时钟关闭,程序不再执行,但是外设的时钟不会关,USART硬件电路还是可以接收数据的,USART收到数据后,产生中断,唤醒CPU,所以睡眠模式可以。

之后,停机模式(停止模式),这个模式下,所有1.8V区域的时钟都关了,CPU和外设都不能运行,那自然USART也收不到数据,产生不了中断了,并且USART的中断也不能唤醒停止模式,所以当前这个程序功能,用不了停止模式。所以待机模式自然也不行了。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"                      // 调用延时头文件
#include "OLED.h"
#include "Serial.h"


uint8_t RxData;

int main(void)
{
	OLED_Init();                                 // 初始化OLED屏幕
	OLED_ShowString(1,1,"RxData:");
	Serial_Init();
	while(1)
	{	
		if(Serial_GetRxFlag()== 1)  //if成立,就说明收到数据了
		{	
			RxData = Serial_GetRxData();
			Serial_SendByte(RxData);
			OLED_ShowHexNum(1,8,RxData,2);     
			//无需手动清零
		}
	}
}

(2)main.c 

#include "stm32f10x.h"                  // Device header
#include "Delay.h"                      // 调用延时头文件
#include "OLED.h"
#include "Serial.h"


uint8_t RxData;

int main(void)
{
	OLED_Init();                                 // 初始化OLED屏幕
	OLED_ShowString(1,1,"RxData:");
	Serial_Init();
	while(1)
	{	
		if(Serial_GetRxFlag()== 1)  //if成立,就说明收到数据了
		{	
			RxData = Serial_GetRxData();
			Serial_SendByte(RxData);
			OLED_ShowHexNum(1,8,RxData,2);     
			//无需手动清零
		}
		OLED_ShowString(2,1,"Running");
		Delay_ms(100);
		OLED_ShowString(2,1,"       ");
		Delay_ms(100);
		__WFI();          //中断唤醒,推荐使用中断唤醒,程序会在这里进入睡眠模式,然后产生中断后继续运行
//		__WFE();          //事件唤醒,配置比较麻烦
		/*不开启睡眠模式时,循环会不断执行,开启睡眠模式后,循环只在中断唤醒发生后执行*/
		
	}
}

不开启睡眠模式时,循环会不断执行,开启睡眠模式后,循环只在中断唤醒发生后执行。 

这里直接使用

__WFI();

进入睡眠模式,在相当于使用了默认的立刻睡眠模式,即下图所示的SLEEPONEXIT=0;

如果一定需要修改,参照下图。 

12.4 停止模式+对射式红外传感器计次

12.4.1 硬件电路

12.4.2 软件部分

(1)复制《对射式红外传感器计次》并更改名为《停止模式+对射式红外传感器计次》

(2)外部中断是不需要时钟的,因此可以采用停止模式来让它工作。

(3)PWR库函数

void PWR_DeInit(void);                                //恢复缺省配置
void PWR_BackupAccessCmd(FunctionalState NewState);   //使能后备区域的访问
void PWR_PVDCmd(FunctionalState NewState);            //使能PVD功能
void PWR_PVDLevelConfig(uint32_t PWR_PVDLevel);       //配置PVD的阈值电压
void PWR_WakeUpPinCmd(FunctionalState NewState);      //使能位于PA0位置的WKUP引脚,配合待机模式使用
void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry);        //进入停止模式,调用这个函数就可以进入停止模式
void PWR_EnterSTANDBYMode(void);                      //进入待机模式
FlagStatus PWR_GetFlagStatus(uint32_t PWR_FLAG);      //获取标志位
void PWR_ClearFlag(uint32_t PWR_FLAG);                //清除标志位

(4)注意下载程序时:1.按住复位键不动;2.点下载按钮;3.及时从开复位键。

(5)注意:这里如果中断函数里写了延时函数,会出现卡住不动的情况,把中断函数去掉。

(6)main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"                      // 调用延时头文件
#include "OLED.h"
#include "CountSensor.h"

uint8_t KeyNum;

int main(void)
{
	OLED_Init();                                 // 初始化OLED屏幕
	CountSensor_Init();
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);    //开启PWR的外设时钟控制
	OLED_ShowString(1,1,"Count:");          // 在1行3列显示字符串
	while(1)
	{	
		OLED_ShowNum(1,7,CountSensor_Get(),5);    //将中断次数显示到OLED屏幕上
		OLED_ShowString(2,1,"Running");
		Delay_ms(100);
		OLED_ShowString(2,1,"       ");
		Delay_ms(100);
		PWR_EnterSTOPMode(PWR_Regulator_ON,PWR_STOPEntry_WFI);   //第一个参数开启电压调节器在停止模式里的状态,WFI模式进入停止
		SystemInit();
		/*我们的程序,默认再SystemInit函数里的配置,是使用的HSE外部高速时钟,通过PLL倍频,得到72MHz主频,但是进入停止模式后,
		PLL和HSE都停止了,而且在退出停止模式时,它并不会再自动帮我们开启PLL和HSE,而是默认用HSI的8MHz,直接作为主频,所以说如果忽略了这个问题,
		那么就会出现一个现象,程序刚上电是72MHz的主频,但是进入停止模式,再唤醒之后,就变成8MHz的主频了。所以我们一般在唤醒后,第一时间就是重启HSE,
		配置主频为72MHz,这个操作也不麻烦,配置函数已经写好了,我们只需要再调用SystemInit就行。*/
	}
}

12.5 待机模式+实时时钟

12.5.1 硬件电路

12.5.2 软件部分

(1)复制《实时时钟》工程并改名为《待机模式+实时时钟》

(2)main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"                      // 调用延时头文件
#include "OLED.h"
#include "MyRTC.h"

uint8_t KeyNum;

int main(void)
{
	OLED_Init();                                 // 初始化OLED屏幕
	MyRTC_Init();
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
	
	OLED_ShowString(1,1,"CNT :");                // 显示计数器
	OLED_ShowString(2,1,"ALR :");                // 显示闹钟值
	OLED_ShowString(3,1,"ALRF:");                // 闹钟标志位
	
	PWR_WakeUpPinCmd(ENABLE);                    // WAKEUP上升沿唤醒待机
	
	uint32_t Alarm = RTC_GetCounter()+10;        // 因为闹钟值寄存器是只写的,写进去读不出来,所以用一个变量存一下
	RTC_SetAlarm(Alarm);           // 设置闹钟值为当前的10秒后  
	OLED_ShowNum(2,6,Alarm,10);
	while(1)
	{
		OLED_ShowNum(1,6,RTC_GetCounter(),10);
		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(100);
		OLED_ShowString(4,9,"        ");  
		Delay_ms(100);
		OLED_Clear();                    //模拟OLED进入待机模式
		PWR_EnterSTANDBYMode();          //STM进入待机模式前应当让其他外设先进入待机模式
	}
	
}

(3)使用待机模式一样要把能关的电路都关掉,才能实现最大限度的省电。 

  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值