
实现过程
需要用到的资源包括PWR和EXTI这两个
首先就是开启对应的PWR时钟 EXTI时钟,引脚的外设时钟,中断向量控制表
实现代码
进入待机的方法一共有3中的休眠方式:睡眠模式、停机模式、待机模式;节能的效果是待机模式最好,其次是停机模式,最次是睡眠模式

本次采用的休眠方式,就是停机模式,然后采用外部中断进行唤醒
进入休眠的代码:
RCC_APB1PeriphClockCmd( RCC_APB1Periph_PWR, ENABLE ); //要开启对应的电源控制时钟 // PWR_WakeUpPinCmd( ENABLE ); //这个是使用wakeUP引脚的时候需要使用的 // PWR_EnterSTANDBYMode(); //进入待机模式 // __WFE; //调用ARM内核指令,进入睡眠模式,并且以事件唤醒 PWR_EnterSTOPMode( PWR_Regulator_LowPower, PWR_STOPEntry_WFI ); //进入停机模式
///设置一个外部中断,对应的引脚是 PA11 void PA11_EXTI_Config(){ GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitSrurcture; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( GPIOA, &GPIO_InitStructure); GPIO_EXTILineConfig( GPIO_PortSourceGPIOA, GPIO_PinSource11 ); //这个连接不要忘记了,选择那个引脚作为外部中断线 EXTI_InitStructure.EXTI_Line = EXTI_Line11; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init( &EXTI_InitStructure ); NVIC_InitSrurcture.NVIC_IRQChannel = EXTI15_10_IRQn; NVIC_InitSrurcture.NVIC_IRQChannelPreemptionPriority =0; NVIC_InitSrurcture.NVIC_IRQChannelSubPriority = 1; NVIC_InitSrurcture.NVIC_IRQChannelCmd = ENABLE; NVIC_Init( &NVIC_InitSrurcture ); }///RTC相关初始化:利用这个是的在定时器时间到来的时候,进入休眠 void Wake_Up_RTC_Config(){ RCC_APB1PeriphClockCmd( RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE ); //使能PWR时钟 PWR_BackupAccessCmd( ENABLE ); BKP_DeInit(); RCC_LSICmd( ENABLE ); //使能内部时钟 while( RCC_GetFlagStatus( RCC_FLAG_LSIRDY ) != SET ){ //判断时候开启了内部时钟 } printf("内部低速时钟开启成功\r\n"); RCC_RTCCLKConfig( RCC_RTCCLKSource_LSI ); //使用内部低速时钟 RCC_RTCCLKCmd( ENABLE ); //使能RTC时钟 RTC_WaitForLastTask(); //等待上一次的寄存器操作 RTC_WaitForSynchro(); //等待寄存器同步 RTC_ITConfig( RTC_IT_SEC | RTC_IT_ALR, ENABLE ); //使能秒中断 和警报中断 RTC_WaitForLastTask(); RTC_EnterConfigMode(); RTC_SetPrescaler(40000); //设置分频数 RTC_WaitForLastTask(); RTC_SetCounter(1); //设置起始数值为0 RTC_WaitForLastTask(); RTC_SetAlarm(10); //设置闹钟时间为10s RTC_WaitForLastTask(); RTC_ExitConfigMode(); Wake_Up_RTC_NVIC_Config(); } void Wake_Up_RTC_NVIC_Config(){ NVIC_InitTypeDef RTC_InitStructure; RTC_InitStructure.NVIC_IRQChannel = RTC_IRQn; RTC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; RTC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_Init( &RTC_InitStructure ); }///使用RTC时钟进行进入停机模式,也就是在RTC中断中实现的 void RTC_IRQHandler(){ if( RTC_GetITStatus( RTC_IT_ALR ) == SET ){ //如果RTC闹钟被置位了 wake_flag = 1; //在函数中判断,如果为1 就在主函数中调用 进入休眠的代码 RTC_ClearITPendingBit( RTC_IT_ALR ); //清除标志位 } if( RTC_GetITStatus( RTC_IT_SEC ) == SET ){ //如果秒中断被置位了 printf("当前时间:%d s\r\n", RTC_GetCounter()); //打印出当前的秒数 RTC_ClearITPendingBit( RTC_IT_SEC ); //清除标志位 } RTC_WaitForLastTask(); }//由于,在停机模式下,只要是受到外部中断,就会退出停机状态 void EXTI15_10_IRQHandler(){ if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_11) == 0 ){ GPIO_SetBits(GPIOB,GPIO_Pin_0); GPIO_ResetBits(GPIOB,GPIO_Pin_1); printf("按下K1\r\n"); } //清除标志位 EXTI_ClearITPendingBit(EXTI_Line11|EXTI_Line12); }//在主函数的死循环中,添加这个 if( wake_flag == 1 ){ PWR_Enter_STANDBYMode(); wake_flag = 0; SystemInit(); //系统时间需要重新进行初始化 SysTick_init(); //这个滴答定时器,也是要重新初始化 usart1_config(9600,1);//串口1波特率9600接收中断,这个串口在这里要重新配置的 }注意事项
唤醒的中断(外部中断)的优先级,最好是要高于进入休眠的的中断,而且最好不要在中断中进入休眠,可以用一个标志位进行判断,在中断中进入容易卡住
STM32的工作电压(VDD)为2.0~3.6V。通过内置的电压调节器提供所需的1.8V电源。当主电源VDD掉电后,通过VBAT脚为实时时钟(RTC)和备份寄存器提供电源。

可以看出VDD为STM32提供供电,通过内置的电压调节器提供所需的1.8V电源,1.8V供电区域为CPU核心存储器和内置数字外设。
VBAT是后备供电也就是通过后备电池来供电的。其供电区域为LSE 32K晶体振荡器、后备寄存器RCC BDCR和寄存器RTC。也就是说内部的晶振是停止掉了
最重要的一点就是,在唤醒之后,(休眠的时候,会保护现场?)一定要记得重新对系统时钟进行初始化:SystemInit(); //系统时间需要重新进行初始化,官方库函数
重复进入休眠状态,会取反。也就是当下是休眠的,再进入一次休眠程序,就会退出休眠状态
#好好学习!