17:低功耗篇(PWR)---HAL库

一:PWR

1:简历

PWR(Power Control)电源控制

PWR负责管理STM32内部的电源供电部分,可以实现可编程电压监测器和低功耗模式的功能

可编程电压监测器(PVD)可以监控VDD电源电压,当VDD下降到PVD阀值以下或上升到PVD阀值之上时,PVD会触发中断,用于执行紧急关闭任务

低功耗模式包括睡眠模式(Sleep)、停机模式(Stop)和待机模式(Standby),可在系统空闲时,降低STM32的功耗,延长设备使用时间

2:电源框图

VDD : 右边部分是VDD通过电压调节器,降压到1.8V ; 电压调节器,它的作用是给1.8V区域供电

3:低功耗模式

HSL和HSE : HSI内部高速时钟和HSE外部高速时钟

关闭电路通常有两个做法 : 一个是关闭时钟,另一个是关闭电源

关闭时钟-------所有的运算和涉及时序的操作都会暂停, 但是寄存器和存储器里面保存的数据还可以维持,不会消失

**关闭电源--------**就是电路直接断电 , 电路的操作和数据都会直接丢失 . 所以关闭电源,比关闭时钟更省电

电压调节器: 电压调节器是把VDD的电压降低到了1.8V, 然后电压调节器给1.8V区域的供电 ; 电压调节器相当于1.8V供电区域的电池; 关闭电压调节器 , 1.8V供电区域的都不能使用;

睡眠 : WFI--------任何外设发生任何中断时,芯片都会立刻醒来 , 因为中断发生了,所以醒来之后的第一件事一般就是处理中断函数,处理完中断, 直接从睡的地方继续运行

WFE------等待事件 , 对应的唤醒条件是: 唤醒事件 , 这个事件可以是外部中断配置为事件模式 ; 也可以是使能到中断,但是没有配置NVIC , 调用WFE进入的睡眠模式 ,产生唤醒事件时,会立刻醒来 , 醒来之后,一般不需要进中断函数 , 直接从睡的地方继续运行

睡眠模式只是把1.8V区域的CPU时钟关闭

睡眠模块: WFI模式进入的只能使用中断唤醒,

WFE进入的可以使用中断唤醒,也可以使用事件唤醒。

中断的优先级> 事件的优先级

待机模式指定唤醒方式:

事件和中断:

简单理解

4:模式选择

5:低功耗模式注意事项

A:睡眠模式

执行完WFI/WFE指令后,STM32进入睡眠模式,程序暂停运行,唤醒后程序从暂停的地方继续运行

SLEEPONEXIT位决定STM32执行完WFI或WFE后,是立刻进入睡眠,还是等STM32从最低优先级的中断处理程序中退出时进入睡眠

在睡眠模式下,所有的I/O引脚都保持它们在运行模式时的状态

WFI指令进入睡眠模式,可被任意一个NVIC响应的中断唤醒(外部和内部的中断都ok)

WFE指令进入睡眠模式,可被唤醒事件唤醒

B:停止模式

执行完WFI/WFE指令后,STM32进入停止模式,程序暂停运行,唤醒后程序从暂停的地方继续运行

HSL和HSE : HSI内部高速时钟和HSE外部高速时钟

1.8V供电区域的所有时钟都被停止,PLL、HSI和HSE被禁止,SRAM和寄存器内容被保留下来

在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态

当一个中断或唤醒事件导致退出停止模式时,HSI被选为系统时钟 -------- 你程序刚上电,是72MHz的主频 , 但是进入停止模式,再唤醒之后 , 就变成8MHz的主频了; 所以,我们一般在停止模式唤醒后 , 第一时间就是重新启动HSE,配置主频为72MHz , 我们只需要再调用一下Systemlnit就行

当电压调节器处于低功耗模式下,系统从停止模式退出时,会有一段额外的启动延时

WFI指令进入停止模式,可被任意一个EXTI外部中断唤醒(内部中断不能)

WFE指令进入停止模式,可被任意一个EXTI外部事件唤醒 或者外部中断唤醒 (内部事件不能,内部中断也不可以) 中断的优先级> 事件的优先级

C:待机模式

执行完WFI/WFE指令后,STM32进入待机模式,唤醒后程序从头开始运行

整个1.8V供电区域被断电,PLL、HSI和HSE也被断电,SRAM和寄存器内容丢失,只有备份的寄存器和待机电路维持供电

在待机模式下,所有的I/O引脚变为高阻态(浮空输入)

WKUP引脚的上升沿、RTC闹钟事件的上升沿、NRST引脚上外部复位、IWDG复位退出待机模式

它并不会主动关闭LSI和LSE两个低速时钟 , 因为这两个时钟还要维持RTC和独立看门狗的运行

二 : 案例

A:睡眠模式--WFI

睡眠模式 SLEEP-NOW 以 WF进入,外部中断唤醒

cs 复制代码
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "UART.h"
#include "delay.h"
#include "OLED.h"
#include "wwdg.h"
#include "RTC.h"
#include <time.h>
#include <stdarg.h>
#include "stdio.h"
#include "key.h"


int main(void)
{
	

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
	Uart_Init(115200);
	RTC_Init();
	KEY_Init();

	
		while(1)
		{
			printf("5\r\n");
			HAL_Delay(1000);
			printf("4\r\n");
			HAL_Delay(1000);
			printf("3\r\n");
			HAL_Delay(1000);
			
			
			/*睡眠模式有2个:SLEEP-NOW  SLEEP-ON-EXIT 
			关闭了SLEEP-ON-EXIT那就是打开了SLEEP-NOW  */
			HAL_PWR_DisableSleepOnExit();
			SysTick->CTRL  &=~ SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk;                         
                                             
			HAL_PWR_EnterSLEEPMode(0,PWR_SLEEPENTRY_WFI);  /*以WFI进入的睡眠模式*/
			
			
			printf("2\r\n");
			HAL_Delay(1000);
			printf("1\r\n");
			HAL_Delay(1000);
		
			
		}  
}

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "delay.h"
#include <stdarg.h>
#include "stdio.h"
#include "UART.h"
void KEY_Init(void)
{
		__HAL_RCC_GPIOB_CLK_ENABLE() ;
		
			
		GPIO_InitType.Mode=GPIO_MODE_IT_FALLING;
		GPIO_InitType.Pin=GPIO_PIN_0;
		GPIO_InitType.Pull=GPIO_PULLUP; //上拉
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
	
		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 	

		HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
		HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);
		HAL_NVIC_EnableIRQ(EXTI0_IRQn);
	

}	





void EXTI0_IRQHandler()
{
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}



void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin==GPIO_PIN_0)
	{
			printf("唤醒\r\n");
			SysTick->CTRL  |=SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk; 
	}

}

睡眠模式SLEEP-NOW 以WFI 进入 只能使用中断唤醒;不能使用事件唤醒。

在HAL_Init(); 函数中

cpp 复制代码
   Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) 
  HAL_InitTick(TICK_INT_PRIORITY);

systick配置位1MS的中断,做为HAL库的时基参数。每1m产生1个HAL_IncTick的中断,会唤醒我们的睡眠模式。

cpp 复制代码
/*-------------------------------------------------*/
/*函数名:SysTic系统嘀嗒定时器处理函数             */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void SysTick_Handler(void)
{  
	HAL_IncTick();	
}

我们需要:

cpp 复制代码
在唤醒之前吧HAL_IncTick中断关闭
关闭:将相应的二进制位变为0
&=~  :原来为1变为0  ;  原来是0还是0
SysTick->CTRL  &=~ SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk; 


醒来只后在打开
|=置1
SysTick->CTRL  |=SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk; 

B:睡眠模式--WFI

睡眠模式 SLEEP-NOW 以WFI 进入。内部中断: 定时器2中断唤醒

cpp 复制代码
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "UART.h"
#include "delay.h"
#include "OLED.h"
#include "wwdg.h"
#include "RTC.h"
#include <time.h>
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "IC_encoder.h"

int main(void)
{
	
	

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
	Uart_Init(115200);
	RTC_Init();
	KEY_Init();




	
		while(1)
		{
			printf("5\r\n");
			HAL_Delay(1000);
			printf("4\r\n");
			HAL_Delay(1000);
			printf("3\r\n");
			HAL_Delay(1000);
			//内部中断:定时器唤醒
			IC_encoder_Init(20000-1,7200-1);
			/*睡眠模式有2个:SLEEP-NOW  SLEEP-ON-EXIT 
			关闭了SLEEP-ON-EXIT那就是打开了SLEEP-NOW  */
			HAL_PWR_DisableSleepOnExit();
			SysTick->CTRL  &=~ SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk;                         
                                             
			HAL_PWR_EnterSLEEPMode(0,PWR_SLEEPENTRY_WFI);  /*以WFI进入的睡眠模式*/
			
			
			printf("2\r\n");
			HAL_Delay(1000);
			printf("1\r\n");
			HAL_Delay(1000);
		
		
	
			
		}  
}

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
TIM_HandleTypeDef PA0TIM2CH1Handleinit;

void IC_encoder_Init(uint16_t arr,uint16_t psc)
{
	
	PA0TIM2CH1Handleinit.Instance=TIM2;
	PA0TIM2CH1Handleinit.Init.Period=arr;   //ARR自动重装载值
	PA0TIM2CH1Handleinit.Init.Prescaler=psc;    //预分频系数
	PA0TIM2CH1Handleinit.Init.CounterMode=TIM_COUNTERMODE_UP;  //向上计数	
	HAL_TIM_Base_Init(&PA0TIM2CH1Handleinit);
	__HAL_TIM_CLEAR_FLAG(&PA0TIM2CH1Handleinit,TIM_FLAG_UPDATE);
	//以中断的方式打开定时器
	HAL_TIM_Base_Start_IT(&PA0TIM2CH1Handleinit);

}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM2)
	{

		__HAL_RCC_TIM2_CLK_ENABLE();
	
		HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
		HAL_NVIC_SetPriority(TIM2_IRQn ,2,0);
		HAL_NVIC_EnableIRQ(TIM2_IRQn);
		
	}
}


void TIM2_IRQHandler()
{
	HAL_TIM_IRQHandler(&PA0TIM2CH1Handleinit);	
}


//更新中断的回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{	
		if(htim->Instance==TIM2)
		{		
				//在唤醒睡眠模式后,关闭定时器;否则时间一到,会调用这个回调
				HAL_TIM_Base_Stop_IT(htim);
				SysTick->CTRL  |=SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk; 
				printf("定时器唤醒睡眠模式\r\n");
				
		}
}

本次中断唤醒,我们使用内部的中断:定时器

C:睡眠模式-----WFE

睡眠模式 SLEEP-NOW 以 WFE进入, 外部事件唤醒

WFE的事件唤醒,中断也可以唤醒。 中断的优先级>事件的优先级。

cpp 复制代码
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "UART.h"
#include "delay.h"
#include "OLED.h"
#include "wwdg.h"
#include "RTC.h"
#include <time.h>
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "IC_encoder.h"

int main(void)
{
	
	

  
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
	Uart_Init(115200);
	RTC_Init();
	KEY_Init();


		while(1)
		{
			printf("5\r\n");
			HAL_Delay(1000);
			printf("4\r\n");
			HAL_Delay(1000);
			printf("3\r\n");
			HAL_Delay(1000);

			/*睡眠模式有2个:SLEEP-NOW  SLEEP-ON-EXIT 
			关闭了SLEEP-ON-EXIT那就是打开了SLEEP-NOW  */
			HAL_PWR_DisableSleepOnExit();
			SysTick->CTRL  &=~ SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk;                         
                                             
			HAL_PWR_EnterSLEEPMode(0,PWR_SLEEPENTRY_WFE);  /*以WFI进入的睡眠模式*/
			SysTick->CTRL  |=SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk; 
			
			printf("2\r\n");
			HAL_Delay(1000);
			printf("1\r\n");
			HAL_Delay(1000);		
		}  
}




#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "delay.h"
#include <stdarg.h>
#include "stdio.h"
#include "UART.h"
void KEY_Init(void)
{
		__HAL_RCC_GPIOB_CLK_ENABLE() ;
		GPIO_InitTypeDef GPIO_InitType;
		GPIO_InitType.Mode=GPIO_MODE_EVT_FALLING;
		GPIO_InitType.Pin=GPIO_PIN_0;
		GPIO_InitType.Pull=GPIO_PULLUP; //上拉
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 		
}	

D:睡眠模式-----WFE

睡眠模式 SLEEP-NOW 以 WFE进入, 内部事件唤醒

WFE的事件唤醒,中断也可以唤醒。 中断的优先级>事件的优先级。

内部事件 比 外部事件比较麻烦。

cpp 复制代码
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "UART.h"
#include "delay.h"
#include "OLED.h"
#include "wwdg.h"
#include "RTC.h"
#include <time.h>
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "IC_encoder.h"

int main(void)
{
	
	

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
	Uart_Init(115200);
	RTC_Init();
	KEY_Init();




	
		while(1)
		{
			printf("5\r\n");
			HAL_Delay(1000);
			printf("4\r\n");
			HAL_Delay(1000);
			printf("3\r\n");
			HAL_Delay(1000);
		
			IC_encoder_Init(20000-1,7200);
			HAL_PWR_EnableSEVOnPend();   //使能SEV
			/*睡眠模式有2个:SLEEP-NOW  SLEEP-ON-EXIT 
			关闭了SLEEP-ON-EXIT那就是打开了SLEEP-NOW  */
			HAL_PWR_DisableSleepOnExit();
			SysTick->CTRL  &=~ SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk;                         
                                             
			HAL_PWR_EnterSLEEPMode(0,PWR_SLEEPENTRY_WFE);  /*以WFI进入的睡眠模式*/
			SysTick->CTRL  |=SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk; 
			
			__HAL_TIM_CLEAR_FLAG(&PA0TIM2CH1Handleinit,TIM_FLAG_UPDATE); //清除中断标志位
			NVIC_ClearPendingIRQ(TIM2_IRQn);   //清除NVIC中相应的优先级
			HAL_TIM_Base_Stop_IT(&PA0TIM2CH1Handleinit);
			
			printf("2\r\n");
			HAL_Delay(1000);
			printf("1\r\n");
			HAL_Delay(1000);		
		}  
}


#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
TIM_HandleTypeDef PA0TIM2CH1Handleinit;

void IC_encoder_Init(uint16_t arr,uint16_t psc)
{
	
	PA0TIM2CH1Handleinit.Instance=TIM2;
	PA0TIM2CH1Handleinit.Init.Period=arr;   //ARR自动重装载值
	PA0TIM2CH1Handleinit.Init.Prescaler=psc;    //预分频系数
	PA0TIM2CH1Handleinit.Init.CounterMode=TIM_COUNTERMODE_UP;  //向上计数	
	HAL_TIM_Base_Init(&PA0TIM2CH1Handleinit);
	__HAL_TIM_CLEAR_FLAG(&PA0TIM2CH1Handleinit,TIM_FLAG_UPDATE);
	//以中断的方式打开定时器
	HAL_TIM_Base_Start_IT(&PA0TIM2CH1Handleinit);

}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM2)
	{

		__HAL_RCC_TIM2_CLK_ENABLE();
//	
//		HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
//		HAL_NVIC_SetPriority(TIM2_IRQn ,2,0);
//		HAL_NVIC_EnableIRQ(TIM2_IRQn);
		
	}
}

内部事件的配置:

首先在外设的控制寄存器使能一个中断,但不在NVIC中使能

cpp 复制代码
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
TIM_HandleTypeDef PA0TIM2CH1Handleinit;

void IC_encoder_Init(uint16_t arr,uint16_t psc)
{
	
	PA0TIM2CH1Handleinit.Instance=TIM2;
	PA0TIM2CH1Handleinit.Init.Period=arr;   //ARR自动重装载值
	PA0TIM2CH1Handleinit.Init.Prescaler=psc;    //预分频系数
	PA0TIM2CH1Handleinit.Init.CounterMode=TIM_COUNTERMODE_UP;  //向上计数	
	HAL_TIM_Base_Init(&PA0TIM2CH1Handleinit);
	__HAL_TIM_CLEAR_FLAG(&PA0TIM2CH1Handleinit,TIM_FLAG_UPDATE);
	//以中断的方式打开定时器
	HAL_TIM_Base_Start_IT(&PA0TIM2CH1Handleinit);

}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM2)
	{

		__HAL_RCC_TIM2_CLK_ENABLE();
    }
}

同时在Cortex-M3的系统控制寄存器中使能SEVONPEND位

cpp 复制代码
		HAL_PWR_EnableSEVOnPend();   使能SEV

当CPU从WFE恢复后,需要清除相应外设的中断挂起位和外设NVIC中断通道挂起位(在NVIC中断清除挂起寄存器中)。

cpp 复制代码
	__HAL_TIM_CLEAR_FLAG(&PA0TIM2CH1Handleinit,TIM_FLAG_UPDATE); //清除中断标志位
	NVIC_ClearPendingIRQ(TIM2_IRQn);   //清除NVIC中相应的优先级

我们使用的是定时器2的更新中断做为他的内部事件,清除相应的标志位

E:睡眠模式

睡眠模式 SLEEP-ON-EXIT 全中断模式唤醒

cpp 复制代码
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "UART.h"
#include "delay.h"
#include "OLED.h"
#include "wwdg.h"
#include "RTC.h"
#include <time.h>
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "IC_encoder.h"

int main(void)
{
	
	

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
	Uart_Init(115200);
	RTC_Init();
	KEY_Init();
	HAL_PWR_EnableSleepOnExit();
	IC_encoder_Init(20000-1,7200);


	
		while(1)
		{

			
	
		}  
}

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
TIM_HandleTypeDef PA0TIM2CH1Handleinit;

void IC_encoder_Init(uint16_t arr,uint16_t psc)
{
	
	PA0TIM2CH1Handleinit.Instance=TIM2;
	PA0TIM2CH1Handleinit.Init.Period=arr;   //ARR自动重装载值
	PA0TIM2CH1Handleinit.Init.Prescaler=psc;    //预分频系数
	PA0TIM2CH1Handleinit.Init.CounterMode=TIM_COUNTERMODE_UP;  //向上计数	
	HAL_TIM_Base_Init(&PA0TIM2CH1Handleinit);
	__HAL_TIM_CLEAR_FLAG(&PA0TIM2CH1Handleinit,TIM_FLAG_UPDATE);
	//以中断的方式打开定时器
	HAL_TIM_Base_Start_IT(&PA0TIM2CH1Handleinit);

}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM2)
	{

		__HAL_RCC_TIM2_CLK_ENABLE();
	
		
		HAL_NVIC_SetPriority(TIM2_IRQn ,2,0);
		HAL_NVIC_EnableIRQ(TIM2_IRQn);
		
	}
}


void TIM2_IRQHandler()
{
	HAL_TIM_IRQHandler(&PA0TIM2CH1Handleinit);	
}


//更新中断的回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{	
		if(htim->Instance==TIM2)
		{		
				//在唤醒睡眠模式后,关闭定时器;否则时间一到,会调用这个回调
				
				SysTick->CTRL  |=SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk; 
				printf("在这个中断中做事情,这个是定时器产生的\r\n");
				
		}
}



/*-------------------------------------------------*/
/*函数名:SysTic系统嘀嗒定时器处理函数             */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void SysTick_Handler(void)
{  
	HAL_IncTick();	
	printf("睡眠\r\n");
	SysTick->CTRL  &=~ SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk;    
}

使用我们的SLEEP-ON-EXIT ,主While()不能有任务, 所有的任务全部是靠中断处理的。

关于HAL_Init():

在我们的 HAL_Init(),中把我们的分组分为:4

也就是说我们的NVIC抢占式优先级位:4位,也就是是 0-15

不明白的参考:CSDNhttps://mp.csdn.net/mp_blog/creation/editor/136486395

系统滴答定时器的优先级最低位0x0f=15; 在统从最低优先级的中断处理程序中退出时,微控制器就立即进入睡眠模式。

系统滴答定时器

在我们的 HAL_Init(),中把我们的分组分为:4

也就是说我们的NVIC抢占式优先级位:4位,也就是是 0-15

如果使用的正点原子的Delay.c,那么可以不要下面代码的操作。因为他们在delay_init中把

系统滴答定时器的中断关闭了,没有中断信号,也就不会唤醒我们的睡眠模式。

不使用正点原子的Delay.c,必须坐下面的操作,系统滴答定时器中断信号会唤醒我们的睡眠模式

cpp 复制代码
在唤醒之前吧HAL_IncTick中断关闭
关闭:将相应的二进制位变为0
&=~  :原来为1变为0  ;  原来是0还是0
SysTick->CTRL  &=~ SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk; 


醒来只后在打开
|=置1
SysTick->CTRL  |=SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk; 

下面为:void delay_init(uint16_t sysclk)

cpp 复制代码
/**
 * @brief       初始化延迟函数
 * @param       sysclk: 系统时钟频率, 即CPU频率(HCLK)
 * @retval      无
 */
void delay_init(uint16_t sysclk)
{
#if SYS_SUPPORT_OS /* 如果需要支持OS. */
    uint32_t reload;
#endif
    SysTick->CTRL = 0;       清Systick状态,以便下一步重设,如果这里开了中断会关闭其中断 
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8);   /* SYSTICK使用内核时钟源8分频,因systick的计数器最大值只有2^24 */

    g_fac_us = sysclk / 8;                                      /* 不论是否使用OS,g_fac_us都需要使用,作为1us的基础时基 */
#if SYS_SUPPORT_OS                                              /* 如果需要支持OS. */
    reload = sysclk / 8;                                        /* 每秒钟的计数次数 单位为M */
    reload *= 1000000 / delay_ostickspersec;                    /* 根据delay_ostickspersec设定溢出时间
                                                                 * reload为24位寄存器,最大值:16777216,在9M下,约合1.86s左右
                                                                 */
    g_fac_ms = 1000 / delay_ostickspersec;                      /* 代表OS可以延时的最少单位 */
    SysTick->CTRL |= 1 << 1;                                    /* 开启SYSTICK中断 */
    SysTick->LOAD = reload;                                     /* 每1/delay_ostickspersec秒中断一次 */
    SysTick->CTRL |= 1 << 0;                                    /* 开启SYSTICK */
#endif
}

F:停机模式

停机模式 以WFI进入

cpp 复制代码
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "UART.h"

#include "OLED.h"
#include "wwdg.h"
#include "RTC.h"
#include <time.h>
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "IC_encoder.h"

int main(void)
{
	
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */       
	Uart_Init(115200);
	KEY_Init();




	
		while(1)
		{
			printf("5\r\n");
			HAL_Delay(1000);
			printf("4\r\n");
			HAL_Delay(1000);
			printf("3\r\n");
			HAL_Delay(1000);
		

			SysTick->CTRL  &=~ SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk;                         
                                             
			HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON,PWR_STOPENTRY_WFI);  /*以WFI进入的睡眠模式*/
		

			printf("2\r\n");
			HAL_Delay(1000);
			printf("1\r\n");
			HAL_Delay(1000);		
		}  
}

#include "stm32f1xx_hal.h"
#include "rcc.h"

#include <stdarg.h>
#include "stdio.h"
#include "UART.h"
void KEY_Init(void)
{
		__HAL_RCC_GPIOB_CLK_ENABLE() ;
		
		GPIO_InitTypeDef GPIO_InitType;

	
		GPIO_InitType.Mode=GPIO_MODE_IT_FALLING;
		GPIO_InitType.Pin=GPIO_PIN_0;
		GPIO_InitType.Pull=GPIO_PULLUP; //上拉
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
	
		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 	

		HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);
		HAL_NVIC_EnableIRQ(EXTI0_IRQn);
	

}	




void EXTI0_IRQHandler()
{
	sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}



void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin==GPIO_PIN_0)
	{
			printf("唤醒\r\n");
			SysTick->CTRL  |=SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk; 
	}

}

停机模式以WFI进入的,只有外部中断才可以唤醒 (其他任何方式都不可以)

停机模式会关闭我们的时钟,我们需要在外部中断中在打开一次。

G:停机模式

停机模式 以WFE进入

cpp 复制代码
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "UART.h"

#include "OLED.h"
#include "wwdg.h"
#include "RTC.h"
#include <time.h>
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "IC_encoder.h"

int main(void)
{
	
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */       
	Uart_Init(115200);
	KEY_Init();




	
		while(1)
		{
			printf("5\r\n");
			HAL_Delay(1000);
			printf("4\r\n");
			HAL_Delay(1000);
			printf("3\r\n");
			HAL_Delay(1000);
		
	
			SysTick->CTRL  &=~ SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk;                         
                                             
			HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON,PWR_STOPENTRY_WFE);  /*以WFI进入的睡眠模式*/
		
			SysTick->CTRL  |=SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk; 
			sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */       
			printf("2\r\n");
			HAL_Delay(1000);
			printf("1\r\n");
			HAL_Delay(1000);		
		}  
}
#include "stm32f1xx_hal.h"
#include "rcc.h"

#include <stdarg.h>
#include "stdio.h"
#include "UART.h"
void KEY_Init(void)
{
		__HAL_RCC_GPIOB_CLK_ENABLE() ;
		
		GPIO_InitTypeDef GPIO_InitType;
	
	
		GPIO_InitType.Mode=GPIO_MODE_EVT_FALLING;
		GPIO_InitType.Pin=GPIO_PIN_0;
		GPIO_InitType.Pull=GPIO_PULLUP; //上拉
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
	
		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 	

	
	

}	

停机模式 以WFE进入 的 只能被外部中断唤醒 或者 外部事件 唤醒。

H:待机模式

WKUP引脚唤醒

cpp 复制代码
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "UART.h"

#include "OLED.h"
#include "wwdg.h"
#include "RTC.h"
#include <time.h>
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "IC_encoder.h"

int main(void)
{
	
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */       
	Uart_Init(115200);
	KEY_Init();


	__HAL_RCC_PWR_CLK_ENABLE() ;  //待机模式需要使能PWR时钟

	
	if(__HAL_PWR_GET_FLAG(PWR_FLAG_SB))
	{
			//TRUE
			printf("系统进入待机模式\r\n");
		__HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
	
	}
	else{
		printf("系统不在待机模式\r\n");
	
	}
		while(1)
		{
			printf("5\r\n");
			HAL_Delay(1000);
			printf("4\r\n");
			HAL_Delay(1000);
			printf("3\r\n");
			HAL_Delay(1000);
		
		
			/*需要使能WKUP引脚,我们的STM32F1C8T6只有PA0一个
			不需要单独配置PA0的IO口初始化*/
			HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
			/*
			除了电源控制/状态寄存器(PWR_CSR),所有寄存器被复位。
			WUF:唤醒标志 打开待机模式的时候首先把标志位清除了
			*/
			__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
			HAL_PWR_EnterSTANDBYMode();
			
			printf("2\r\n");
			HAL_Delay(1000);
			printf("1\r\n");
			HAL_Delay(1000);		
		}  
}

H:待机模式

使用RTC闹钟唤醒

RTC:

CSDNhttps://mp.csdn.net/mp_blog/creation/editor/140343928

cpp 复制代码
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "UART.h"

#include "OLED.h"
#include "wwdg.h"
#include "RTC.h"
#include <time.h>
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "IC_encoder.h"

int main(void)
{
	
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */       
	Uart_Init(115200);
	KEY_Init();
	RTC_Init();

	__HAL_RCC_PWR_CLK_ENABLE() ;  //待机模式需要使能PWR时钟

	
	if(__HAL_PWR_GET_FLAG(PWR_FLAG_SB))
	{
			//TRUE
			printf("系统进入待机模式\r\n");
		__HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
	
	}
	else{
		printf("系统不在待机模式\r\n");
	
	}
		while(1)
		{
			printf("5\r\n");
			HAL_Delay(1000);
			printf("4\r\n");
			HAL_Delay(1000);
			printf("3\r\n");
			HAL_Delay(1000);
		
		
			/*需要使能WKUP引脚,我们的STM32F1C8T6只有PA0一个
			不需要单独配置PA0的IO口初始化*/
			//HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
			/*
			除了电源控制/状态寄存器(PWR_CSR),所有寄存器被复位。
			WUF:唤醒标志 打开待机模式的时候首先把标志位清除了
			*/
			__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
			HAL_PWR_EnterSTANDBYMode();
			
			printf("2\r\n");
			HAL_Delay(1000);
			printf("1\r\n");
			HAL_Delay(1000);		
		}  
}

#include "stm32f1xx_hal.h"
#include <time.h>
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"



RTC_HandleTypeDef RTC_Handle;
struct tm timedata;    /*设置时间的结构体*/
struct tm alarm_clock;   /*设置闹钟的结构体*/
time_t temp;     /*mktime的反回值为time_t类型的,所以这个也定义为这个类型的*/

void RTC_Init(void)
{

	RTC_Handle.Instance=RTC;
	RTC_Handle.Init.AsynchPrediv=RTC_AUTO_1_SECOND;  /*这个参数自动1S给CNT加1*/
	RTC_Handle.Init.OutPut=RTC_OUTPUTSOURCE_NONE;  /*RTC输出引脚*/
	HAL_RTC_Init(&RTC_Handle);
	
	timedata.tm_year=2024-1900;
	timedata.tm_mon=7;
	timedata.tm_mday=11;
	timedata.tm_hour=14;
	timedata.tm_min=29;
	timedata.tm_sec=30;
	
	/*设置闹钟结构体  6s后面触发闹钟*/

	alarm_clock.tm_year=2024-1900;
	alarm_clock.tm_mon=7;
	alarm_clock.tm_mday=11;
	alarm_clock.tm_hour=14;
	alarm_clock.tm_min=29;
	alarm_clock.tm_sec=36;
	
	temp = mktime(&timedata);   /*把时间变为秒的c库*/
	__HAL_RTC_WRITEPROTECTION_DISABLE(&RTC_Handle);   /*关闭写保护*/
	/*写时钟计数器寄存器*/
	WRITE_REG(RTC_Handle.Instance->CNTH,temp>>16);
	WRITE_REG(RTC_Handle.Instance->CNTL,temp&0x0000FFFF);
	
	temp = mktime(&alarm_clock);   /*把时间变为秒的c库*/
	/*写闹钟寄存器*/
	WRITE_REG(RTC_Handle.Instance->ALRH,temp>>16);
	WRITE_REG(RTC_Handle.Instance->ALRL,temp&0x0000FFFF);
	__HAL_RTC_WRITEPROTECTION_ENABLE(&RTC_Handle);   /*开启写保护*/
	
	HAL_Delay(20);    /*必须写延迟*/
	
	/*在HAL_RTC_SetAlarm_IT函数下复制的。我们没有使用HAL的,使用的为c库使用需要自己干*/
	
  __HAL_RTC_ALARM_CLEAR_FLAG(&RTC_Handle, RTC_FLAG_ALRAF); /*清除RTC的闹钟标志位*/

  
  __HAL_RTC_ALARM_ENABLE_IT(&RTC_Handle, RTC_IT_ALRA);  /*使能闹钟打开中断*/

 
  __HAL_RTC_ALARM_EXTI_ENABLE_IT();  /*打开EXTI时钟线;EXTI线17连接到RTC闹钟事件*/

  __HAL_RTC_ALARM_EXTI_ENABLE_RISING_EDGE();  /*上升沿触发*/


}


void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
		
	__HAL_RCC_RTC_ENABLE();         //打开RTC的时钟
	__HAL_RCC_BKP_CLK_ENABLE();     //打开备份区时钟
	/*闹钟的NVIC*/

//	HAL_NVIC_SetPriority(RTC_Alarm_IRQn,2,0);
//	HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
	
}
相关推荐
wenchm39 分钟前
细说STM32F407单片机轮询方式读写SPI FLASH W25Q16BV
stm32·单片机·嵌入式硬件
委员1 小时前
基于NodeMCU的物联网电灯控制系统设计
单片机·物联网·嵌入式·nodemcu··lu_asr01·gy-302
北国无红豆1 小时前
【CAN总线】STM32的CAN外设
c语言·stm32·嵌入式硬件
单片机学习之路1 小时前
【C语言】结构
c语言·开发语言·stm32·单片机·51单片机
m0_748254093 小时前
STM32--超声波模块(HC—SR04)(标准库+HAL库)
stm32·单片机·嵌入式硬件
南城花随雪。3 小时前
单片机:实现FFT快速傅里叶变换算法(附带源码)
单片机·嵌入式硬件·算法
逝灮3 小时前
【蓝桥杯——物联网设计与开发】基础模块8 - RTC
stm32·单片机·嵌入式硬件·mcu·物联网·蓝桥杯·rtc
LXL_244 小时前
模拟——郑益慧_笔记1_绪论
嵌入式硬件
weixin_452600699 小时前
串行时钟保持芯片D1380/D1381,低功耗工作方式自带秒、分、时、日、日期、月、年的串行时钟保持芯片,每个月多少天以及闰年能自动调节
科技·单片机·嵌入式硬件·时钟·白色家电电源·微机串行时钟
森旺电子13 小时前
51单片机仿真摇号抽奖机源程序 12864液晶显示
单片机·嵌入式硬件·51单片机