一、低功耗模式
1.简介
通俗的来讲低功耗模式就是降低单片机的运行功耗
STM32F10xxx有三种低功耗模式:(1)睡眠模式(Cortex™-M3内核停止,所有外设包括Cortex-M3核心的外设,如NVIC、系统时 钟(SysTick)等仍在运行) (2)停止模式(所有的时钟都已停止) (3)待机模式(1.8V电源关闭)此外,在运行模式下,可以通过以下方式中的一种降低功耗:(1)降低系统时钟 (2)关闭APB和AHB总线上未被使用的外设时钟。 这里以停止模式为例来讲解
2.进入STOP模式
要想进入停止模式,我们只需调用固件库的函数:/*入口参数:PWR_Regulator_LowPower:低功耗模式进入PWR_STOPEntry_WFI: WFI进入,任意外部中断退出*/PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFI)
执行完这一句后就能进入停止模式,进入STOP模式后程序就会停在这个位置不会再往下跑了,当我们通过外部中断来退出STOP模式后,程序开始从当前位置的下一句运行起来。
3.退出STOP模式
这里我们通过一个外部中断来退出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.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_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;//PA10GPIO_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 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//子优先级3NVIC_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); //初始化串口1USART_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 RXEXTI_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;//外部中断线10NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级2, NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道NVIC_Init(&NVIC_InitStructure);}/*外部中断*/void EXTI15_10_IRQHandler(void){EXTI_ClearITPendingBit(EXTI_Line10); //清除LINE 10 上的中断标志位}
这里我们初始化了串口1,然后初始化了外部中断的线10 对应串口1的接收引脚PA10,当我们通过串口工具发送数据时就会产生一个外部中断,从而来退出STOP模式
二、RTC实时时钟
1.简介
实时时钟是一个独立的定时器。RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后,RTC的设置和时间维持不变。 ● 可编程的预分频系数:分频系数最高为220。 ● 32位的可编程计数器,可用于较长时间段的测量。 ● 2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟 频率的四分之一以上)。 ● 可以选择以下三种RTC的时钟源: ─ HSE时钟除以128; ─ LSE振荡器时钟; ─ LSI振荡器时钟(详见6.2.8 308/754 节RTC时钟)。 ● 2个独立的复位类型: ─ APB1接口由系统复位; ─ RTC核心(预分频器、闹钟、计数器和分频器)只能由后备域复位(详见6.1.3节)。 ● 3个专门的可屏蔽中断: ─ 闹钟中断,用来产生一个软件可编程的闹钟中断。 ─ 秒中断,用来产生一个可编程的周期性中断信号(最长可达1秒)。 ─ 溢出中断,指示内部可编程计数器溢出并回转为0的状态。2.RTC实时时钟的使用
RTC可以使用外部时钟源,即在低功耗模式下依然能够正常运行
在第一节中讲了外部中断来退出STOP模式,那是需要我们主动去操作来产生一个外部中断,而RTC可以定时来自动产生外部中断从而退出STOP模式
从简介中我们知道RTC有三个专门的中断,我们常用的就是秒中断和闹钟中断,这里闹钟中断发生时会产生一个外部中断,我们可以从STM32中文参考手册中外部中断事件章节得知
3.配置RTC
下面直接上代码
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哦,也就是说现在是5SRTC_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闹钟为外部中断17EXTI_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哦,,也就是说现在是11SRTC_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;}}}
在主函数中我们上电就进入STOP模式,这时候程序不会进到while里去而是停在了Enter_Stop_Mode(); 这一行,我们配置了RTC闹钟中断为10s发送一次,也就是说等待10s后就退出STOP模式进到while里去