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);
	
}
相关推荐
无垠的广袤9 小时前
【工业树莓派 CM0 NANO 单板计算机】本地部署 EMQX
linux·python·嵌入式硬件·物联网·树莓派·emqx·工业物联网
雲烟11 小时前
嵌入式设备EMC安规检测参考
网络·单片机·嵌入式硬件
泽虞11 小时前
《STM32单片机开发》p7
笔记·stm32·单片机·嵌入式硬件
田甲12 小时前
【STM32】 数码管驱动
stm32·单片机·嵌入式硬件
up向上up12 小时前
基于51单片机垃圾箱自动分类加料机快递物流分拣器系统设计
单片机·嵌入式硬件·51单片机
纳祥科技21 小时前
Switch快充方案,内置GaN,集成了多个独立芯片
单片机
单片机日志1 天前
【单片机毕业设计】【mcugc-mcu826】基于单片机的智能风扇系统设计
stm32·单片机·嵌入式硬件·毕业设计·智能家居·课程设计·电子信息
松涛和鸣1 天前
从零开始理解 C 语言函数指针与回调机制
linux·c语言·开发语言·嵌入式硬件·排序算法
小曹要微笑1 天前
STM32F7 时钟树简讲(快速入门)
c语言·stm32·单片机·嵌入式硬件·算法
XINVRY-FPGA1 天前
XCVP1802-2MSILSVC4072 AMD Xilinx Versal Premium Adaptive SoC FPGA
人工智能·嵌入式硬件·fpga开发·数据挖掘·云计算·硬件工程·fpga