一: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);
}