PWR简介

低功耗模式说白了就是省电
结构

VDDA是模拟供电。VDD供电中电压调节器可以把电压降至1.8v,给1.8v供电区域供电,电压调节器就相当于1.8v区域的电源,电压低可以降低功耗。RCC是复位与时钟控制器,后备供电区域中RCC BDCR寄存器是RCC的寄存器,叫备份域控制寄存器,低电压检测器可以控制给后备区域的供电是VDD还是VBAT。
上电复位和掉电复位

当电压低于下限PDR时stm32复位,停止操作;高于上限POR时解除复位。上限和下限之间相差40mV,这是为了防抖。解除复位也有一个滞后时间,不会马上解除。阈值通常在1.9v左右。
可编程电压检测器PVD


PVD也是检测电压,上限和下限之间相差100mV。电压低于下限或高于上限时PVD可以申请中断提醒用户,PVD中断通道要借助外部中断EXTI,因为只有外部中断可以唤醒停止模式。
低功耗模式

这三种低功耗模式,从上到下,越来越省电,关闭部分越来越多,越来越低功耗,但也越来越难唤醒;1.8v区域是芯片可以控制的,VDD区域的VDD是外部输入,芯片不能控制,只能控制其中晶振,后备区域更是独立。
睡眠模式
进入睡眠模式,需要通过调用WFI或者WFE,WFI就是等待中断,所以唤醒是通过中断唤醒;WFE就是等待事件,唤醒通过事件唤醒。关闭了1.8v区域的CPU时钟,但没有影响到其中的外设;对VDD区域没有影响;电压调节器是开启的,所以1.8v区域没有掉电,SRAM存储器和寄存器里的数据都还在。
停机模式
进入停机模式,配置PDDS位为0(PDDS=1表示待机模式),LPDS位配置电压调节器开启还是低功耗模式,配置SLEEPDEEP=1表示要进入深度睡眠,最后调用WFI或者WFE。选择WFI,则唤醒是通过外部中断的中断模式唤醒;选择WFE,则唤醒是通过外部中断的事件模式唤醒。停机模式关闭了1.8v区域的CPU和外设时钟,既然CPU和外设都关闭了,也就不需要VDD区域的HSI和HSE了,它们也关闭了。电压调节器是开启或者低功耗模式,区别就是低功耗模式更省电,唤醒时间更久,1.8v区域就没有掉电,数据都还在。但是LSI,LSE没有关闭,因为这是为RTC和看门狗准备的。
待机模式
进入待机模式,配置PDDS位=1,SLEEPDEEP位=1,调用WFI或者WFE。只能通过WKUP引脚上升沿、RTC闹钟、NRST引脚复位、和独立看门狗复位来唤醒。几乎能关闭的都关闭了,1.8v区域都关闭,VDD区域HSE,HSI关闭,电压调节器也关闭了,所以数据都清零了。
模式选择

WFI和WFE是最后选择,一旦执行后就会进入低功耗模式,所以图中寄存器配置都要在选择WFI,WFE之前进行。
三种模式细节

睡眠模式时电压调节器没有关闭,SRAM和寄存器数据都在,所以唤醒后程序会从暂停的地方开始运行。

停止模式时电压调节器也没有关闭,SRAM和寄存器数据都在,所以唤醒后程序也会从暂停地方开始运行。停止模式时HSI,HSE都被关闭,但在唤醒后不会主动开启HSE,会将HSI选为系统时钟,所以唤醒后系统主频就不是72MHZ,而是HSI的8MHZ,停止模式需要唤醒后手动开启HSE。

待机模式时电压调节器关闭,SRAM数据丢失,所以唤醒后程序从头开始运行,从头开始程序会自动选择HSE。
如果正常模式下电流为50mA,那么睡眠模式下电流就是30mA,停止模式下为50uA,待机模式为5uA。
睡眠模式+串口接收数据代码
下位机等待上位机发送数据,可能偶尔才会发送,但需要程序一直运行一直等待,比较耗电。
低功耗模式中睡眠模式关闭了CPU时钟,程序停止运行,外设时钟没有关闭,USART依旧可以工作,能及时接收到数据,收到数据产生的中断也能唤醒睡眠模式。另外两个低功耗模式把外设时钟也关闭了,就收不到数据了,收到数据的中断也不能唤醒这两个模式,所以选择睡眠模式搭配串口接收数据。
主函数
uint8_t RXdata;
int main()
{
OLED_Init();
serial_init();
while(1)
{
RXdata=serial_receive();
serial_send(RXdata);
OLED_ShowHexNum(1,1,RXdata,2);
OLED_ShowString(2,1,"running");
Delay_ms(100);
OLED_ShowString(2,1," ");
Delay_ms(100);
//睡眠模式选择WFI中断唤醒,还有两位SLEEPDEEP,SLEEPONEXTI需要用寄存器配置,这里不配置就使用默认值0,即立即睡眠
__WFI();
//上电程序运行到此就会进入睡眠,running不再显示,外设USART还在工作,接收到数据,产生中断唤醒后会先进入中断函数执行,然后才会继续从这里执行程序,接收数据,running显示一下,运行到此又进入睡眠
}
}
停止模式+使用外部中断计次代码
主函数
//电平没有变化时程序依然在不断循环检测,running一直在显示。使用停止模式,running只在唤醒时显示,外部中断可以唤醒。外部中断是不需要时钟的。进入停止模式需要PWR
int main()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
OLED_Init();
countsensor_init();
OLED_ShowString(1,1,"count:");
while(1)
{
OLED_ShowNum(1,7,countsensor_get(),4);
OLED_ShowString(2,1,"running");
Delay_ms(100);
OLED_ShowString(2,1," ");
Delay_ms(100);
PWR_EnterSTOPMode(PWR_Regulator_ON,PWR_STOPEntry_WFI);//电压调节器开启,选择WFI。这个函数中已经配置好了SLEEPDEEP,PDDS和LPDS位。
//程序运行到此就进入了停止模式,产生外部中断时唤醒,进入中断函数执行后,回到这里继续向下执行程序,记一次数,显示一次running后又运行到此进入停止模式
//注意第一次唤醒之后系统不会主动打开HSE时钟,而是选择了HSI时钟,所以之后running闪烁时间不对
SystemInit();//调用函数恢复使用HSE时钟
}
}
待机模式+RTC闹钟、WKUP引脚代码
//RTC闹钟触发和WKUP引脚上升沿可以唤醒待机模式,进入待机模式需要PWR。
int main()
{
OLED_Init();
MYRTC_init();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
PWR_WakeUpPinCmd(ENABLE);//WKUP引脚在PA0,使能后强制为下拉输入,不需要初始化PA0口。WKUP引脚输入高电平一次也会唤醒一次。
OLED_ShowString(1,1,"CNT:");//计数器值
OLED_ShowString(2,1,"ALR:");//闹钟值
uint32_t alarm=RTC_GetCounter()+10;
RTC_SetAlarm(alarm);//设定闹钟值为当前计数值+10
while(1)
{
OLED_ShowNum(2,5,alarm,10);
OLED_ShowNum(1,5,RTC_GetCounter(),10);
Delay_ms(500);
//stm32待机,如果外挂模块还在运行的话依然耗电,所以stm32待机前应该把能关闭的外挂模块都关闭
OLED_Clear();//以OLED为例,待机后清屏,只有唤醒后,程序从头开始运行后才会显示。
PWR_EnterSTANDBYMode();//进入待机模式,闹钟触发和WKUP引脚上升沿时会唤醒,程序从头开始执行。
//从头开始执行,程序会自动执行SystemInit()函数,选择HSE时钟,不需要像停止模式那种手动开启
}
}

567

被折叠的 条评论
为什么被折叠?



