赞
踩
目录
在学习STM32待机唤醒功能之前,我们首先来系统的学习32单片机的低功耗模式,这将对于我们理解睡眠/停止/待机模式有很大的帮助。
默认情况下,系统复位或者上电复位后,MCU微控制器进入运行模式。在运行模式下,CPU通过HCLK提供时钟,并且执行程序代码。系统提供了多个低功耗模式,可在CPU不需要运行时(例如等待外部事件时)节省功耗。由用户根据应用选择具体的低功耗模式,以在低功耗、短启动时间和可用唤醒源之间寻求最佳平衡。
STM32有三种低功耗模式:
此外,还可以通过其他方式降低功耗:
1. 降低系统时钟速度
2. 不使用APBx和AHBx外设时,将对应的外设时钟关闭
其中:嵌入式线性调压器为备份域和待机电路以外的所有数字电路供电。调压器输出电压约为1.2V。
在运行模式下,可以通过对预分频寄存器编程来降低系统时钟(SYSCLK、HCLK、PCLK1和PCLK2)速度。进入睡眠模式之前,也可以使用这些预分频器降低外设速度。
在运行模式下,可以随时的停止各个外设和存储器的HCLKx和PCLKx以降低功耗。要进一步的降低睡眠模式的功耗,可在执行WFI或者WFE指令之前禁止外设时钟。
外设时钟门控由AHB1外设时钟使能寄存器RCC_AHB1ENR、AHB2外设时钟使能寄存器RCC_AHB2ENR和AHB3外设时钟使能寄存器RCC_AHB3ENR进行控制。
在睡眠模式下,复位RCC_AHBxLPENR和RCC_APBxLPENR寄存器中的对应位可以自动禁止外设时钟。
执行WFI(等待中断)或者WFE(等待事件)指令即可进入睡眠模式。根据M4F内核系统控制寄存器中的SLEEPONEXIT位的设置,可以通过两种方案选择睡眠模式进入机制。
立即休眠:如果SLEEPONEXIT位清零,MCU将在执行WFI和WFE指令时立即进入睡眠模式。
退出时休眠:如果SLEEPONEXIT位置1,MCU将在退出优先级最低的ISR时立即进入睡眠模式。(ISR的全称是Interrupt Service Routines,也就是中断服务,中断是有NVIC中断优先级的,这里的意思就是当执行完中断中优先级最低的中断以后进入休眠)
因为进入睡眠模式是由指令WFI和指令WFE控制的。所以退出休眠模式也应该从这两方面入手。
如果使用WFI指令进入睡眠模式,则通过中断控制器NVIC确认的任意外设中断都会将器件从睡眠模式唤醒。
如果使用WFE指令进入睡眠模式,MCU将在有事件发生时立即退出睡眠模式。
唤醒事件可以通过以下方式产生:
在外设的控制寄存器使能一个中断,但不在NVIC中使能,同时使能M4F内核系统控制寄存器中的SEVONPEND位。当MCU从WFE恢复时,需要清除相应外设的中断挂起位和外设NVIC中断通道挂起位(在NVIC中断清除挂起寄存器中)。
配置一个外部或者内部EXTI线为事件模式。当CPU从WFE恢复时,因为对应事件线的挂起位没有被置位,不必清除相应外设的中断挂起位或NVIC中断通道挂起位。
停止模式基于M4内核深度睡眠模式与外设时钟门控。调压器既可以配置为正常模式,也可以配置为低功耗模式。在停止模式下,1.2V域中的所有时钟都会停止,PLL、HSI和HSE RC振荡器也会被禁止。内部SRAM和寄存器内容将会被保留。
将PWR_CR电源控制寄存器中的FPDS位置1后,Flash闪存还会在器件进入停止模式时进入掉电状态。Flash处于掉电模式时,将器件从停止模式唤醒将需要额外的启动延时。
要进一步降低停止模式的功耗,可将内部调压器设置为低功耗模式。通过对STM32F4的PWR电源控制寄存器(PWR_CR)的LPDS位进行配置。
如果正在进行Flash编程,停止模式的进入将延迟到存储器访问结束后执行。
如果正在访问APB域,停止模式的进入则延迟到APB访问结束后执行。
在停止模式下,可以通过对各个控制位进行编程来选择以下功能:
独立看门狗IWDG:IWDG通过写入其密钥寄存器或使用硬件选项来启动。而且一旦启动便无法停止,除非复位。
实时时钟RTC:通过RCC备份域控制寄存器RCC_BDCR中的RTCEN位进行配置。
内部RC振荡器(LSI RC):通过RCC时钟控制和状态寄存器(RCC_CSR)中的LSION位进行配置。
外部32.768KHz振荡器(LSE OSC):通过RCC备份域控制寄存器RCC_BDCR中的LSEON位进行配置。
在停止模式下,ADC和DAC也会产生功耗,除非在进入停止模式前将其禁止。要禁用这些转换器,必须将ADC_CR2寄存器的ADON位和DAC_CR寄存器中的ENx位都清零。
通过发出中断或者唤醒事件退出停止模式时,将选择HSI RC振荡器作为系统时钟。
待机模式下可达到最低功耗。待机模式基于M4内核深度睡眠模式,该模式在深度睡眠模式时关闭电压调节器。因此1.2V域断电。PLL、HSI振荡器也将关闭。除备份域(RTC寄存器、RTC备份寄存器和备份SRAM)和待机电路中的寄存器外,SRAM和寄存器内容都将丢失。
在待机模式下,可以通过对各控制位进行编程来选择以下功能:
●独立的看门狗 (IWDG):IWDG 通过写入其密钥寄存器或使用硬件选项来启动。而且一 旦启动便无法停止,除非复位。
●实时时钟 (RTC):通过备份域控制寄存器 (RCC_BDCR) 中的 RTCEN 位进行配置。
●内部 RC 振荡器 (LSI RC):通过控制/状态寄存器 (RCC_CSR) 中的 LSION 位进行配置。
●外部 32.768 kHz 振荡器 (LSE OSC):通过备份域控制寄存器 (RCC_BDCR) 中的 LSEON 位进行配置。
检测到外部复位(NRST引脚)、IWDG引脚、WKUP引脚上升沿、RTC闹钟、入侵事件或时间戳事件时,微控制器退出待机模式。从待机模式唤醒后,除PWR电源控制/状态寄存器PWR_CSR外,所有寄存器都将复位。
从待机模式唤醒后,程序将按照复位(启动引脚采样、复位向量已获取等)后的方式重新执行。PWR电源控制/状态寄存器PWR_CSR中的SBF状态标志指示MCU已处于待机模式。
从待机模式唤醒后的代码执行等同于复位后的执行(采样启动模式引脚,读取复位向量等)。电源控制/状态寄存器PER_CSR将会指示内核由待机状态退出。意思就是说从待机模式退出后等同于按下开发板上的复位键,执行相对于的代码。
在进入待机模式后,除了复位引脚、RTC_AF1引脚(PC13)(如果针对入侵、时间戳、RTC闹钟输出或RTC时钟校准输出进行了配置)和WK_UP(PA0)(如果使能了)等引脚外,其他所有IO引脚都将处于高阻态。
总结:在这三种低功耗模式中,最低功耗的是待机模式,在此模式下,最低只需要2.2uA左右的电流。停机模式是次低功耗的,其典型的电流消耗在350uA左右。最后就是睡眠模式了。
通过对进入待机模式的学习,我们已经清楚了需要将电源控制寄存器PWR_CR的 位1 和 位2 置1,使器件在CPU进入深度睡眠时进入待机模式,并且将WUF唤醒标志清零。
这里我们需要 位8 置1,从待机模式中唤醒器件;也可以通过位0来检测是否收到了唤醒标志。
首先注意:对于已经使能了RTC中断或者RTC唤醒中断的情况,必须先禁止中断,清除相关中断标志位,清除唤醒中断WK_UP,等一切都完成以后,再次使能中断,进入低功耗模式。具体如下:
1. 禁止RTC中断(ALRAIE、ALRBIE闹钟A和B、WUTIE、TAMPIE和TSIE等)
2. 清零对应中断标志位
3. 清除PWR唤醒(WUF)标志(通过设置PWR_CR的CWUF位实现)
4. 重新使能RTC对应中断
5. 进入低功耗模式
1. 使能电源时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //使能PWR外设时钟
2. 设置WK_UP(也就是PA0引脚,KEY_UP按键)引脚作为唤醒源
PWR_WakeUpPinCmd(ENABLE); //使能KEY_UP按键唤醒功能 ,用KEY_UP将CPU从待机模式唤醒
3. 设置SLEERDEEP(深度睡眠)位,设置PDDS位,执行WFI指令,进入待机模式
进入待机模式,首先设置SLEEPDEEP位,接着通过PWR_CR设置PDDS位,使得CPU进入深度睡眠时进入待机模式,最后执行WFI指令开始进入待机模式,并等待WK_UP(KEY_UP)中断的到来;
void PWR_EnterSTANDBYMode(void); //进入待机模式
4. 编写WK_UP中断函数
通过WK_UP中断(PA0中断)来唤醒CPU,同时通过该函数进入待机模式。
该实验程序实现功能:长按3秒KEY_UP按键开机,通过LED0指示灯指示程序开始运行,再次长按按键,进入待机模式,LED0关闭,程序停止运行。类似于手机开关机。
- #include "stm32f4xx.h"
- #include "delay.h"
- #include "usart.h"
- #include "LED.h"
- #include "lcd.h"
- #include "usmart.h"
- #include "Key.h"
- #include "WKUP.h"
-
- //LCD状态设置函数
- void led_set(u8 sta)//只要工程目录下有usmart调试函数,主函数就必须调用这两个函数
- {
- LED1=sta;
- }
- //函数参数调用测试函数
- void test_fun(void(*ledset)(u8),u8 sta)
- {
- led_set(sta);
- }
-
- int main(void)
- {
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统分组中断优先级2
- delay_init(168);
- uart_init(115200);//这里切记先初始化串口,否则无法显示实验现象
- LED_Init();
- LCD_Init();
- WKUP_Init();
- POINT_COLOR=RED;
- LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
- LCD_ShowString(30,70,200,16,16,"WKUP Test");
- LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
- LCD_ShowString(30,110,200,16,16,"2023/20/23");
- LCD_ShowString(30,130,200,16,16,"WK_UP:Standby/WK_UP");
- while(1)
- {
- LED0=!LED0;
- delay_ms(250);
- }
- }
-
-
- #include "stm32f4xx.h"
- #include "WKUP.h"
- #include "Key.h"
- #include "LED.h"
- #include "delay.h"
-
- //系统进入待机模式
- void Sys_Enter_Standby(void)
- {
- while(WKUP_KD);//while循环内设置为空,等待KEY_UP按键松开,跳过while循环
-
- RCC_AHB1PeriphResetCmd(0x04FF,ENABLE);//复位所有IO口
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);//使能PWR电源时钟
-
- PWR_BackupAccessCmd(ENABLE);//使能后备域寄存器,RTC实时时钟中介绍过该寄存器
-
- RTC_ITConfig(RTC_IT_ALRB|RTC_IT_ALRA|RTC_IT_TS|RTC_IT_WUT,DISABLE);//禁止RTC相关中断,这些中断可能在RTC实验中开启了
- RTC_ClearITPendingBit(RTC_IT_ALRB|RTC_IT_ALRA|RTC_IT_TS|RTC_IT_WUT);//清除相关中断标志位
-
- PWR_ClearFlag(PWR_FLAG_WU);//清除Wake_up唤醒标志
-
- PWR_WakeUpPinCmd(ENABLE);//使能KEY_UP按键唤醒功能 ,用KEY_UP将CPU从待机模式唤醒
-
- PWR_EnterSTANDBYMode();//进入待机模式
- }
- //检测WKUP脚的信号
- //返回值1:连续按下3s以上
- // 0:错误的触发
- u8 Check_WKUP(void)
- {
- u8 t=0;//记录时间
- u8 tx=0;//记录松开的次数
- LED0=0;//LED0点亮
- while(1)
- {
- if(WKUP_KD)//KEY_UP按键按下
- {
- t++; //记录KEY_UP按键按下的时间,需要去判断这个时间的长短,就跟我们的手机关机一样,不会只是按一下电源键就关机了,需要按下保持一段时间
- tx=0; //只要进入if循环语句,那么按键就一直被按下,所以松开次数始终都是零
- }
- else //松开KEY_UP
- {
- tx++;//松开的次数++
- if(tx>3)//如果松开的次数大于3,超过90s内没有WKUP信号
- {
- LED0=1; //LED0熄灭
- return 0;//错误按键,返回
- //这里的错误的意思是:还是我们手机关机的例子,假设需要长按电源键5秒关机,如果我们在5秒内频繁的松开按键,就一定不会关机了;直接一点,松开一次按键就不会关机了
- //也就表示用户取消了关机,return 0;
- }
- }
- delay_ms(30);
- if(t>=100)//按下超过3秒钟 表示按键超过了预设的时间,那么进入待机模式
- {
- LED0=0;
- return 1;//按键3s以上了,进入待机模式
- }
- }
- }
- //中断,检测到PA0脚上的一个上升沿
- //中断线0上的中断检测
- void EXTI0_IRQHandler(void)
- {
- EXTI_ClearITPendingBit(EXTI_Line0);//清除LINE0上的中断标志位
- if(Check_WKUP())//表示检测3秒以上的函数返回值为1
- {
- Sys_Enter_Standby();//进入待机模式
- }
- }
- //PA0 KEY_UP 唤醒中断初始化
- void WKUP_Init(void)
- {
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//使能GPIOA时钟
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);//使能SYSCFG时钟
-
- GPIO_InitTypeDef GPIO_InitStructure;
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN;//按键的模式是输入
- GPIO_InitStructure.GPIO_OType=GPIO_OType_OD;
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
- GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_DOWN;//按键KEY_UP下拉
- GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;
- GPIO_Init(GPIOA,&GPIO_InitStructure);
-
- if(Check_WKUP()==0) //也是和我们手机开机一样,手机开机也是需要长按电源键,如果在长按电源键的期间,频繁的松开电源键,此时手机是开不开机的;
- //开发板也是这样,初始化时先检测如果是非正常开机,那么直接进入待机模式
- {
- Sys_Enter_Standby();//不是正常的开机,进入待机模式
- }
- SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);//PA0连接到中断线0
-
- EXTI_InitTypeDef EXTI_InitStructure;
- EXTI_InitStructure.EXTI_Line=EXTI_Line0;
- EXTI_InitStructure.EXTI_LineCmd=ENABLE;
- EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
- EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;//上升沿触发
- EXTI_Init(&EXTI_InitStructure);
-
- NVIC_InitTypeDef NVIC_InitStructure;
- NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;//外部中断0
- NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x02;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x02;
- NVIC_Init(&NVIC_InitStructure);
- }
-
-
- #ifndef _WKUP__H_
- #define _WKUP__H_
-
- #define WKUP_KD PAin(0)
- void Sys_Enter_Standby(void);
- u8 Check_WKUP(void);
- void EXTI0_IRQHandler(void);
- void WKUP_Init(void);
-
- #endif
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。