一、WFI和WFE
WFI: wait for interrupt,是"等待中断"的意思;
WFE: wait for event,是"等待事件"的意思;
1)执行HAL_PWR_DisableSleepOnExit(),则令SLEEPONEXIT位置0;
当SLEEPDEEP=0,SLEEPONEXIT=0,如果执行WFI,则立即进入"sleep模式",中断唤醒后,程序会继续执行main()中的其它语句;
退出睡眠方式是:中断;
当SLEEPDEEP=0,SLEEPONEXIT=0,如果执行WFE,则立即进入"sleep模式",唤醒后,程序会继续执行main()中的其它语句;
退出睡眠方式是:唤醒事件,包括中断;
2)执行HAL_PWR_EnableSleepOnExit(),则令SLEEPONEXIT位置1;
若执行WFI或WFE,则立即进入"sleep模式";中断唤醒后,程序只执行"相应的中断服务程序",退出中断后,会再次进入"sleep模式",
因此,不再执行执行main()中的其它语句;
唤醒退出方式是:中断;
3)执行HAL_PWR_EnableSEVOnPend(),则令SEVONPEND位置1;允许事件唤醒内核(包括中断唤醒),
在执行WFE指令前,需要先设置SEVONPEND=1,才可以使用事件唤醒,否则,只能使用中断唤醒;
但通过WFI进入的"sleep模式",即使设置SEVONPEND=1,事件也无法唤醒CPU核,不清楚原因?
翻译的文档,写得实在糟糕,一些功能性表格,就不截图了,免得会产生误解!!!
4)"stop模式"可以执行事件唤醒和中断唤醒。
注意:在sleep模式中,所有的"IO引脚"和"run模式"保持相同的电平状态;
在进入睡眠前,需要配置好"相应的唤醒源",关闭不用的模块,如SysTick,以便CPU进入Sleep模式;
二、测试程序
#include "SleepMode.h"
/*
"sleep模式"
WFI: wait for interrupt,是"等待中断"的意思;
WFE: wait for event,是"等待事件"的意思;
1)执行HAL_PWR_DisableSleepOnExit(),则令SLEEPONEXIT位置0;
当SLEEPDEEP=0,SLEEPONEXIT=0,如果执行WFI,则立即进入"sleep模式",中断唤醒后,程序会继续执行main()中的其它语句;
退出睡眠方式是:中断;
当SLEEPDEEP=0,SLEEPONEXIT=0,如果执行WFE,则立即进入"sleep模式",唤醒后,程序会继续执行main()中的其它语句;
退出睡眠方式是:唤醒事件,包括中断;
2)执行HAL_PWR_EnableSleepOnExit(),则令SLEEPONEXIT位置1;
若执行WFI或WFE,则立即进入"sleep模式";中断唤醒后,程序只执行"相应的中断服务程序",退出中断后,会再次进入"sleep模式",
因此,不再执行执行main()中的其它语句;
唤醒退出方式是:中断;
3)执行HAL_PWR_EnableSEVOnPend(),则令SEVONPEND位置1;允许事件唤醒内核(包括中断唤醒),
在执行WFE指令前,需要先设置SEVONPEND=1,才可以使用事件唤醒,否则,只能使用中断唤醒,
但通过WFI进入的"sleep模式",即使设置SEVONPEND=1,事件也无法唤醒CPU核,不清楚原因?
4)"stop模式"可以执行事件唤醒和中断唤醒。
注意:在sleep模式中,所有的"IO引脚"和"run模式"保持相同的电平状态;
在进入睡眠前,需要配置好"相应的唤醒源",关闭不用的模块,如SysTick,以便CPU进入Sleep模式;
*/
void EnterSLEEPMode_With_WFE(void);
void EnterSLEEPMode_With_WFI(void);
void EnterSLEEPMode_With_WFE_And_After_Interrupt(void);
void EnterSLEEPMode_With_WFI_And_After_Interrupt(void);
//函数功能:中断或事件唤醒后,程序会继续执行main()中的其它语句
//通过WFE进入的"sleep模式",退出方式是:唤醒事件,也可以是中断
void EnterSLEEPMode_With_WFE(void)
{
__HAL_RCC_PWR_CLK_ENABLE();
//PWR时钟使能
//这一句不能少,否则,不能进入"sleep模式"
HAL_PWR_DisableSleepOnExit();
//则令SLEEPONEXIT位置0;
//如果执行WFE,则立即进入"sleep模式"
HAL_PWR_EnableSEVOnPend();
//当"SEVONPEND=1"时,允许事件唤醒内核(包括中断唤醒)
//这一句不能少,否则,事件无法唤醒CPU
HAL_PWR_EnterSLEEPMode(PWR_SLEEPENTRY_WFE);
//令SLEEPDEEP=0,然后执行WFE,令CPU进入"sleep模式"
}
//函数功能:中断唤醒后,程序会继续执行main()中的其它语句
//通过WFI进入的"sleep模式",退出方式是:只能是中断
void EnterSLEEPMode_With_WFI(void)
{
__HAL_RCC_PWR_CLK_ENABLE();
//PWR时钟使能
//这一句不能少,否则,不能进入"sleep模式"
HAL_PWR_DisableSleepOnExit();
//则令SLEEPONEXIT位置0;
//如果执行WFI,则立即进入"sleep模式"
HAL_PWR_EnableSEVOnPend();
//当"SEVONPEND=1"时,允许事件唤醒内核(包括中断唤醒)
//这一句不能少,否则,事件无法唤醒CPU
HAL_PWR_EnterSLEEPMode(PWR_SLEEPENTRY_WFI);
//令SLEEPDEEP=0,然后执行WFI,令CPU进入"sleep模式"
}
//函数功能:中断唤醒后,程序只执行"相应的中断服务程序",退出中断后,会再次进入"sleep模式";
//SLEEPONEXIT位置1,通过WFE进入的"sleep模式",退出方式是:只能是中断
void EnterSLEEPMode_With_WFE_And_After_Interrupt(void)
{
__HAL_RCC_PWR_CLK_ENABLE();
//PWR时钟使能
//这一句不能少,否则,不能进入"sleep模式"
HAL_PWR_EnableSleepOnExit();
//则令SLEEPONEXIT位置1,若执行WFE,则立即进入"sleep模式";当有中断发生后,则执行完中断服务程序,会继续进入"sleep模式"
HAL_PWR_EnterSLEEPMode(PWR_SLEEPENTRY_WFE);
//令SLEEPDEEP=0,然后执行WFE,令CPU进入"sleep模式"
}
//函数功能:中断唤醒后,程序只执行"相应的中断服务程序",退出中断后,会再次进入"sleep模式";
//SLEEPONEXIT位置1,通过WFI进入的"sleep模式",退出方式是:只能是中断
void EnterSLEEPMode_With_WFI_And_After_Interrupt(void)
{
__HAL_RCC_PWR_CLK_ENABLE();
//PWR时钟使能
//这一句不能少,否则,不能进入"sleep模式"
HAL_PWR_EnableSleepOnExit();
//则令SLEEPONEXIT位置1,若执行WFI,则立即进入"sleep模式";当有中断发生后,则执行完中断服务程序,会继续进入"sleep模式"
HAL_PWR_EnterSLEEPMode(PWR_SLEEPENTRY_WFI);
//令SLEEPDEEP=0,然后执行WFI,令CPU进入"sleep模式"
}
#include "py32f0xx_hal.h"
#include "SystemClock.h"
#include "delay.h"
#include "LED.h"
#include "SystemClock.h"
#include "USART2.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "string.h" //使能strcpy(),strlen(),memset()
#include "EXTI.h"
#include "SleepMode.h"
const char CPU_Reset_REG[]="\r\nCPU reset!\r\n";
const char CPU_Run_REG[]="\r\nCPU run!\r\n";
int main(void)
{
HSE_Config();
// HSI_Config();
// HAL_Init();//systick初始化
delay_init();
HAL_Delay(1000);
USART2_Init(115200);
//PA0是为USART2_TX,PA1是USART2_RX
//中断优先级为0x01
//波特率为115200,数字为8位,停止位为1位,无奇偶校验,允许发送和接收数据,只允许接收中断,并使能串口
printf("%s",CPU_Reset_REG);
HAL_Delay(1000);
printf("%s",CPU_Run_REG);
MCU_LED_Init();
HAL_SuspendTick();//systick中断关闭,防止systick中断唤醒
EXTI12_Init();//将PA12引脚配置为外部中断引脚
printf("SLEEP MODE!\n\n");
EnterSLEEPMode_With_WFE();
// EnterSLEEPMode_With_WFI();
// EnterSLEEPMode_With_WFE_And_After_Interrupt();
// EnterSLEEPMode_With_WFI_And_After_Interrupt();
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_12);//清除外部事件标志位
//清除"GPIO_PIN_12外部事件标志位"
//当软件或者硬件产生上升沿/下降沿触发事件时,该位置位;
NVIC_ClearPendingIRQ(EXTI4_15_IRQn);//清除EXTI4_15_IRQn中断源的中断标志位
HAL_ResumeTick();//systick中断开启
printf("WAKEUP OK!\n\n");
while (1)
{
HAL_Delay(500);
MCU_LED_Toggle();
printf("1234567890\r\n");
}
}
事件唤醒:
//函数功能:将PA12引脚配置为外部事件引脚
void EXTI12_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructureure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //GPIOA时钟使能
GPIO_InitStructureure.Pin = GPIO_PIN_12;//选择第12脚
GPIO_InitStructureure.Pull = GPIO_PULLUP;//引脚上拉被激活
GPIO_InitStructureure.Speed = GPIO_SPEED_FREQ_MEDIUM;//配置GPIO速度为中速
GPIO_InitStructureure.Mode = GPIO_MODE_IT_FALLING;//配置为下降沿检测模式
HAL_GPIO_Init(GPIOA, &GPIO_InitStructureure);
//根据GPIO_InitStructureure结构变量指定的参数初始化GPIOA的外设寄存器
//因为使用了GPIO_MODE_IT_FALLING,所以使能了外部中断线
// HAL_NVIC_SetPriority(EXTI4_15_IRQn,0x0F,0);
//EXTI4_15_IRQn表示外部中断线4~中断线15,EXTI4_15_IRQn
//设置中断优先级为0x0F,0无意义,注意:0<=PreemptPriority<=3,值越大,表示中断优先级越低
//Enable and set Button EXTI Interrupt to the lowest priority
// HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);
//EXTI4_15_IRQn表示外部中断线4~中断线15,EXTI4_15_IRQn
//使能EXTI4_15_IRQn中断
}
中断唤醒
//函数功能:将PA12引脚配置为外部中断引脚
void EXTI12_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructureure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //GPIOA时钟使能
GPIO_InitStructureure.Pin = GPIO_PIN_12;//选择第12脚
GPIO_InitStructureure.Pull = GPIO_PULLUP;//引脚上拉被激活
GPIO_InitStructureure.Speed = GPIO_SPEED_FREQ_MEDIUM;//配置GPIO速度为中速
GPIO_InitStructureure.Mode = GPIO_MODE_IT_FALLING;//配置为下降沿检测模式
HAL_GPIO_Init(GPIOA, &GPIO_InitStructureure);
//根据GPIO_InitStructureure结构变量指定的参数初始化GPIOA的外设寄存器
//因为使用了GPIO_MODE_IT_FALLING,所以使能了外部中断线
HAL_NVIC_SetPriority(EXTI4_15_IRQn,0x03,0);
//EXTI4_15_IRQn表示外部中断线4~中断线15,EXTI4_15_IRQn
//设置中断优先级为0x03,0无意义,注意:0<=PreemptPriority<=3,值越大,表示中断优先级越低
//Enable and set Button EXTI Interrupt to the lowest priority
HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);
//EXTI4_15_IRQn表示外部中断线4~中断线15,EXTI4_15_IRQn
//使能EXTI4_15_IRQn中断
}
三、事件唤醒测试结果