/*-------------------------------------------------------------------------------------- * @file RTCSetup.c * @author ZhangJing * @version base on stm32f0x * @date 2015.09.11 * @brief RTC驱动 ---------------------------------------------------------------------------------------*/ #include "stm32f10x_gpio.h" #include "stm32f10x_rtc.h" #include "stm32f10x_pwr.h" #include "stm32f10x_bkp.h" #include "stm32f10x_exti.h" #include "TypeDefine.h" #include "DrawLCDGUI.h" #include "RTCSetup.h" #include "FM31256.h" //extern SpeakerWorkType speakerWorkStep;//蜂鸣器工作步骤 const uint8_t table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表 const uint8_t mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年月份日期表 static ErrorStatus HSEStartUpStatus;//HSE时钟启动状态 /********************************************************************************* * Function: Is_Leap_Year * Object: 判断是否是闰年 * 输入: 年份 * 输出: 该年是不是闰年。1是 0不是 * 备注: //月份 1 2 3 4 5 6 7 8 9 10 11 12 //闰年 31 29 31 30 31 30 31 31 30 31 30 31 //非闰年 31 28 31 30 31 30 31 31 30 31 30 31 **********************************************************************************/ uint8_t Is_Leap_Year(uint16_t year) { if( year % 4 == 0 ) //必须能被4整除 { if( year % 100 == 0 ) { if( year % 400 == 0 ) { return 1;//如果以00结尾,还能被400整除 } else { return 0; } } else { return 1; } } else { return 0; } } /********************************************************************************* * Function: RTC_Get_Week * Object: 获得现在是星期几 * 输入: 公历年月日 * 输出: 星期号 * 备注: 1、uint8_t yearH,yearL;年的高低字节计算 * 2、uint16_t temp2;作为计算的临时变量 **********************************************************************************/ uint8_t RTC_Get_Week(uint16_t year,uint8_t month,uint8_t day) { uint16_t temp2; uint8_t yearH,yearL; yearH = year / 100; yearL = year % 100; // 如果为21世纪,年份数加100 if( yearH > 19 ) { yearL += 100; } //所过闰年数只算1900年之后的 temp2 = yearL + yearL / 4; temp2 = temp2 % 7; temp2 = temp2 + day + table_week[month - 1]; if( yearL % 4 == 0 && month < 3 ) { temp2--; } return( temp2 % 7 ); } /********************************************************************************* * Function: RTC_Set * Object: 设置时钟 * 输入: 月份数据表 * 输出: 0:成功 其它:错误代码 * 备注: 把输入的时钟转换为秒钟 * 以1970年1月1日为基准 * 1970~2099年为合法年份 **********************************************************************************/ uint8_t RTC_Set(uint16_t syear,uint8_t smon,uint8_t sday,uint8_t hour,uint8_t min,uint8_t sec) { uint16_t t; uint32_t seccount=0; // RTC_ITConfig(RTC_IT_SEC, DISABLE); //关闭秒中断 // RTC_WaitForLastTask(); if( syear < 2015 || syear > 2099 ) { return 1;//syear范围1970-2099,此处范围设置为2015-2099 } for( t = 1970;t < syear;t++) //把所有年份的秒钟相加 { if( Is_Leap_Year( t ) ) { seccount += 31622400;//闰年的秒钟数 } else { seccount += 31536000;//平年的秒钟数 } } smon -= 1; for( t = 0;t < smon;t++ ) //把前面月份的秒钟数相加 { seccount += (uint32_t)mon_table[t] * 86400;//月份秒钟数相加 if( Is_Leap_Year( syear ) && ( t == 1 ) ) { seccount += 86400;//闰年2月份增加一天的秒钟数 } } seccount += (uint32_t)( sday - 1 ) * 86400;//把前面日期的秒钟数相加 seccount += (uint32_t)hour * 3600;//小时秒钟数 seccount += (uint32_t)min * 60;//分钟秒钟数 seccount += sec;//最后的秒钟数加上去 //设置时钟 // RCC->APB1ENR|=1<<28;//使能电源时钟 // RCC->APB1ENR|=1<<27;//使能备份时钟 // PWR->CR|=1<<8; //取消备份区写保护 // //以上三步是必须的! RTC_WaitForLastTask(); RTC_SetCounter(seccount); RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); PWR_BackupAccessCmd(ENABLE); RTC_WaitForLastTask(); RTC_ITConfig(RTC_IT_SEC, ENABLE); //打开秒中断 return 0; } /********************************************************************************* * Function: RTC_Get * Object: 得到当前时间 * 输入: 无 * 输出: 0:成功 其它:错误代码 * 备注: 1、static uint16_t daycnt=0;天数计算 * 2、uint32_t timecount=0;时间计算 * 3、uint32_t temp=0;临时变量 * 4、uint16_t temp1=0;临时变量 **********************************************************************************/ uint8_t RTC_Get(void) { static uint16_t daycnt=0; uint32_t timecount=0; uint32_t temp=0; uint16_t temp1=0; timecount = RTC_GetCounter(); // timecount=RTC->CNTH;//得到计数器中的值(秒钟数) // timecount<<=16; // timecount+=RTC->CNTL; temp = timecount / 86400; //得到天数(秒钟数对应的) if( daycnt != temp )//超过一天了 { daycnt = temp; temp1 = 1970; //从1970开始 while( temp >= 365 ) { if( Is_Leap_Year( temp1 ) )//是闰年 { // if( temp >= 366 ) // { // temp -= 366;//闰年的秒钟数 // } // else // { // temp1++; // break; // } temp -= 366;//闰年的秒钟数 } else { temp -= 365; //平年 // temp1++; } temp1++; } displayTimeBuf.year = temp1;//得到年份 temp1 = 0; while( temp >= 28 )//超过了一个月 { if( Is_Leap_Year( displayTimeBuf.year ) && ( temp1 == 1 ) )//当年是不是闰年/2月份 { if( temp >= 29 ) { temp -= 29;//闰年的秒钟数 } else { break; } } else { if( temp >= mon_table[temp1] ) { temp -= mon_table[temp1];//平年 } else { break; } } temp1++; } displayTimeBuf.month = temp1 + 1;//得到月份 displayTimeBuf.date = temp + 1; //得到日期 } temp = timecount % 86400; //得到秒钟数 displayTimeBuf.hour = temp / 3600; //小时 displayTimeBuf.minute = ( temp % 3600 ) / 60; //分钟 displayTimeBuf.seconds = ( temp % 3600 ) %60; //秒钟 //displayTimeBuf.week = RTC_Get_Week( displayTimeBuf.year,displayTimeBuf.month,displayTimeBuf.date );//获取星期 return 0; } /********************************************************************************* * Function: RTCInit * Object: RTC计时功能初始化 * 输入: 无 * 输出: 无 * 备注: uint16_t waitForOscSource;临时计算变量 **********************************************************************************/ void RTCInit(void) { uint16_t waitForOscSource; //在BKP后备寄存器1中,存储了一个特殊字符0xA5A5 //第一次上电或后备电源掉电后,该寄存器数据丢失 //表明RTC数据丢失,需重新配置 if( BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5 ) { //重新配置RTC /* Enable PWR and BKP clocks */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); /* Allow access to BKP Domain */ PWR_BackupAccessCmd(ENABLE); /* Reset Backup Domain */ BKP_DeInit(); /* Enable LSE */ RCC_LSEConfig(RCC_LSE_ON); for( waitForOscSource = 0;waitForOscSource < 5000;waitForOscSource++ ) { } // /* Wait till LSE is ready */ while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET); /* Select LSE as RTC Clock Source */ RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); /* Enable RTC Clock */ RCC_RTCCLKCmd(ENABLE); /* Wait for RTC registers synchronization */ RTC_WaitForSynchro(); // /* Wait until last write operation on RTC registers has finished */ // RTC_WaitForLastTask(); // // /* Enable the RTC Second */ // RTC_ITConfig(RTC_IT_SEC, ENABLE); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); /* Set RTC prescaler: set RTC period to 1sec */ RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */ /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); //配置完成后,向后备电源写特殊字符0xA5A5 BKP_WriteBackupRegister(BKP_DR1, 0xA5A5); RTC_Set(2015,01,01,0,0,0);//默认时间 } else { //若后备电源没有掉电,则无需重新配置RTC //这里我们可以利用RCC_GetFlagStatus()函数查看本次复位类型 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); for( waitForOscSource = 0;waitForOscSource < 5000;waitForOscSource++ ); if(RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET) { //这是上电复位 } else if(RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET) { //这是外部RESET管脚复位 } //清除RCC中复位标志 RCC_ClearFlag(); //虽然RTC模块不需要配置,且掉电后依靠后备电源依然运行 //但是每次上电后,还是要使能RTCCLK??????? //RCC_RTCCLKCmd(ENABLE); //等待RTC时钟与APB1时钟同步 //RTC_WaitForSynchro(); //使能秒中断 RTC_ITConfig(RTC_IT_SEC, ENABLE); //等待操作完成 RTC_WaitForLastTask(); } return; } /********************************************************************************* * Function: SystemTimeUpdate * Object: 系统时间更新 * 输入: 无 * 输出: 无 * 备注: 1、通过 TaskSchedulerFlag.timeReadFlag标志位来判断是否更新时间 * 2、每读取一次时间进行一次时间显示界面的刷新 **********************************************************************************/ void SystemTimeUpdate( void ) { if( TaskSchedulerFlag.timeReadFlag == TASK_FLAG_SET )//时间读取 { if( realTimeData.stateRun != Poweroff ) { //获取当前时间 if( speakerWorkStep != emSpeakerNoneWork) return; GetCurDateTimeFromRTC(); if( emCurrentPicture == TimeDisp )//更新显示时间页面 { emDisplayPicture = TimeDisp; } TaskSchedulerFlag.timeReadFlag = TASK_FLAG_CLEAR; } } } /********************************************************************************* * Function: RTC_NVIC_Config * Object: RTC中断配置 * 输入: 无 * 输出: 无 * 备注: NVIC_InitTypeDef NVIC_InitStructure; 中断配置结构体 **********************************************************************************/ void RTC_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; //RTC全局中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //先占优先级1位,从优先级3位 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能该通道中断 NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定参数初始化外设NVIC寄存器 } /********************************************************************************* * Function: RTC_Alarm_Configuration * Object: RTC闹钟初始化 * 输入: 无 * 输出: 无 * 备注: 启动时钟、配置LSI做RTC时钟、设置预分频40000得到1Hz * 设置运行时间WORK_TIMES **********************************************************************************/ void RTC_Alarm_Configuration(void) { /* Enable PWR and BKP clocks */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); /* Allow access to BKP Domain */ PWR_BackupAccessCmd(ENABLE); /* Reset Backup Domain */ BKP_DeInit(); /* RTC clock source configuration ----------------------------------------*/ /* Enable the LSI OSC */ RCC_LSICmd(ENABLE); /* Wait till LSI is ready */ while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) { } /* Select the RTC Clock Source */ RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); /* Enable the RTC Clock */ RCC_RTCCLKCmd(ENABLE); /* Wait for RTC registers synchronization */ RTC_WaitForSynchro(); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); /* 使能RTC闹钟中断*/ RTC_ITConfig(RTC_IT_ALR, ENABLE); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); /* Set RTC prescaler: set RTC period to 1sec */ RTC_SetPrescaler(40000); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); //中断配置 RTC_NVIC_Config(); //设置运行WORK_TIMES RTC_SetAlarm(RTC_GetCounter() + WORK_TIMES); RTC_WaitForLastTask(); } /********************************************************************************* * Function: StopMode_Configuration * Object: 停止模式RTC闹钟初始化 * 输入: 无 * 输出: 无 * 备注: 启动时钟、配置LSI做RTC时钟、设置预分频40000得到1Hz **********************************************************************************/ void StopMode_Configuration(void) { EXTI_InitTypeDef EXTI_InitStructure; /* Enable PWR and BKP clocks */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); /* Allow access to BKP Domain */ PWR_BackupAccessCmd(ENABLE); /* Reset Backup Domain */ BKP_DeInit(); /* RTC clock source configuration ----------------------------------------*/ /* Enable the LSI OSC */ RCC_LSICmd(ENABLE); /* Wait till LSI is ready */ while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) { } /* Select the RTC Clock Source */ RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); /* Enable the RTC Clock */ RCC_RTCCLKCmd(ENABLE); /* Wait for RTC registers synchronization */ RTC_WaitForSynchro(); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); /* Set RTC prescaler: set RTC period to 1sec */ RTC_SetPrescaler(40000); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); /* Configure EXTI Line17(RTC Alarm) to generate an interrupt on rising edge */ EXTI_ClearITPendingBit(EXTI_Line17); EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event; EXTI_InitStructure.EXTI_Line = EXTI_Line17; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); } /******************************************************************************* * Function Name : SYSCLKConfig_STOP * Description : Configures system clock after wake-up from STOP: enable HSE, PLL * and select PLL as system clock source. * Input : None * Output : None * Return : None *******************************************************************************/ void StopMode_SYSCLKConfig_STOP(void) { /* Enable HSE */ RCC_HSEConfig(RCC_HSE_ON); /* Wait till HSE is ready */ HSEStartUpStatus = RCC_WaitForHSEStartUp(); if(HSEStartUpStatus == SUCCESS) { /* Enable PLL */ RCC_PLLCmd(ENABLE); /* Wait till PLL is ready */ while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) { } /* Select PLL as system clock source */ RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); /* Wait till PLL is used as system clock source */ while(RCC_GetSYSCLKSource() != 0x08) { } } } /********************************************************************************* * Function: StopMode_EnterStopMode * Object: 进入停止模式 * 输入: uint32_t s,闹钟触发时间 * 输出: 无 * 备注: 设置待机时间并进入停止,闹钟事件发生自动退出stop模式 * 进入stop模式,闹钟唤醒后跳出函数运行 * RAM/GPIO状态保持 * s为单位秒 **********************************************************************************/ void StopMode_EnterStopMode(uint32_t s) { //设置待机时间 RTC_SetAlarm(RTC_GetCounter() + s); RTC_WaitForLastTask(); /* LDO不关掉,闹钟事件唤醒*/ PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFE); //唤醒后重新配置系统时钟 StopMode_SYSCLKConfig_STOP(); }