在实际应用中,会出现许多复位或者掉电的情况,下面提供了一种方法使即使是在掉电和复位事件发生时,仍旧可以利用低功耗模式继续对于实时时钟进行供电,保证时钟的正常运行!
//bsp_rtc.h #ifndef _BSP_RTC_H #define _BSP_RTC_H #include "misc.h" /*全局变量*/ uint8_t RTCInterruptFlag=0; //RTC 中断标志 uint32_t RTC_TimeNum=0; // 设置时间变量 uint16_t Year; uint8_t Month; uint8_t Day; /* RTC hardware init*/ void RTC_NVIconfigration(void); void RTC_configration(void); void RTC_Init(void); /*日历及时间输入*/ uint8_t RTC_InputTime(uint32_t border); uint32_t RTC_TimeCollate(void); void RTC_RxIntHandler(void); /*获得年月日*/ uint16_t GetYear(); uint8_t GetMonth(); uint8_t GetDay(); void CalenderSet(void); void CalenderCount(void); static uint8_t Choice_MonthDay(uint16_t temp_year,uint8_t temp_month); /*显示*/ void RTC_TimeDisplay(uint32_t TimeVar); /*测试*/ void Text_RTC(void); #endif/*_BSP_RTC_H*/
#include "bsp_rtc.h" #define RTCClockSource_LSE uint32_t TimeDisplay=0; // 用于显示测试 每次进入中断改变 /*************************************************************************************************** *\Function RTC_NVIconfigration() *\Description 设置RTC中断优先级 *\Parameter void *\Return void *\Note *\Log 2014年7月24日 ***************************************************************************************************/ void RTC_NVIconfigration(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //已经在bsp_usart.c进行了设置 /* Enable the RTC Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; //配置外部中断源(秒中断) NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } /*************************************************************************************************** *\Function RTC_configration(void) *\Description RTC配置函数 *\Parameter void *\Return void *\Note *\Log 2014年7月24日 ***************************************************************************************************/ void RTC_configration(void) { /* 使能 PWR 和 BKP 的时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); PWR_DeInit(); /* 允许访问BKP区域 */ PWR_BackupAccessCmd(ENABLE); /* 复位BKP */ BKP_DeInit(); #ifdef RTCClockSource_LSI /* 使能内部RTC时钟 */ RCC_LSICmd(ENABLE); /* 等待RTC内部时钟就绪 */ while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) { } /* 选择RTC内部时钟为RTC时钟 */ RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); #elif defined RTCClockSource_LSE /* 使能RTC外部时钟 */ RCC_LSEConfig(RCC_LSE_ON); /* 等待RTC外部时钟就绪 */ while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) { } /* 选择RTC外部时钟为RTC时钟 */ RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); #endif /* 使能RTC时钟 */ RCC_RTCCLKCmd(ENABLE); #ifdef RTCClockOutput_Enable /* Disable the Tamper Pin */ BKP_TamperPinCmd(DISABLE); /* To output RTCCLK/64 on Tamper pin, the tamper functionality must be disabled */ /* 使能在TAMPER脚输出RTC时钟 */ BKP_RTCCalibrationClockOutputCmd(ENABLE); #endif /* 等待RTC寄存器同步 */ RTC_WaitForSynchro(); /* 等待写RTC寄存器完成 */ RTC_WaitForLastTask(); /* 使能RTC秒中断 */ RTC_ITConfig(RTC_IT_SEC, ENABLE); /* 等待写RTC寄存器完成 */ RTC_WaitForLastTask(); /* 设置RTC预分频 */ #ifdef RTCClockSource_LSI RTC_SetPrescaler(31999); /* RTC period = RTCCLK/RTC_PR = (32.000 KHz)/(31999+1) */ #elif defined RTCClockSource_LSE RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */ #endif /* 等待写RTC寄存器完成 */ RTC_WaitForLastTask(); } /*************************************************************************************************** *\Function RTC_Init(void) *\Description RTC初始化 *\Parameter void *\Return void *\Note *\Log 2014年7月24日 ***************************************************************************************************/ void RTC_Init(void) { if (BKP_ReadBackupRegister(BKP_DR1)!=0x0229) { //printf("\n RTC not yet configured...."); RTC_configration(); printf("\n RTC实时时钟设置..."); /*日历设置操作*/ CalenderSet(); BKP_WriteBackupRegister(BKP_DR2,GetYear()); BKP_WriteBackupRegister(BKP_DR3,GetMonth()); BKP_WriteBackupRegister(BKP_DR4,GetDay()); /*实时时钟设置操作:*/ RTC_WaitForLastTask(); RTC_SetCounter(RTC_TimeCollate()); // 进行设置 RTC_WaitForLastTask(); BKP_WriteBackupRegister(BKP_DR1,0x0229); } else { if (RCC_GetFlagStatus(RCC_FLAG_PORRST)!=RESET) { printf("\r\n\n 掉电重启事件...."); } else if (RCC_GetFlagStatus(RCC_FLAG_PINRST)!=RESET) { printf("\r\n\n 外部复位事件...."); } printf("\r\n 等待时间同步...."); /*在备份寄存器中重新读出年月日*/ Year=BKP_ReadBackupRegister(BKP_DR2); Month=BKP_ReadBackupRegister(BKP_DR3); Day=BKP_ReadBackupRegister(BKP_DR4); /*在读取RTC寄存器时,RTC的APB1接口曾经处于禁止状态,则软件首先必须等待 RTC_CRL寄存器中的RSF 位(寄存器同步标志)被硬件置’1’ 。 注: RTC的 APB1 接口不受WFI和WFE等低功耗模式的影响。 */ RTC_WaitForSynchro(); // 同步 RTC_WaitForLastTask(); RTC_ITConfig(RTC_IT_SEC,ENABLE);// 使能秒中断 RTC_WaitForLastTask(); } RCC_ClearFlag(); } /////////硬件初始化//////////////////////////////////////////////////////////////// /*************************************************************************************************** *\Function GetYear() *\Description 获得年数 *\Parameter void *\Return uint16_t Year *\Note *\Log 2014年7月5日 * ***************************************************************************************************/ uint16_t GetYear() { return Year; } /*************************************************************************************************** *\Function GetMonth() *\Description 获得月数 *\Parameter void *\Return uint8_t Month *\Note *\Log 2014年7月5日 * ***************************************************************************************************/ uint8_t GetMonth() { return Month; } /*************************************************************************************************** *\Function GetDay() *\Description 获得月数 *\Parameter void *\Return uint8_t Day *\Note *\Log 2014年7月5日 * ***************************************************************************************************/ uint8_t GetDay() { return Day; } /*************************************************************************************************** *\Function Choice_MonthDay(uint16_t temp_year,uint8_t temp_month) *\Description 选择月份天数 *\Parameter uint16_t temp_year *\Parameter uint8_t temp_month *\Return Month_day *\Note *\Log 2014年7月5日 * ***************************************************************************************************/ static uint8_t Choice_MonthDay(uint16_t temp_year,uint8_t temp_month) { uint8_t Month_day=0; switch(temp_month) { case 1:case 3:case 5:case 7:case 8: case 10:case 12: Month_day=31; break; case 4: case 6: case 9: case 11: Month_day=30; break; case 2: if(temp_year%4==0&&temp_year%100!=0||temp_year%400==0) Month_day=29; else Month_day=28; default: break; } return Month_day; } /*************************************************************************************************** *\Function CalenderCount() *\Description 日历计数函数 *\Parameter void *\Return void *\Note *\Log 2014年7月5日 * ***************************************************************************************************/ void CalenderCount(void) { if(Day>0&&Day<Choice_MonthDay(Year,Month)) { Day++; } else { Month++; if(Month>12) { Month=1; Year++; } Day=1; } } /*************************************************************************************************** *\Function CalenderInput() *\Description 用户日历校对输入函数 *\Parameter void *\Return void *\Note *\Log 2014年7月5日 * ***************************************************************************************************/ uint8_t CalenderInput() { RTCInterruptFlag=0; RTC_TimeNum=0; RTC_ITConfig(RTC_IT_SEC,DISABLE); // 失能能秒中断 /* 防止第一个字符无法发出,关闭发送中断 */ if (USART_GetITStatus(USART2,USART_IT_TXE)!=RESET) { USART_ITConfig(USART2,USART_IT_TXE,DISABLE); } /*等待用户输入数据*/ while (!RTCInterruptFlag) { printf(""); } RTC_ITConfig(RTC_IT_SEC,ENABLE);// 失能能秒中断 return RTC_TimeNum; } /*************************************************************************************************** *\Function RTC_InputTime(uint32_t border) *\Description 用户时间校时输入函数 *\Parameter uint32_t border 边界大小 *\Return uint8_t *\Note *\Log 2014年7月24日 ***************************************************************************************************/ uint8_t RTC_InputTime(uint32_t border) { RTCInterruptFlag=0; RTC_TimeNum=0; RTC_ITConfig(RTC_IT_SEC,DISABLE); // 失能能秒中断 /* 防止第一个字符无法发出,关闭发送中断 */ if (USART_GetITStatus(USART2,USART_IT_TXE)!=RESET) { USART_ITConfig(USART2,USART_IT_TXE,DISABLE); } /*等待用户输入数据*/ while (!RTCInterruptFlag) { printf(""); // 个人理解,因为RTC时间设置和数据输入都是用的USAET2 这样做是为了防止优先级被占用,不加的话while将不起作用 } printf("\n您键入的数值是:"); if (RTC_TimeNum > border) { printf("\n\r请键入0到%d之间的数字", border); return 0xFF; } RTC_ITConfig(RTC_IT_SEC,ENABLE);// 失能能秒中断 return RTC_TimeNum; } /*************************************************************************************************** *\Function void RTC_RxIntHandler() *\Description RTC中断处理函数 *\Parameter void *\Return void *\Note *\Log 2014年7月24日 * 放在中断处理中 ***************************************************************************************************/ void RTC_RxIntHandler(void) { uint32_t temp_data; RTCInterruptFlag=1; if (USART_GetFlagStatus(USART2,USART_IT_RXNE)!=RESET) { temp_data=(USART_ReceiveData(USART2)); RTC_TimeNum=(temp_data>>4)*10+(temp_data&0x0F); // 设置时间变量 // printf("当前的temp_data %d",RTC_Num); } if (USART_GetITStatus(USART2,USART_IT_TXE)!=RESET) { USART_ITConfig(USART2,USART_IT_TXE,DISABLE); } } ///////////以上为底层函数////////////////////////////////////////////////////////////////////////////////////////// /*************************************************************************************************** *\Function CalenderSet(void) *\Description 日历设置函数 *\Parameter void *\Return void *\Note 用于串口输入 *\Log 2014年7月5日 * ***************************************************************************************************/ void CalenderSet(void) { printf("\n=========设置年月日=================:"); printf("\n请输入年份:"); Year=(uint16_t)CalenderInput()*100; Year=Year+CalenderInput(); printf("\n您输入的年份是:%d",Year); printf("\n\n请输入月份:"); Month=CalenderInput(); printf("\n您输入的月份是:%d",Month); printf("\n\n请输入日期:"); Day=CalenderInput(); printf("\n您输入的日期是:%d",Day); } /**************************************************************************** * 名 称:uint32_t RTC_TimeCollate(void) * 功 能:时间校正函数 * 入口参数:无 * 出口参数:uint32_t * 说 明:用于串口输入 * 调用方法: ****************************************************************************/ uint32_t RTC_TimeCollate(void) { uint32_t Tmp_HH = 0xFF, Tmp_MM = 0xFF, Tmp_SS = 0xFF; printf("\r\n==============时间设置========================="); printf("\r\n 请输入小时:"); while (Tmp_HH == 0xFF) { Tmp_HH = RTC_InputTime(23); } printf(": %d", Tmp_HH); printf("\r\n 请输入分钟:"); while (Tmp_MM == 0xFF) { Tmp_MM = RTC_InputTime(59); } printf(": %d", Tmp_MM); printf("\r\n 请输入秒数:"); while (Tmp_SS == 0xFF) { Tmp_SS = RTC_InputTime(59); } printf(": %d", Tmp_SS); /* 返回保存在RTC计数寄存器里的值 */ return((Tmp_HH*3600 + Tmp_MM*60 + Tmp_SS)); } /**************************************************************************** * 名 称:void RTC_TimeDisplay(uint32_t TimeVar) * 功 能:显示当前时间 * 入口参数:无 * 出口参数:无 * 说 明: * 调用方法: ****************************************************************************/ void RTC_TimeDisplay(uint32_t TimeVar) { uint32_t THH = 0, TMM = 0, TSS = 0; /* 计算小时 */ THH = TimeVar/3600; /* 计算分钟 */ TMM = (TimeVar % 3600)/60; /* 计算秒 */ TSS = (TimeVar % 3600)% 60; printf("\n%d年 %d月 %d日 ",Year,Month,Day); printf("Time: %0.2d:%0.2d:%0.2d\r\n",THH, TMM, TSS); } ///////////以上为应用层函数///////////////////////////////////////////////////////////////// /*************************************************************************************************** *\Function RTC_text(void) *\Description 时间显示 *\Parameter void *\Return void *\Note *\Log 时间 * ***************************************************************************************************/ void RTC_text(void) { printf("\n\r"); while (1) { /* 秒更新发生 */ if(TimeDisplay == 1) { /* 显示当前时间 */ RTC_TimeDisplay(RTC_GetCounter()); TimeDisplay = 0; } } }
/******************************************************************************* * Function Name : RTC_IRQHandler * Description : This function handles RTC global interrupt request. * Input : None * Output : None * Return : None *******************************************************************************/ void RTC_IRQHandler(void) { if(RTC_GetITStatus(RTC_IT_SEC) != RESET) //读取秒中断状态 { RTC_ClearITPendingBit(RTC_IT_SEC); //清除秒中断标志 /* 时钟更新标志置位 */ TimeDisplay = 1; RTC_WaitForLastTask(); //等待上一次对RTC寄存器的写操作是否已经完成 if(RTC_GetCounter() == 0x0001517F) //当前时间是23:59:59时 复位为0:0:0 { RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); PWR->CR|=1<<8; //取消备份区写保护 RTC_EnterConfigMode(); //允许配置 RTC_WaitForLastTask(); //等待上一次对RTC寄存器的写操作是否已经完成 RTC_SetCounter(0x0); //写入复位值 RTC_WaitForLastTask(); //等待上一次对RTC寄存器的写操作是否已经完成 CalenderCount(); BKP_WriteBackupRegister(BKP_DR2,GetYear()); BKP_WriteBackupRegister(BKP_DR3,GetMonth()); BKP_WriteBackupRegister(BKP_DR4,GetDay()); } else if(RTC_GetCounter() > 0x0001517F) //当再次上电后计数值超过0x00015180, 复位为当前值取模0x00015180。 { RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); PWR->CR|=1<<8; //取消备份区写保护 RTC_EnterConfigMode(); //允许配置 RTC_WaitForLastTask(); //等待上一次对RTC寄存器的写操作是否已经完成 RTC_SetCounter(RTC_GetCounter()%0x0001517F); //写入复位值 RTC_WaitForLastTask(); //等待上一次对RTC寄存器的写操作是否已经完成 CalenderCount(); BKP_WriteBackupRegister(BKP_DR2,GetYear()); BKP_WriteBackupRegister(BKP_DR3,GetMonth()); BKP_WriteBackupRegister(BKP_DR4,GetDay()); } } }