参考:bilibili:江协科技,看着视频写了个文字版的,相当于总结笔记,也方便以后查看。
简介:
PWR负责管理STM32内部的电源供电部分,可以实现可编程电压检测器和低功耗模式的功能,这里主要讲解低功耗模式。
低功耗模式包括睡眠模式(sleep)、停机模式(stop)和待机模式(standby),可在系统空闲时,降低STM32的功耗,延长设备的使用时间。
下面是电源框图,可以看出主要分为了三个区域:VDDA供电区域,VDD供电区域和1.8V供电区域还有最下面的后备供电区域。其中,VDD供电区域中有电压调节器,可以电压调节至1.8V,然后为1.8V区域供电。
![](https://i-blog.csdnimg.cn/blog_migrate/e76d38572823f8bd1676e3fcd94953e8.png)
大致看一下电源框图,了解各部分是由哪个供电区域供电。
下面是低功耗模式表:
![](https://i-blog.csdnimg.cn/blog_migrate/65d6c73fc93caa6c7cb90c8c8a35f3e7.png)
我们可以看到各个模式的进入条件,唤醒条件和模式对电路的操作。
首先,我们来看睡眠模式。
进入条件为WFI或WFE指令,如果是WFI指令进入睡眠模式,则唤醒需要中断来唤醒,如果由WFE指令进入睡眠模式,唤醒需要事件来唤醒。
睡眠模式只是将CPU时钟关闭,对其他无影响。进入睡眠模式后,程序停止运行,各个寄存器的数据都还在。
关于睡眠模式,我们可以通过设置系统控制寄存器中的SLEEPONEXIT位的值来选择睡眠模式的机制:如果SLEEPONEXIT位被清除,当WRI或WFE被执行时,微控制器立即进入睡眠模式。如果SLEEPONEXIT位被置位,系统从最低优先级的中断处理程序中退出时,微控制器就立即进入睡眠模式。即:SLEEPONEXIT = 0时,立刻进入睡眠模式,SLEEPONEXIT = 1时,先处理完中断处理程序后再进入睡眠模式。
了解了睡眠模式,我们尝试去写睡眠模式的代码,我们可以做串口的实验,由OLED屏显示,代码如下:
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,2);
}
OLED_ShowString(2, 1, "Running:");
Delay_ms(500);
OLED_ShowString(2, 1, " ");
Delay_ms(500);
__WFI();
}
}
我们可以看到,主循环的最后一行调用了__WFI函数进入了睡眠模式。
程序现象:
睡眠模式下,当我们在串口助手上发送数据时产生中断,标志位改变,然后接收数据显示数据,OLED屏幕上会闪烁一次Running。
如果没有进入睡眠模式,我们在主循环中会不断地检测标志位,不断地显示Running,这会导致无意义的耗电,睡眠模式可以减少此类耗电。
睡眠模式的进入条件是WFI或WFE指令,所以不需要PWR外设的函数。
接着,我们来看停机模式:
进入条件:将
SLEEPDEEP设置为1,表示深度睡眠模式,将PDDS设为0,表示停机模式,LPDS可以设置电压调节器,LPDS = 0,电压调节器开启,LPDS = 1,电压调节器处于低功耗模式,最后调用WFI或WFE指令即可。
唤醒条件为:任一外部中断。
停机模式下1.8V供电区域的的所有时钟都被停止,PLL、HSI和HSE RC振荡器的功能被禁止,SRAM和寄存器内容被保留下来。在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态。意思就是CPU和外设暂停工作,但是电压调节器没有关,存储器和寄存器里的数据还在,只能由外部中断来唤醒。
了解了停机模式,下面来尝试下一下停机模式的代码,由于只能由外部中断来唤醒,所以串口的中断就不行了,串口实验做不了,可以使用对射式红外传感器计次来做实验。
代码如下:
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_Cet(),5);
OLED_ShowString(2, 1, "Running");
Delay_ms(100);
OLED_ShowString(2, 1, " ");
Delay_ms(100);
PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);
SystemInit();
}
}
由于要使用PWR外设的库函数,所以要先开启PWR外设的时钟,在while循环中,最后调用
PWR_EnterSTOPMode函数进入停机模式,传入的参数为PWR_Regulator_ON表示电压调节器关闭,PWR_STOPEntry_WFI表示使用WFI指令。
在
PWR_EnterSTOPMode之后还有一个SystemInit函数。这是由于,当一个中断或唤醒事件导致退出停止模式时,
HSI RC振荡器被选为系统时钟。我们本来是选用HSE作为系统时钟的,但是唤醒后选HSI作为系统时钟了,这样主频会降低,为了保证其他部分不出现未知的错误,我们在
PWR_EnterSTOPMode调用一次SystemInit函数重新选择HSE作为系统时钟。
程序现象:
烧录后闪烁一次Running后进入停机模式,当红外传感器检测到信号时产生外部中断,然后唤醒,唤醒后会OLED屏上闪烁一次Running,之后又进入停机模式。
最后,来看待机模式
进入条件:将
SLEEPDEEP设置为1,表示深度睡眠模式,将PDDS设为1,表示待机模式,然后调用WFI或WFE指令即可。
唤醒条件:当一个外部复位(NRST
引脚
)
、
IWDG
复位、
WKUP
引脚上的上升沿或
RTC
闹钟事件的上升沿发生时
,微控制器从待机模式退出。从待机唤醒后,除了电源控制
/
状态寄存器,
所有寄存器被复位。
待机模式可实现系统的最低功耗,整个1.8V供电区域被断电。PLL、HSI和HSE振荡器也被断电。SRAM和寄存器内容丢失。只有备份的寄存器和待机电路维持供电。在待机模式下,所有的IO引脚变为高阻态。
了解了待机模式,下面来尝试下一下待机模式的代码,我们可以使用RTC实时时钟的闹钟事件来推出待机模式。
代码如下:
int main(void)
{
uint32_t Alarm;
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:");
PWR_WakeUpPinCmd(ENABLE);
Alarm = RTC_GetCounter() + 10;
RTC_SetAlarm(Alarm);
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(1000);
OLED_ShowString(4, 1, " ");
Delay_ms(1000);
OLED_Clear();
PWR_EnterSTANDBYMode();
}
}
由于要使用PWR外设的库函数,所以要先开启PWR外设的时钟,之后我们设置闹钟的值为当前计数器的值加10,也就是10秒,每10秒会退出一次待机模式。而
PWR_WakeUpPinCmd函数可以使能唤醒引脚的功能。调用了这个函数,在WKUP引脚的上升沿也可以推出待机模式。在while循环中,最后调用
PWR_EnterSTANDBYMode函数进入待机模式。
退出待机模式后,程序从头执行,所以PWR_EnterSTANDBYMode函数之后不要再写代码了,因为不会执行到。
OELD_Clear函数是为了模拟模块关闭的效果。
程序现象:
OLED屏幕会显示一下当前计数器的值和闹钟的值,然后清屏,进入待机模式,在10秒后,当前计数器的值等于闹钟值,退出待机模式,程序从头开始,重新设置闹钟值,再显示,之后再进入待机模式。我们也可以使用WKUP引脚的上升沿来退出待机模式,在STM32F103C8T6中,WKUP引脚是PA0,我们可以在PA0引脚接入飞线,当另一端接入VCC时产生上升沿,退出待机模式。
到此为止,三种低功耗模式就讲解完了。
![](https://i-blog.csdnimg.cn/blog_migrate/0214532ffc3052907b5150737247bfff.png)