STM32F4待机唤醒机制详解

目录

1. 低功耗模式

1.1 降低系统时钟速度

1.2 外设时钟门控

2. 睡眠模式

2.1 进入睡眠模式

2.2 退出休眠模式

3. 停止模式

3.1 进入停止模式

3.2 退出停止模式

4. 待机模式

4.1 进入待机模式

4.2 退出待机模式

4.3 电源控制寄存器:PWR_CR

4.4 电源控制/状态寄存器:PWR_CSR

5. 库函数配置进入待机模式

6. 实验程序


        在学习STM32待机唤醒功能之前,我们首先来系统的学习32单片机的低功耗模式,这将对于我们理解睡眠/停止/待机模式有很大的帮助。

1. 低功耗模式

        默认情况下,系统复位或者上电复位后,MCU微控制器进入运行模式。在运行模式下,CPU通过HCLK提供时钟,并且执行程序代码。系统提供了多个低功耗模式,可在CPU不需要运行时(例如等待外部事件时)节省功耗。由用户根据应用选择具体的低功耗模式,以在低功耗、短启动时间和可用唤醒源之间寻求最佳平衡。

STM32有三种低功耗模式

  • 睡眠模式(Cortex -M4F内核停止,外设保持运行)
  • 停止模式(所有时钟都停止)
  • 待机模式(1.2V域断电)
  • 此外,还可以通过其他方式降低功耗:

            1. 降低系统时钟速度

            2. 不使用APBx和AHBx外设时,将对应的外设时钟关闭

    其中:嵌入式线性调压器为备份域和待机电路以外的所有数字电路供电。调压器输出电压约为1.2V。

    1.1 降低系统时钟速度

            在运行模式下,可以通过对预分频寄存器编程来降低系统时钟(SYSCLK、HCLK、PCLK1和PCLK2)速度。进入睡眠模式之前,也可以使用这些预分频器降低外设速度。

    1.2 外设时钟门控

            在运行模式下,可以随时的停止各个外设和存储器的HCLKx和PCLKx以降低功耗。要进一步的降低睡眠模式的功耗,可在执行WFI或者WFE指令之前禁止外设时钟。

            外设时钟门控由AHB1外设时钟使能寄存器RCC_AHB1ENR、AHB2外设时钟使能寄存器RCC_AHB2ENR和AHB3外设时钟使能寄存器RCC_AHB3ENR进行控制。

            在睡眠模式下,复位RCC_AHBxLPENR和RCC_APBxLPENR寄存器中的对应位可以自动禁止外设时钟。

    2. 睡眠模式

    2.1 进入睡眠模式

            执行WFI(等待中断)或者WFE(等待事件)指令即可进入睡眠模式。根据M4F内核系统控制寄存器中的SLEEPONEXIT位的设置,可以通过两种方案选择睡眠模式进入机制。

    立即休眠:如果SLEEPONEXIT位清零,MCU将在执行WFI和WFE指令时立即进入睡眠模式。

    退出时休眠:如果SLEEPONEXIT位置1,MCU将在退出优先级最低的ISR时立即进入睡眠模式。(ISR的全称是Interrupt Service Routines,也就是中断服务,中断是有NVIC中断优先级的,这里的意思就是当执行完中断中优先级最低的中断以后进入休眠)

    2.2 退出休眠模式

    因为进入睡眠模式是由指令WFI和指令WFE控制的。所以退出休眠模式也应该从这两方面入手。

    如果使用WFI指令进入睡眠模式,则通过中断控制器NVIC确认的任意外设中断都会将器件从睡眠模式唤醒。

    如果使用WFE指令进入睡眠模式,MCU将在有事件发生时立即退出睡眠模式。

    唤醒事件可以通过以下方式产生:

            在外设的控制寄存器使能一个中断,但不在NVIC中使能,同时使能M4F内核系统控制寄存器中的SEVONPEND位。当MCU从WFE恢复时,需要清除相应外设的中断挂起位和外设NVIC中断通道挂起位(在NVIC中断清除挂起寄存器中)。

            配置一个外部或者内部EXTI线为事件模式。当CPU从WFE恢复时,因为对应事件线的挂起位没有被置位,不必清除相应外设的中断挂起位或NVIC中断通道挂起位。

    3. 停止模式

            停止模式基于M4内核深度睡眠模式与外设时钟门控。调压器既可以配置为正常模式,也可以配置为低功耗模式。在停止模式下,1.2V域中的所有时钟都会停止,PLL、HSI和HSE RC振荡器也会被禁止。内部SRAM和寄存器内容将会被保留。

            将PWR_CR电源控制寄存器中的FPDS位置1后,Flash闪存还会在器件进入停止模式时进入掉电状态。Flash处于掉电模式时,将器件从停止模式唤醒将需要额外的启动延时。

    3.1 进入停止模式

    要进一步降低停止模式的功耗,可将内部调压器设置为低功耗模式。通过对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位都清零。

    3.2 退出停止模式

    通过发出中断或者唤醒事件退出停止模式时,将选择HSI RC振荡器作为系统时钟。

    4. 待机模式

            待机模式下可达到最低功耗。待机模式基于M4内核深度睡眠模式,该模式在深度睡眠模式时关闭电压调节器。因此1.2V域断电。PLL、HSI振荡器也将关闭。除备份域(RTC寄存器、RTC备份寄存器和备份SRAM)和待机电路中的寄存器外,SRAM和寄存器内容都将丢失。

    4.1 进入待机模式

    在待机模式下,可以通过对各控制位进行编程来选择以下功能:

    ●独立的看门狗 (IWDG):IWDG 通过写入其密钥寄存器或使用硬件选项来启动。而且一 旦启动便无法停止,除非复位。

    ●实时时钟 (RTC):通过备份域控制寄存器 (RCC_BDCR) 中的 RTCEN 位进行配置。

    ●内部 RC 振荡器 (LSI RC):通过控制/状态寄存器 (RCC_CSR) 中的 LSION 位进行配置。

    ●外部 32.768 kHz 振荡器 (LSE OSC):通过备份域控制寄存器 (RCC_BDCR) 中的 LSEON 位进行配置。

    4.2 退出待机模式

            检测到外部复位(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左右。最后就是睡眠模式了。

    4.3 电源控制寄存器:PWR_CR

    通过对进入待机模式的学习,我们已经清楚了需要将电源控制寄存器PWR_CR的 位1 和 位2 置1,使器件在CPU进入深度睡眠时进入待机模式,并且将WUF唤醒标志清零。

    4.4 电源控制/状态寄存器:PWR_CSR

    这里我们需要 位8 置1,从待机模式中唤醒器件;也可以通过位0来检测是否收到了唤醒标志。

    5. 库函数配置进入待机模式

    首先注意:对于已经使能了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,同时通过该函数进入待机模式。

    6. 实验程序

            该实验程序实现功能:长按3秒KEY_UP按键开机,通过LED0指示灯指示程序开始运行,再次长按按键,进入待机模式,LED0关闭,程序停止运行。类似于手机开关机。

    6.1 main.c

    #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);
    	}
    }
    
    
    

    6.2 WKUP.c

    #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);
    }
    
    
    

    6.3 WKUP.h

    #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
    
    

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32F4待机唤醒机制详解

    发表评论