疑问
在做待机唤醒实验时,会有这样的疑问:只有进入待机模式的代码,那么唤醒是如何唤醒的?
原理
(此节主要针对待机唤醒原理进行简单介绍,可以根据自己情况自动略过)
STM32F4待机模式
- 在系统或电源复位以后,微控制器处于运行状态。运行状态下的 HCLK 为 CPU 提供时钟,内核执行程序代码。当 CPU不需继续运行时,可以利用多个低功耗模式来节省功耗,例如等待某个外部事件时。
- STM32F4 的 3 种低功耗模式
- 在这三种低功耗模式中,最低功耗的是待机模式,在此模式下,最低只需要 2.2uA 左右的 电流。停机模式是次低功耗的,其典型的电流消耗在350uA 左右。最后就是睡眠模式了。
进入/退出待机模式
拓展:
- 在进入模式的第三步:将WUF位清零,查看寄存器PWR_CSR。
清零需要通过PWR_CR进行配置:CWUF位置1。
- 退出模式
- 根据图1及下面stm32部分时钟树可知,在待机的状态下,PLL、HSI 和 HSE 振荡器被断电、1.2V供电区别断电,可以认为只有LSI、LSE振荡器在工作,因此RTC、独立看门狗可以工作,从而进行待机唤醒。(还有WKUP上升沿、NRST复位)
代码
#include "wkup.h"
#include "led.h"
#include "delay.h"
#include "usart.h" //系统进入待机模式
void Sys_Enter_Standby(void)
{ while(WKUP_KD); //等待WK_UP按键松开(在有RTC中断时,必须等WK_UP松开再进入待机)RCC_AHB1PeriphResetCmd(0X04FF,ENABLE);//复位所有IO口RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//使能PWR时钟 PWR_BackupAccessCmd(ENABLE);//后备区域访问使能//这里我们就直接关闭相关RTC中断RTC_ITConfig(RTC_IT_TS|RTC_IT_WUT|RTC_IT_ALRB|RTC_IT_ALRA,DISABLE);//关闭RTC相关中断,可能在RTC实验打开了。RTC_ClearITPendingBit(RTC_IT_TS|RTC_IT_WUT|RTC_IT_ALRB|RTC_IT_ALRA);//清楚RTC相关中断标志位。PWR_ClearFlag(PWR_FLAG_WU);//清除Wake-up 标志PWR_WakeUpPinCmd(ENABLE);//设置WKUP用于唤醒PWR_EnterSTANDBYMode(); //进入待机模式}
//检测WKUP脚的信号
//返回值1:连续按下3s以上
// 0:错误的触发
u8 Check_WKUP(void)
{u8 t=0;u8 tx=0;//记录松开的次数LED0=0; //亮灯DS0 while(1){if(WKUP_KD)//已经按下了{t++;tx=0;}else {tx++; if(tx>3)//超过90ms内没有WKUP信号{LED0=1;return 0;//错误的按键,按下次数不够}}delay_ms(30);if(t>=100)//按下超过3秒钟{LED0=0; //点亮DS0 return 1; //按下3s以上了}}
}
//中断,检测到PA0脚的一个上升沿.
//中断线0线上的中断检测
void EXTI0_IRQHandler(void)
{ EXTI_ClearITPendingBit(EXTI_Line0); // 清除LINE10上的中断标志位if(Check_WKUP())//关机? { Sys_Enter_Standby(); //进入待机模式 }
} //PA0 WKUP唤醒初始化
void WKUP_Init(void)
{ GPIO_InitTypeDef GPIO_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;EXTI_InitTypeDef EXTI_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;//下拉GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);//PA0 连接到中断线0EXTI_InitStructure.EXTI_Line = EXTI_Line0;//LINE0EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能LINE0EXTI_Init(&EXTI_InitStructure);//配置NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//外部中断0NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道NVIC_Init(&NVIC_InitStructure);//配置NVIC//(检查是否是正常开)机 if(Check_WKUP()==0){Sys_Enter_Standby(); //不是开机,进入待机模式 }
}
此处为wkup.c代码,比较简单,这里不再进行阐述,下面主要针对上述疑问进行解释。(通过查阅资料,自行理解的逻辑)
理解
-
首先,开机后,从int main()函数进行初始化,进行到WKUP_Init();对PA0进行相关配置,检查是否长按开机键,没有,进入Sys_Enter_Standby()函数,在函数内最后进入待机模式:PWR_EnterSTANDBYMode()。
-
PWR_EnterSTANDBYMode()配置在原理中已经进行介绍,不再重复,主要针对函数内的WFI进行介绍:
WFI: wait for Interrupt 等待中断,即下一次中断发生前都在此hold住不干活
也就是说:当前大环境是正常运行状态,当运行于此处时,卡住不进行后续代码执行,相当于进行待机处理。
所以在初始化时,运行到WFI就卡在了一个地方 -
当按键wkup被按下时,其实已经进行了待机唤醒处理,(从按下时,灯光亮了一下就可以看出来,程序已经开始运行了,没有继续卡在原处),此时是从int main()函数第一行开始重新运行。进行到WKUP_Init();对PA0进行相关配置,检查是否长按开机键。
-
没有按够3s,重复上述1、2步骤;
-
当按键达到3s后,跳过WKUP_Init()函数中的ifCheck_WKUP()函数,程序正常运行,从而达到唤醒功能。并配置PA0中断方式。
-
当再次按键达到3s后,进入中断,判断有没有按够3s。时间不够,继续运行正常状态;时间达到3s,Sys_Enter_Standby()
再次进入待机模式。
void EXTI0_IRQHandler(void)
{ EXTI_ClearITPendingBit(EXTI_Line0); // 清除LINE10上的中断标志位if(Check_WKUP())//关机? { Sys_Enter_Standby(); //进入待机模式 }
}