1.PWR简介
PWR属于外设部分 调用时需要先开启时钟
2.电源框图
- 这个图可以分为三个部分,最上面是模拟部分供电叫做VDDA,中间是数字部分供电,包括两块区域,VDD供电区域和1.8v供电区域,下面是后备供电,叫做VBAT。
-
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供电运行,使用低电压运行的主要目的是降低功耗,电压越低内部电路运行的功耗就相对越低。
-
电压调节器,它的作用是给1.8V区供电,因为我们后面会提到这个1.8V区域和电压调节器,最下面就是我们上一节提到的VBAT后备供电区域了,其中包括LSE 32K晶体振荡器、后备寄存器、RCC BDCR计寄存器和RTC,RCC BDCR是RTC的寄存器,叫做备份域控制寄存器,也是和后备区域有关的寄存器,所以也可以有VBAT供电,然后这里有个低电压检测器,可以控制这个开关,VDD有电时由VDD供电,VDD没电时由VBAT供电。
3.上电复位和掉电复位
当VDD或者VDDA电压过低时,内部电路直接产生复位,让STM32复位住不要乱操作,这个复位和不复位的界限之间,设置了一个40毫伏的迟滞电压,大于上限POR(Power On Reset)时解除复位,小于下限PDR(Power Down Reset)时复位,这是一个典型的迟滞比较器,设置两个阈值的作用,就是防止电压在某个阈值附近波动时,造成输出也来回抖动,下面的复位信号reset是低电平有效的,所以在前面和后面电压过低时是复位的,中间电压正常的时候不复位。
STM32数据手册,在5.3.3内嵌复位和电源控制模块特性里有这个表,这里写了上电或掉电复位阈值,下降沿,也就是PDR掉电复位的阈值下限,典型值是1.88V,上升沿,也就是POR上电复位的阈值上限,典型值是1.92V,1.92-1.88就是迟滞的阈值40毫伏,所以如果忽略迟滞的话,简单来说就是大于1.9V上电,低于1.9V掉电,然后最后一行就是TRSTTEMPO,复位持续时间,典型值是2.5ms。
4.可编程电压检测器
可编程电压监测器,简称PVD,他的工作流程和上面差不多都是监测VDD和VDDA的供电电压,但是PVD的区别就是,首先它这个阈值电压是可以使用程序指定的,可以自定义调节,调节的范围可以看一下数据手册,在这个表的上面就是PVD的阈值,配置PLS寄存器的3个位可以选择右边这么多的阈值,因为这里也同样是迟滞比较,所以有两个阈值,可选范围是2.2V到2.9V左右,PVD上限和下限之间的迟滞电压是100毫伏,可以看到PVD的电压是比上电掉电复位的电压要高的,画个图就是3.3伏是正常的供电,当这个电压降低在2.9伏到2.2伏之间,属于PVD监测的范围,可以通过PVD设置一个警告线,之后再降低到1.9伏,就是复位电路的检测范围,低于1.9伏直接复位住不让动,就是这两个电压监测的工作任务,那当然PVD触发之后,芯片还是能正常工作的,只不过是电源电压过低,该提醒一下用户了,所以看一下下面这个PVD输出,这个是正逻辑,电压过低时为1,电压正常值为0,这个信号可申请中断,在上升沿或者下降沿时触发中断,一是提醒程序进行适当的处理,另外这个PVD的中断申请,是通过外部中断实现的,我们可以看一下外部中断这一节,这个图(EXTI基本结构图)可以看到PVD输出的信号是跑到这里来了,所以如果要使用PVD的话,记得要配置外部中断,然后下面这里还有RTC(EXTI基本结构图),这个是RTC的闹钟信号,也有接到外部中断,其实RTC自己是有中断的,那为啥还要借到外部中断,这个等会就知道了,因为低功耗模式设计的是,只有外部中断可以唤醒停止模式,其他这些设备也想唤醒停止模式的话,都可以通过借道外部中断来实现,其实后面这两个USB和ETH,也都只有他们的wake up唤醒信号接过来了,目的也是为了唤醒停止模式,这个了解一下。
5.低功耗模式
-
睡眠模式,这是浅睡眠,这里写了直接调用WFI或者WFE即可进入,这两个东西是内核的指令,对应库函数里也有对应的函数,直接调用函数即可,其中WFI的意思是wait for interrupt等待中断,意思就是我先睡了,如果有中断发生的话再叫我起来,所以对应的唤醒条件是任意中断,调用WFI进入的睡眠模式,任何外设发生任何中断时,芯片都会立刻醒来,因为中断发生了,醒来之后的第一件事一般就是处理中断函数,然后下面WFE意思是wait for event等待事件,对应的唤醒条件是唤醒事件,这个事件可以是外部中断配置为事件模式,也可以是使能了中断,但是没有配置NVIC,调用WFE进入的睡眠模式,产生唤醒事件时会立刻醒来,醒来之后一般不需要进中断函数,直接从睡的地方继续运行,这是WFI和WFE的作用,相同点是调用任意一个之后,芯片都进入睡眠,不同点是WFI进入的得用中断唤醒,WFE进入的得用事件唤醒,最后看一下睡眠模式对电路的影响,对1.8V区域时钟的影响是CPU时钟关,对其他时钟和ADC时钟无影响,对VDD区域时钟的影响是无,对电压调节器的操作是开,所以睡眠模式对电路的影响就是,只把CPU时钟关了,对其他电路没有任何操作,CPU时钟关了,程序就会暂停,不会继续运行了,CPU不运行芯片功耗就会降低,另外这里还可以看出,关闭电路通常有两个做法,一个是关闭时钟,另一个是关闭电源,关闭时钟,所有的运算和涉及时序的操作都会暂停,但是寄存器和存储器里面保存的数据还可以维持不会消失,关闭电源就是电路直接断电,电路的操作和数据都会直接丢失,所以关闭电源比关闭时钟更省电。
-
停机模式,首先sleepdeep位设置为1,告诉CPU你可以放心的睡进入深度睡眠模式,另外PDDS这一位用来区分它是停机模式,还是下面的待机模式,PDDS等于0进入停机模式,PDDS等于1进入待机模式,之后LPDS用来设置最后这个电压调节器,是开启还是进入低功耗模式,RPDS等于0电压调节器开启,RPDS等于1电压调节器进入低功耗,最后当我们把这些位提前设置好了,最后再调用WFA或者WFE,芯片就可以进入停止模式了,然后停止模式的唤醒,因为这个模式下芯片睡得更深,关的东西更多,所以唤醒条件就苛刻一些,是任一外部中断,刚才睡眠模式是任一中断,所有外设的中断都行,现在停止模式,要求就是只有外部中断才能唤醒,其他中断唤醒不了,刚才我们还提到了,PVD、RTC闹钟、USB唤醒、ETH唤醒借道了外部中断,所以这四个信号也可以唤醒停止模式,因为这里并没有区分WFI和WFE,其实也可以想象得到,WFI要用外部中断的中断模式唤醒,WFE要用外部中断的事件模式唤醒,这是对应的,最后看停止模式对电路有哪些操作呢,首先关闭所有1.8伏区域的时钟,这意思就是不仅CPU不能运行,外设也运行不了,定时器在定时的会暂停,串口收发数据也会暂停,不过由于没关闭电源,所以CPU和外设的寄存器数据都是维持原状的,之后下一个HSI和HSE的振荡器关闭,既然CPU和外设时钟都关了,那这两个高速时钟显然也没用了,所以HSI内部高速时钟和HSE外部高速时钟会关闭,当然他没提到的,LSI内部低速时钟和LSE外部低速时钟,这两个并不会主动关闭,如果开启过这两个时钟还可以继续运行,最后电压调节器,这里可以选择是开启或者处于低功耗模式,刚才说了,这个电压调节器是由这个LPDS位控制的,这个开启和低功耗模式有啥区别呢,其实区别也不大,电压调节器无论是开启还是低功耗,都可以维持1.8伏区域寄存器和存储器的数据内容,区别就是低功耗模式更省电一些,同时低功耗模式在唤醒时要花更多的时间,相反电压调压器开启的话就是更耗电一些唤醒更快了,那这些就是停止模式的介绍,主要操作就是把运行的高速时钟都关了,CPU和外设都暂停工作,但是电压调节器并没有关,存储器和寄存器数据可以维持原样,它的唤醒条件比较苛刻,只能通过外部中断唤醒。
-
待机模式,和停机模式差不多,首先sleepdeep也是置1即将深度睡眠,然后PDDS置1表示即将进入待机模式,最后调用WFI或者WFE就可以进入待机模式了,然后看一下唤醒条件,普通外设的中断和外部中断都无法唤醒待机模式,待机模式只有这几个指定的信号才能唤醒,第一个是wake up引脚的上升沿,wake up引脚,可以看一下引脚定义,这里PA0-WKUP指示了引脚的位置,就是PA0的位置,第二个是RTC闹钟事件,这个我们的示例代码和上一节RTC提到过,RTC闹钟可以唤醒待机模式,应用场景就是芯片每隔一段时间自动工作一次,第三个是NRST引脚上的外部复位,意思就是按一下复位键,它也是能唤醒的,最后一个IWDG独立看门狗复位,这个了解一下就行了,看门狗我们最后介绍,可以看出待机模式,只有这指定的四个信号能唤醒,其他信号都唤醒不了,唤醒条件最为苛刻,之后待机模式对电路的操作基本上是能关的全都关了,1.8伏区域的时钟关闭,两个高速时钟关闭,电压调节器关闭,这意味着1.8伏区域的电源关闭,内部的存储器和寄存器的数据全部丢失,但是和停止模式一样,它并不会主动关闭LSI和LSE两个低速时钟,因为这两个时钟还要维持RTC和独立看门狗的运行,所以不会关闭。
6.模式选择
7.睡眠模式
•执行完WFI/WFE指令后,STM32进入睡眠模式,程序暂停运行,唤醒后程序从暂停的地方继续运行
•SLEEPONEXIT位决定STM32执行完WFI或WFE后,是立刻进入睡眠,还是等STM32从最低优先级的中断处理程序中退出时进入睡眠
•在睡眠模式下,所有的I/O引脚都保持它们在运行模式时的状态
•WFI指令进入睡眠模式,可被任意一个NVIC响应的中断唤醒
•WFE指令进入睡眠模式,可被唤醒事件唤醒
8.停止模式
•执行完WFI/WFE指令后,STM32进入停止模式,程序暂停运行,唤醒后程序从暂停的地方继续运行
•1.8V供电区域的所有时钟都被停止,PLL、HSI和HSE被禁止,SRAM和寄存器内容被保留下来
•在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态
•当一个中断或唤醒事件导致退出停止模式时,HSI被选为系统时钟
•当电压调节器处于低功耗模式下,系统从停止模式退出时,会有一段额外的启动延时
•WFI指令进入停止模式,可被任意一个EXTI中断唤醒
•WFE指令进入停止模式,可被任意一个EXTI事件唤醒
9.待机模式
•执行完WFI/WFE指令后,STM32进入待机模式,唤醒后程序从头开始运行
•整个1.8V供电区域被断电,PLL、HSI和HSE也被断电,SRAM和寄存器内容丢失,只有备份的寄存器和待机电路维持供电
•在待机模式下,所有的I/O引脚变为高阻态(浮空输入)
•WKUP引脚的上升沿、RTC闹钟事件的上升沿、NRST引脚上外部复位、IWDG复位退出待机模式
10.修改主频
10.1 接线图
10.2 相关代码
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"int main(void)
{OLED_Init(); //OLED初始化OLED_ShowString(1, 1, "SYSCLK:"); //显示静态字符串OLED_ShowNum(1, 8, SystemCoreClock, 8); //显示SystemCoreClock变量//SystemCoreClock的值表示当前的系统主频频率while (1){OLED_ShowString(2, 1, "Running"); //闪烁Running,指示当前主循环运行的快慢Delay_ms(500);OLED_ShowString(2, 1, " ");Delay_ms(500);}
}
system_stm32f10x.c中SystemCoreClock函数的定义主频部分 修改成其他M数 OLED显示屏第一行会显示对应主频数的八位格式 第二行的Running频率也会发生改变 一开始是72M 对应的就是1s 因为Delay.c中定义的就是72M 所以周期就为1s 像上述代码是36M 主频变为原来的一半 则周期为2s。
11.睡眠模式+串口接送+接收
11.1 接线图
11.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) //检查串口接收数据的标志位{RxData = Serial_GetRxData(); //获取串口接收的数据Serial_SendByte(RxData); //串口将收到的数据回传回去,用于测试OLED_ShowHexNum(1, 8, RxData, 2); //显示串口接收的数据}OLED_ShowString(2, 1, "Running"); //OLED闪烁Running,指示当前主循环正在运行Delay_ms(100);OLED_ShowString(2, 1, " ");Delay_ms(100);__WFI(); //执行WFI指令,CPU睡眠,并等待中断唤醒}
}
调用WFI()函数可以实现睡眠模式 当while循环执行到WFI() 函数进入睡眠模式 用串口助手发送数据时 USART外设接收到数据 产生中断 唤醒CPU 睡眠模式唤醒之后 在暂停的地方继续运行 所以程序会运行到WIF之后 程序回到while循环开头之前 先进入USART中断函数 在中断函数里读取数据 置RxFlag 清除RXNE 之后回到主循环 这时RxFlag置1 便继续执行下面程序 进行数据回传 所以每发送一次数据 OLED显示数据一次Running 同时RxData显示发送数据
12.停止模式+串口发送+接收
12.1 接线图
12.2 相关代码
12.2.1 相关API
PWR_EnterSTOPMode
/*** @brief Enters STOP mode.* @param PWR_Regulator: specifies the regulator state in STOP mode.* This parameter can be one of the following values:* @arg PWR_Regulator_ON: STOP mode with regulator ON* @arg PWR_Regulator_LowPower: STOP mode with regulator in low power mode* @param PWR_STOPEntry: specifies if STOP mode in entered with WFI or WFE instruction.* This parameter can be one of the following values:* @arg PWR_STOPEntry_WFI: enter STOP mode with WFI instruction* @arg PWR_STOPEntry_WFE: enter STOP mode with WFE instruction* @retval None*/
void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry)
功能:进入停止(STOP)模式
参数:PWR_Regulator: 电压转换器在停止模式下的状态PWR_STOPEntry: 选择使用指令 WFE 还是 WFI 来进入停止模式
返回值:无
第一步先读取PWR_CR寄存器 放在临时变量中 第二步清除PDDS和LPDS位 清除PDDS位代表PDDS=0 对应6.模式选择图中停止模式的选择部分 第三步根据指定参数设置LPDS位 若选择调压器开启 这里LPDS=0 若选择调压器低功耗 则LPDS=1 第四步将临时变量写入PWR_CR寄存器 参数生效 最后设置内核的SLEEPDEEP位 其中|=SLEEPDEEP的掩码表示把SLEEPDEEP置1 即进入深度睡眠模式 程序最后是一个if判断 如果第二个参数是WFI 则执行WFI指令 否则执行WFE指令 停止模式唤醒之后 最底下程序会把SLEEPDEEP位清0
12.2.2 main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"int main(void)
{/*模块初始化*/OLED_Init(); //OLED初始化CountSensor_Init(); //计数传感器初始化/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //开启PWR的时钟//停止模式和待机模式一定要记得开启/*显示静态字符串*/OLED_ShowString(1, 1, "Count:");while (1){OLED_ShowNum(1, 7, CountSensor_Get(), 5); //OLED不断刷新显示CountSensor_Get的返回值OLED_ShowString(2, 1, "Running"); //OLED闪烁Running,指示当前主循环正在运行Delay_ms(100);OLED_ShowString(2, 1, " ");Delay_ms(100);PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI); //STM32进入停止模式,并等待中断唤醒SystemInit(); //唤醒后,要重新配置时钟//按下复位显示Running时间很快,而红外模块计次时Running闪烁慢,唤醒后会自动配置HSI位系统时钟,原来的是72M,现在HSI是8M,若不重新配置,则复位后就是8M,显示时间长}
}
13.待机模式+RTC实时时钟
13.1 接线图
13.2 相关代码
13.2.1 相关API
PWR_EnterSTANDBYMode
/*** @brief Enters STANDBY mode.* @param None* @retval None*/
void PWR_EnterSTANDBYMode(void)
功能:进入待机(STANDBY)模式
参数:无
返回值:无
第一步清除WakeUp标志位 第二部选择STANDBY模式 实际上就是把PDDS置1 表示进入待机模式 第三步把SLEEPDEEP置1 进入深度睡眠 最后一步调用WFI指令
PWR_WakeUpPinCmd
/*** @brief Enables or disables the WakeUp Pin functionality.* @param NewState: new state of the WakeUp Pin functionality.* This parameter can be: ENABLE or DISABLE.* @retval None*/
void PWR_WakeUpPinCmd(FunctionalState NewState)
功能:使能或者失能唤醒管脚功能
参数:NewState: 唤醒管脚功能的新状态,这个参数可以取:ENABLE 或者 DISABLE
返回值: 无
13.2.2 main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyRTC.h"int main(void)
{/*模块初始化*/OLED_Init(); //OLED初始化MyRTC_Init(); //RTC初始化/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //开启PWR的时钟//停止模式和待机模式一定要记得开启/*显示静态字符串*/OLED_ShowString(1, 1, "CNT :");OLED_ShowString(2, 1, "ALR :");OLED_ShowString(3, 1, "ALRF:");/*使能WKUP引脚*/PWR_WakeUpPinCmd(ENABLE); //使能位于PA0的WKUP引脚,WKUP引脚上升沿唤醒待机模式/*设定闹钟*/uint32_t Alarm = RTC_GetCounter() + 10; //闹钟为唤醒后当前时间的后10sRTC_SetAlarm(Alarm); //写入闹钟值到RTC的ALR寄存器OLED_ShowNum(2, 6, Alarm, 10); //显示闹钟值while (1){OLED_ShowNum(1, 6, RTC_GetCounter(), 10); //显示32位的秒计数器OLED_ShowNum(3, 6, RTC_GetFlagStatus(RTC_FLAG_ALR), 1); //显示闹钟标志位 当计时值等于闹钟值 标志位值1 OLED_ShowString(4, 1, "Running"); //OLED闪烁Running,指示当前主循环正在运行Delay_ms(100);OLED_ShowString(4, 1, " ");Delay_ms(100);OLED_ShowString(4, 9, "STANDBY"); //OLED闪烁STANDBY,指示即将进入待机模式Delay_ms(1000);OLED_ShowString(4, 9, " ");Delay_ms(100);OLED_Clear(); //OLED清屏,模拟关闭外部所有的耗电设备,以达到极度省电,待机时熄屏PWR_EnterSTANDBYMode(); //STM32进入停止模式,并等待指定的唤醒事件(WKUP上升沿或RTC闹钟)/*待机模式唤醒后,程序会重头开始运行*/}
}