通俗的来讲低功耗模式就是降低单片机的运行功耗
/*
入口参数:
PWR_Regulator_LowPower:低功耗模式进入
PWR_STOPEntry_WFI : WFI进入,任意外部中断退出
*/
PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFI)
这里我们通过一个外部中断来退出STOP模式,我们采用串口的接收引脚通过发送数据产生引脚的电平变化来触发外部中断产生,从而退出STOP模式
具体配置代码如下
u8 UART1_RECEIVE_OK = 0; //UART1中断接收完成标志位
u8 UART1_RECEIVE_DATA[200]; //UART1接收缓存
u8 UART1_SEND_DATA[200]; //UART1发送缓存
u8 R_COUNT = 0;
/*
串口1初始化
*/
void USART1_Init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART1, ENABLE); //使能串口1
}
/*
串口1中断
*/
void USART1_IRQHandler(void) //串口1中断服务程序
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//判断是否为接收中断
{
UART1_RECEIVE_DATA[R_COUNT] = USART_ReceiveData(USART1);
//USART_SendData(USART1,UART1_RECEIVE_DATA[R_COUNT]);
R_COUNT++;
}
if(R_COUNT == 199)
{
R_COUNT = 0;
}
UART1_RECEIVE_OK = 1;
}
/*
外部中断初始化
*/
void exti_init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用功能时钟
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource10);
EXTI_InitStructure.EXTI_Line=EXTI_Line10; //串口1 RX
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //外部中断线10
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
}
/*
外部中断
*/
void EXTI15_10_IRQHandler(void)
{
EXTI_ClearITPendingBit(EXTI_Line10); //清除LINE 10 上的中断标志位
}
RTC可以使用外部时钟源,即在低功耗模式下依然能够正常运行
在第一节中讲了外部中断来退出STOP模式,那是需要我们主动去操作来产生一个外部中断,而RTC可以定时来自动产生外部中断从而退出STOP模式
从简介中我们知道RTC有三个专门的中断,我们常用的就是秒中断和闹钟中断,这里闹钟中断发生时会产生一个外部中断,我们可以从STM32中文参考手册中外部中断事件章节得知
下面直接上代码
rtc.c
#include "rtc.h"
#include "sys.h"
/*
RTC初始化
*/
u8 RTC_Init(void)
{
//检查是不是第一次配置时钟
u8 temp=0;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
BKP_DeInit(); //复位备份区域
RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) //检查指定的RCC标志位设置与否,等待低速晶振就绪
{
temp++;
delay_ms(10);
}
if(temp>=250)
return 1;//初始化时钟失败,晶振有问题
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
RTC_WaitForSynchro(); //等待RTC寄存器同步
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_ITConfig(RTC_IT_ALR, ENABLE); //使能RTC时钟中断
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_EnterConfigMode();/// 允许配置
RTC_SetPrescaler(32767); //设置RTC预分频的值
RTC_SetCounter(0); // 这里的设置,每设置一次就要等待写操作完成
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_SetAlarm(5); // 设置闹钟的时间要加1哦,也就是说现在是5S
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_ExitConfigMode(); //退出配置模式
RTC_NVIC_Config(); //RCT中断分组设置
return 0; //ok
}
void RTC_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
//EXTI17配置
EXTI_InitStructure.EXTI_Line = EXTI_Line17; // RTC闹钟为外部中断17
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// 时钟中断配置
NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn; //RTC全局中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级1位,从优先级3位
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //先占优先级0位,从优先级4位
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能该通道中断
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}
/*
RTC闹钟中断函数,当RTC计数器的值和闹钟设定的一样时,触发这个函数
*/
void RTCAlarm_IRQHandler(void)
{
printf("RTC ALARM INTERRUPT\r\n");
if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
{
RTC_SetCounter(0); // 清除RTC计数器,从新开始计数
RTC_WaitForLastTask();
RTC_SetAlarm(10); // 设置闹钟 设置闹钟的时间要加1哦,,也就是说现在是11S
RTC_WaitForLastTask();
}
RTC_ClearITPendingBit(RTC_IT_ALR); //清闹钟中断
EXTI_ClearITPendingBit(EXTI_Line17); //闹钟事件发生,会产生一个EXTI_17外部中断,此标志位要清除,否则下次停止模式进入失败
RTC_ClearITPendingBit(RTC_IT_OW); //清闹钟中断
RTC_WaitForLastTask();
}
这里我们配置了RTC的初始化和闹钟中断,每次产生闹钟中断我们便打印一句话来测试
main.c
#include "stm32f10x.h"
#include "sys.h"
extern u8 UART1_RECEIVE_OK;
extern u8 UART1_RECEIVE_DATA[200];
extern u8 R_COUNT;
/*
进入STOP模式
*/
void Enter_Stop_Mode()
{
printf("enter stop mode\r\n");
PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFI); //进入STOP停机模式
SystemInit(); //系统初始化
delay_ms(10);
printf("wake up\r\n");
}
int main(void)
{
USART1_Init(115200); //初始化串口
exti_init(); //初始化外部中断
delay_init(); //初始化延时
delay_ms(1000);
RTC_Init(); //RTC初始化
Enter_Stop_Mode();
while(1)
{
delay_ms(1000);
printf("running now\r\n");
if(UART1_RECEIVE_OK) //发送串口中断
{
UART1_RECEIVE_OK = 0;
if(UART1_RECEIVE_DATA[0] == 0xaa) //如果收到0xaa则进入stop模式
{
Enter_Stop_Mode();
UART1_RECEIVE_OK = 0;
}
R_COUNT = 0;
}
}
}