GD32F103VE睡眠与唤醒,兆易官网给的程序没有测试。等测试后,才发现有问题。
现修改,测试如下:
#include "SleepMode.h"
#include "delay.h"
u8 WFE_CMD_EnterSleepModeFlag;
void Enter_DeepSleepMode(void);
void Enter_SleepMode_Use_WFI_CMD(void);
void Enter_SLEEP_After_Interrupt(void);
void Enter_DeepSleepMode0_Use_WFE_CMD(void);
void Enter_DeepSleepMode1_Use_WFE_CMD(void);
//函数功能:CPU进入深度睡眠,但在被唤醒后CPU进入低功耗模式
//唤醒方式: 外部中断;若没有唤醒,再次烧录程序,就会无法烧录;
//测试OK
void Enter_DeepSleepMode(void)
{
WFE_CMD_EnterSleepModeFlag=0;
rcu_periph_clock_enable(RCU_PMU);//使能"电源管理单元时钟"外设时钟
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER,WFI_CMD);
//PMU_LDO_LOWPOWER命令CPU进入深度睡眠,LDO为低功耗模式
//注意:WFI need EXTI interrupt wakeup
}
//函数功能:使用WFI命令使CPU进入睡眠模式,来自EXTI的任何中断可唤醒
//systick中断会唤醒MCU,因此,CPU进入睡眠前先关闭systick的中断;退出睡眠模式后,需要CPU再打开systick的中断
//测试OK
void Enter_SleepMode_Use_WFI_CMD(void)
{
WFE_CMD_EnterSleepModeFlag=1;
SysTickInterruptDisable();//CPU进入睡眠前,需要关闭系统滴答时钟中断
rcu_periph_clock_enable(RCU_PMU);//使能"电源管理单元时钟"
pmu_to_sleepmode(WFI_CMD);
//通过WFI命令CPU进入睡眠
//唤醒:任何中断均可唤醒
// SysTickInterruptEnable();//CPU被唤醒后,要打开系统滴答时钟中断
}
//函数功能:配置中断发生后,执行完当前中断立即进入睡眠
//EXTI3中断后,进入睡眠
//EXTI7中断后,取消设置"退出中断后立即进入睡眠",同时打开系统滴答时钟中断
//测试OK
void Enter_SLEEP_After_Interrupt(void)
{
SysTickInterruptDisable();//CPU进入睡眠前,需要关闭系统滴答时钟中断
WFE_CMD_EnterSleepModeFlag=2;
rcu_periph_clock_enable(RCU_PMU);//使能"电源管理单元时钟"
system_lowpower_set(SCB_LPM_SLEEP_EXIT_ISR);
//SCB_LPM_SLEEP_EXIT_ISR允许"退出中断后立即进入睡眠"
//EXTI3中断后,进入睡眠
//EXTI7中断后,取消设置"退出中断后立即进入睡眠",同时打开系统滴答时钟中断
}
//函数功能:使用WFE命令使CPU进入深度睡眠模式,但在被唤醒后CPU进入低功耗模式
//测试OK
void Enter_DeepSleepMode0_Use_WFE_CMD(void)
{
rcu_periph_clock_enable(RCU_PMU);//使能RCU_PMU外设时钟
system_lowpower_reset(SCB_LPM_WAKE_BY_ALL_INT);//设置SEVONPEND=0
WFE_CMD_EnterSleepModeFlag=3;
pmu_to_deepsleepmode(PMU_LDO_NORMAL,WFE_CMD);
//SLEEPDEEP=1,STBMOD=0,使用WFE命令或执行SEV事件,使CPU进入深度睡眠,但LDO工作
//唤醒:若通过WFE进入,来自EXTI的任何事件可唤醒,本程序使用外部事件;
}
//函数功能:使用WFE命令使CPU进入深度睡眠模式,但在被唤醒后CPU进入低功耗模式
//测试OK
void Enter_DeepSleepMode1_Use_WFE_CMD(void)
{
rcu_periph_clock_enable(RCU_PMU);//使能RCU_PMU外设时钟
system_lowpower_set(SCB_LPM_WAKE_BY_ALL_INT);//设置SEVONPEND=1
WFE_CMD_EnterSleepModeFlag=4;
pmu_to_deepsleepmode(PMU_LDO_NORMAL,WFE_CMD);
//SLEEPDEEP=1,STBMOD=0,使用WFE命令或执行SEV事件,使CPU进入深度睡眠,但LDO工作
//唤醒:若通过WFE进入,SEVONPEND=1时,中断可唤醒,本程序使用USART0
}
#include "KEY.h"
#include "SleepMode.h"
#include "LED.h"
#include "delay.h"
#include "SYSCLKConfig.h"
/*
GD32F103VE外部中断线线0~15,对应外部IO口的输入中断。
它有7个中断向量,外部中断线0 ~ 4分别对应EXTI0_IRQn ~ EXTI4_IRQn中断向量;
外部中断线 5 ~ 9 共用一个 EXTI9_5_IRQn中断向量;外部中断线10~15 共用一个EXTI15_10_IRQn中断向量。
7个中断向量:EXTI0_IRQn,EXTI1_IRQn,EXTI2_IRQn, EXTI3_IRQn, EXTI4_IRQn,EXTI9_5_IRQn和EXTI15_10_IRQn。
这7个中断向量对应7个中断服务函数:
EXTI0_IRQHandler();
EXTI1_IRQHandler();
EXTI2_IRQHandler();
EXTI3_IRQHandler();
EXTI4_IRQHandler();
EXTI9_5_IRQHandler();
EXTI15_10_IRQHandler();
*/
void ExternalInterrupt3_Init(void);
void ExternalInterrupt7_Init(void);
//函数功能:初始化ExternalInterrupt3
void ExternalInterrupt3_Init(void)
{
//NVIC_PRIGROUP_PRE4_SUB0:抢占优先级为4bit(取值为0~15),子优先级为0bit(没有响应优先级)
//NVIC_PRIGROUP_PRE3_SUB1:抢占优先级为3bit(取值为0~7),子优先级为1bit(取值为0~1)
//NVIC_PRIGROUP_PRE2_SUB2:抢占优先级为2bit(取值为0~3),子优先级为2bit(取值为0~3)
//NVIC_PRIGROUP_PRE1_SUB3:抢占优先级为1bit(取值为0~1),子优先级为3bit(取值为0~7)
//NVIC_PRIGROUP_PRE0_SUB4:抢占优先级为0bit(没有抢占优先级),子优先级为3bit(取值为0~15)
nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);//设置系统中断优先级"抢占优先级为4bit,子优先级为0bit"
nvic_irq_enable(EXTI3_IRQn, 2U, 0U);//设置EXTI3_IRQn的中断优先级,抢占优先级为2,子优先级为0
rcu_periph_clock_enable(RCU_GPIOE);//使能GPIOE时钟,enable GPIO clock
rcu_periph_clock_enable(RCU_AF); //外部中断,需要使能复用功能时钟
gpio_init(GPIOE, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3);//将GPIOE3设置为浮空输入
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOE, GPIO_PIN_SOURCE_3);
//设置GE3引脚为外部中断源,select GPIO pin exti sources
exti_init(EXTI_3, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
//配置外部中断线使用外部中断3(EXTI_3)
//中断模式为外部中断(EXTI_INTERRUPT)
//中断触发方式为下降沿中断(EXTI_TRIG_FALLING)
exti_interrupt_flag_clear(EXTI_3);//清除外部中断3标志
exti_interrupt_enable(EXTI_3);//使能外部中断3(EXTI_3)
}
//函数功能:初始化ExternalInterrupt7
void ExternalInterrupt7_Init(void)
{
rcu_periph_clock_enable(RCU_GPIOE);//使能GPIOE时钟,enable GPIO clock
rcu_periph_clock_enable(RCU_AF);//使能复用功能时钟
gpio_init(GPIOE, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_7);//将GPIOE7设置为浮空输入
nvic_irq_enable(EXTI5_9_IRQn, 2U, 0U);//设置EXTI5_9_IRQn的中断优先级,抢占优先级为2,子优先级为0
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOE, GPIO_PIN_SOURCE_7);
//设置GE7引脚为外部中断源,select GPIO pin exti sources
exti_init(EXTI_7, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
//配置外部中断线使用外部中断7(EXTI_7)
//中断模式为外部中断(EXTI_INTERRUPT)
//中断触发方式为下降沿中断(EXTI_TRIG_FALLING)
exti_interrupt_flag_clear(EXTI_7);//清除外部中断7标志
exti_interrupt_enable(EXTI_7);//使能外部中断7(EXTI_7)
}
//函数功能:外部中断3的中断服务函数,外部中断0~外部中断4具有独立的中断入口地址
void EXTI3_IRQHandler(void)
{
FlagStatus ret;
ret=exti_interrupt_flag_get(EXTI_3);
//读取外部中断3(EXTI_3)的中断标志
//get EXTI lines flag when the interrupt flag is set
if(RESET != ret)
{
MCU_Led_Toggle();
}
exti_interrupt_flag_clear(EXTI_3);//清除外部中断3标志
}
//this function handles external lines 5 to 9 interrupt request
//函数功能:外部中断5~外部中断9的中断服务函数
void EXTI5_9_IRQHandler(void)
{
FlagStatus ret;
ret=exti_interrupt_flag_get(EXTI_7);
//读取外部中断7(EXTI_7)的中断标志
//get EXTI lines flag when the interrupt flag is set
if(RESET != ret)
{
if(WFE_CMD_EnterSleepModeFlag==0)
MySystemClockInit(1,1);//恢复使用外部晶振,带有PLL工作,系统时钟108MHz
if(WFE_CMD_EnterSleepModeFlag==1)
{
SysTickInterruptEnable();//CPU被唤醒后,要打开系统滴答时钟中断
}
if(WFE_CMD_EnterSleepModeFlag==2)
{
SysTickInterruptEnable();//CPU被唤醒后,要打开系统滴答时钟中断
rcu_periph_clock_enable(RCU_PMU);//使能"电源管理单元时钟"
system_lowpower_reset(SCB_LPM_SLEEP_EXIT_ISR);
//取消设置"退出中断后立即进入睡眠",
}
if(WFE_CMD_EnterSleepModeFlag==3)
MySystemClockInit(1,1);//恢复使用外部晶振,带有PLL工作,系统时钟108MHz
if(WFE_CMD_EnterSleepModeFlag==4)
MySystemClockInit(1,1);//恢复使用外部晶振,带有PLL工作,系统时钟108MHz
}
exti_interrupt_flag_clear(EXTI_7);//清除外部中断7标志
}
#include "SYSCLKConfig.h"
void RCU_IRC8M_Clock_Out_Init(void);
void RCU_HXTAL_Clock_Out_Init(void);
void RCU_SYS_Clock_Out_Init(void);
void MySystemClockInit(uint8_t tRCClockSource,uint8_t use_pll);
//函数功能:内部RC8MHz振荡器时钟输出
void RCU_IRC8M_Clock_Out_Init(void)
{
rcu_periph_clock_enable(RCU_GPIOA);//使能GPIOA时钟,enable GPIO clock
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
//将GPIOA8设置为复用功能IO推挽输出,初始化时钟输出引脚
//CK_OUT0为时钟输出引脚,映射到PA8引脚
rcu_ckout0_config(RCU_CKOUT0SRC_IRC8M);
//设置CK_OUT0引脚(PA8)的输出时钟源为CK_IRC8M(内部RC8MHz)时钟
}
//函数功能:外部8MHz振荡器时钟输出
void RCU_HXTAL_Clock_Out_Init(void)
{
rcu_periph_clock_enable(RCU_GPIOA);//使能GPIOA时钟,enable GPIO clock
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
//将GPIOA8设置为复用功能IO推挽输出,初始化时钟输出引脚
//CK_OUT0为时钟输出引脚,映射到PA8引脚
rcu_ckout0_config(RCU_CKOUT0SRC_HXTAL);
//设置CK_OUT0引脚(PA8)的输出时钟源为CK_HXTAL(外部8MHz)时钟
}
//函数功能:将CK_OUT0映射到PA8引脚,输出系统时钟;
void RCU_SYS_Clock_Out_Init(void)
{
rcu_periph_clock_enable(RCU_GPIOA);//使能GPIOA时钟,enable GPIO clock
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
//将GPIOA8设置为复用功能IO推挽输出,初始化时钟输出引脚
//CK_OUT0为时钟输出引脚,映射到PA8引脚
rcu_ckout0_config(RCU_CKOUT0SRC_HXTAL);
//设置CK_OUT0引脚(PA8)的输出时钟源为CK_HXTAL(外部8MHz)时钟
}
//MySystemClockInit(0,1);//使用内部RC振荡器,带有PLL工作,系统时钟108MHz
//MySystemClockInit(1,1);//使用外部晶振,带有PLL工作,系统时钟108MHz
//MySystemClockInit(0,0);//直接使用内部RC振荡器,不带PLL工作,系统时钟8MHz
//MySystemClockInit(1,0);//直接使用外部晶振,不带PLL工作,系统时钟8MHz
//函数功能:系统时钟初始化
void MySystemClockInit(uint8_t tRCClockSource,uint8_t use_pll)
{
///选择"振荡源"开始//
if(tRCClockSource==0)//内部RC时钟
{
rcu_osci_on(RCU_IRC8M);//启用"内部RC8MHz振荡器"
rcu_osci_stab_wait(RCU_IRC8M);//等待内部RC8MHz振荡器稳定
}
else//内部高速晶振时钟
{
rcu_osci_on(RCU_HXTAL);//启用"外部8MHz晶振"
rcu_osci_stab_wait(RCU_HXTAL);//等待外部8MHz晶振振荡器稳定
}
///选择"振荡源"结束//
///选择"PLL时钟源"开始//
if(use_pll)//使用PLL
{
if(tRCClockSource==0)//内部RC时钟
{
rcu_pll_config(RCU_PLLSRC_IRC8M_DIV2,RCU_PLL_MUL27);
//设置内部RC8MHz输出的时钟再2分频后作为PLL输入时钟源,并设置倍频因子为27
rcu_osci_on(RCU_PLL_CK);//使能"PLL时钟"
rcu_osci_stab_wait(RCU_PLL_CK);//等待"PLL时钟"稳定
}
else
{
rcu_predv0_config(RCU_PREDV0_DIV2);//设置外部晶振输出后再进行2分频得到的时钟
rcu_pll_config(RCU_PLLSRC_HXTAL,RCU_PLL_MUL27);
//设置外部晶振8MHz输出的时钟再2分频后作为PLL输入时钟源,并设置倍频因子为27
rcu_osci_on(RCU_PLL_CK);//使能"PLL时钟"
rcu_osci_stab_wait(RCU_PLL_CK);//等待"PLL时钟"稳定
}
}
///选择"PLL时钟源"结束//
///选择"系统时钟源"开始//
if(use_pll)//使用PLL
{
rcu_system_clock_source_config(RCU_CKSYSSRC_PLL);
//选择选择CK_PLL时钟作为系统时钟CK_SYS
}
else
{
if(tRCClockSource==0)//内部RC时钟
{
rcu_system_clock_source_config(RCU_CKSYSSRC_IRC8M);
//直接选择内部RC8MHz输出的时钟CK_IRC8M作为系统时钟CK_SYS
}
else
{
rcu_system_clock_source_config(RCU_CKSYSSRC_HXTAL);
//直接选择外部晶振时钟输出的CK_HXTAL时钟作为系统时钟CK_SYS
}
}
///选择"系统时钟源"结束//
}
#ifndef __DELAY_H
#define __DELAY_H
#include "sys.h" //使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t,bool
#define SysTickInterruptEnable() SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; //开启SysTick中断
#define SysTickInterruptDisable() SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk; //关闭SysTick中断
extern void delay_init(void);
extern void delay_ms(u32 nms);
extern void delay_us(u32 nus);
extern void delay_xms(u32 nms);
#endif
#include "delay.h"
//
//如果需要使用OS,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS == 1
#include "FreeRTOS.h" //FreeRTOS使用
#include "task.h"
#else
#define configTICK_RATE_HZ (1000) //时钟节拍频率,这里设置为1000,周期就是1ms
#endif
static u8 fac_us=0; //us延时倍乘数
#if SYSTEM_SUPPORT_OS == 1
static u16 fac_ms=0; //ms延时倍乘数,在ucos下,代表每个节拍的ms数
#endif
/函数声明开始//
void delay_init(void);
void delay_ms(u32 nms);
void delay_us(u32 nus);
void delay_xms(u32 nms);
/函数声明结束//
extern void xPortSysTickHandler(void); //引用外部函数
//函数功能:SystemTimer中断服务函数,使用ucos时用到
void SysTick_Handler(void)
{
#if SYSTEM_SUPPORT_OS == 1
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
xPortSysTickHandler();
}
#endif
}
//初始化延迟函数
//SystemTimer的时钟固定为AHB时钟,这里为了兼容FreeRTOS,所以将SystemTimer的时钟频率改为AHB的频率!
//SystemTimer是一个24位的定时器
void delay_init()
{
u32 reload;
//SystemCoreClock=108000000
fac_us=SystemCoreClock/1000000; //不论是否使用OS,fac_us都需要使用
reload=SystemCoreClock/1000000; //每秒钟需要计数的次数
reload*=1000000/configTICK_RATE_HZ; //根据configTICK_RATE_HZ设定溢出时间
//reload为24位寄存器,最大值:16777216,在108MHz下,约合0.155s左右
#if SYSTEM_SUPPORT_OS == 1
fac_ms=1000/configTICK_RATE_HZ; //代表OS可以延时的最少单位
#endif
SysTick->LOAD = reload; //每1/configTICK_RATE_HZ秒中断一次
SysTick->VAL = 0; //设置SysTick计数器器初始值为0,Load the SysTick Counter Value
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1 );
//SysTick_IRQn=-1,设置的是Cortex-M系统中断优先级 set Priority for Systick Interrupt
//设置SYSTICK的优先级为15
SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk; //选择内核时钟FCLK为时钟源(GD32的FCLK为108MHz)
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; //开启SysTick中断
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //使能SysTick工作
/*
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | //选择内核时钟FCLK为时钟源(GD32的FCLK为108MHz)
SysTick_CTRL_TICKINT_Msk | //开启SysTick中断
SysTick_CTRL_ENABLE_Msk; //使能SysTick工作
*/
//NVIC_SetPriority(SysTick_IRQn, 0x00U);
//当IRQn<0时,设置的是Cortex-M系统中断优先级
//设置SYSTICK的优先级为0
}
//函数功能:精确延时延时nus微妙
//nus:要延时的us数.
//nus:0~204522252(最大值即2^32/fac_us@fac_us=168)
void delay_us(u32 nus)
{
u32 ticks;
u32 told,tnow,tcnt=0;
u32 reload=SysTick->LOAD; //LOAD的值
ticks=nus*fac_us; //需要的节拍数
told=SysTick->VAL; //刚进入时的计数器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
}
}
}
//函数功能:精确延时nms毫秒
//nms:要延时的ms数
//nms:0~65535
void delay_ms(u32 nms)
{
#if SYSTEM_SUPPORT_OS == 1
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
if(nms>=fac_ms) //延时的时间大于OS的最少时间周期
{
vTaskDelay(nms/fac_ms); //FreeRTOS延时
}
nms%=fac_ms; //OS已经无法提供这么小的延时了,采用普通方式延时
}
#endif
delay_us((u32)(nms*1000));//普通方式延时
}
//函数功能:延时nms,不会引起任务调度
//nms:要延时的ms数
void delay_xms(u32 nms)
{
u32 i;
for(i=0;i<nms;i++) delay_us(1000);
}
pmu_to_sleepmode(WFE_CMD);不清楚,为什么不行?后期再更新。