412 lines
15 KiB
C
412 lines
15 KiB
C
#include "rtc.h"
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include "stm32f10x_pwr.h"
|
||
#include "stm32f10x_bkp.h"
|
||
#include "stm32f10x_rtc.h"
|
||
#include "stm32f10x_exti.h"
|
||
|
||
/*********应用层外部调用文件**************/
|
||
#include "systick.h"
|
||
#include "userport.h"
|
||
/*****************************************/
|
||
|
||
Calendar_u uCalendar ;
|
||
|
||
uint8_t const 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 void RTC_NVIC_Config(void)
|
||
* 功能说明: RTC时钟中断和RTC闹钟中断优先级管理
|
||
*************************************************************************************************/
|
||
static void RTC_NVIC_Config(void)
|
||
{
|
||
NVIC_InitTypeDef NVIC_InitStructure ;
|
||
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn ; //RTC中断
|
||
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = RTC_IRQn_PreemptionPriority ; //设置抢占优先级
|
||
NVIC_InitStructure.NVIC_IRQChannelSubPriority = RTC_IRQn_SubPriority ; //设置子优先级
|
||
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE ; //使能该通道中断
|
||
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
|
||
|
||
// NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn ; //RTCAlarm中断
|
||
// NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = RTCAlarm_IRQn_PreemptionPriority ; //设置抢占优先级
|
||
// NVIC_InitStructure.NVIC_IRQChannelSubPriority = RTCAlarm_IRQn_SubPriority ; //设置子优先级
|
||
// NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
|
||
// NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: RunResult RTC_Init(void)
|
||
* 外部引用: ErrorLogPrintf
|
||
* 功能说明: 初始化RTC功能块
|
||
* 出口参数: RunResult: 反映处理结果
|
||
* 说 明: 当LSE失效会启用LSI作为RTC时钟,LSI频率漂移大可能会导致走时不准的问题
|
||
*************************************************************************************************/
|
||
RunResult RTC_Init(void)
|
||
{
|
||
u8 temp=0;
|
||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
|
||
PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
|
||
if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050) //从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
|
||
{
|
||
BKP_DeInit(); //复位备份区域
|
||
RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振
|
||
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250) //检查指定的RCC标志位设置与否,等待低速晶振就绪
|
||
{
|
||
temp++;
|
||
Delay_Ms(10);
|
||
}
|
||
if(temp>=250)return RunErr;//初始化时钟失败,晶振有问题
|
||
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
|
||
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
|
||
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
|
||
RTC_WaitForSynchro(); //等待RTC寄存器同步
|
||
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
|
||
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
|
||
RTC_EnterConfigMode();/// 允许配置
|
||
RTC_SetPrescaler(32767); //设置RTC预分频的值
|
||
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
|
||
Calendar_u uCalendar2 ;
|
||
memcpy(uCalendar2.bytes, (const u8*)"2017-09-21 14:40:00", 19) ;
|
||
RTC_Set(&uCalendar2) ;
|
||
RTC_ExitConfigMode(); //退出配置模式
|
||
BKP_WriteBackupRegister(BKP_DR1, 0X5050); //向指定的后备寄存器中写入用户程序数据
|
||
}
|
||
else//系统继续计时
|
||
{
|
||
|
||
RTC_WaitForSynchro(); //等待最近一次对RTC寄存器的写操作完成
|
||
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
|
||
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
|
||
}
|
||
RTC_NVIC_Config();//RCT中断分组设置
|
||
//RTC_Get();//更新时间
|
||
return RunOK; //ok
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: void RTC_IRQHandler(void)
|
||
* 功能说明: RTC时钟中断
|
||
* 说 明: 每秒触发一次中断
|
||
*************************************************************************************************/
|
||
void RTC_IRQHandler(void)
|
||
{
|
||
if ( RTC_GetITStatus(RTC_IT_SEC) != RESET )//秒钟中断
|
||
{
|
||
RTC_ClearITPendingBit(RTC_IT_SEC); //清秒中断
|
||
RTC_Get(&uCalendar); //更新时间
|
||
}
|
||
if( RTC_GetITStatus(RTC_IT_ALR)!= RESET ) //闹钟中断
|
||
{
|
||
RTC_ClearITPendingBit(RTC_IT_ALR); //清闹钟中断
|
||
EXTI_ClearITPendingBit(EXTI_Line17);
|
||
}
|
||
RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW|RTC_IT_SEC); //清中断
|
||
RTC_WaitForLastTask() ;
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: void RTCAlarm_IRQHandler(void)
|
||
* 功能说明: RTC闹钟中断
|
||
* 说 明: RTC计数器到达RTC->ALR值时发生中断。 注意:RTC闹钟中断挂载在EXTI_Line17中断线上的,注意需要清标志位
|
||
*************************************************************************************************/
|
||
//void RTCAlarm_IRQHandler(void)
|
||
//{
|
||
// if( RTC_GetITStatus(RTC_IT_ALR)!= RESET ) //闹钟中断
|
||
// {
|
||
// EXTI_ClearITPendingBit(EXTI_Line17);
|
||
// RTC_ClearITPendingBit(RTC_IT_ALR); //清闹钟中断
|
||
// printf("\r\n*闹钟中断" ) ;
|
||
// }
|
||
///* Clear the EXTIL line 17 */
|
||
//}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: uint8_t CheckLeepYear(uint16_t year)
|
||
* 功能说明: 检查year是否为闰年
|
||
* 入口参数:
|
||
* @param uint16_t year:年份数值
|
||
* 出口参数:
|
||
* @param uint8_t:检查结果
|
||
@arg 1: year为闰年
|
||
* @arg 0: year为平年
|
||
* 说 明:
|
||
* 月份 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 CheckLeepYear(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 ;
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: uint8_t RTC_Set(Calendar_u *setCalendar)
|
||
* 功能说明: 设置RTC当前时间
|
||
* 入口参数:
|
||
* @param *setCalendar:Calendar_u类型共用体指针
|
||
* 出口参数:
|
||
* @param uint8_t:返回值:0,成功;其他:错误代码.
|
||
* 说 明:
|
||
*************************************************************************************************/
|
||
RunResult RTC_Set(Calendar_u *setCalendar) //
|
||
{
|
||
uint16_t t ;
|
||
u32 seccount = 0 ; //存储setCalendar日期计算出来的总秒钟数 初始化RTC计数器
|
||
uint16_t syear = (setCalendar->bytes[0]-'0')*1000+(setCalendar->bytes[1]-'0')*100+(setCalendar->bytes[2]-'0')*10+(setCalendar->bytes[3]-'0') ;
|
||
uint8_t smon = (setCalendar->bytes[5]-'0')*10 +(setCalendar->bytes[6]-'0') ;
|
||
uint8_t sday = (setCalendar->bytes[8]-'0')*10 +(setCalendar->bytes[9]-'0') ;
|
||
uint8_t hour = (setCalendar->bytes[11]-'0')*10 +(setCalendar->bytes[12]-'0') ;
|
||
uint8_t min = (setCalendar->bytes[14]-'0')*10 +(setCalendar->bytes[15]-'0') ;
|
||
uint8_t sec = (setCalendar->bytes[17]-'0')*10 +(setCalendar->bytes[18]-'0');
|
||
|
||
if( syear<1970 || syear>2099 )
|
||
return (RunErr);
|
||
|
||
for( t = 1970; t < syear; t++ ) //把所有年份的秒钟相加
|
||
{
|
||
if(CheckLeepYear(t))
|
||
seccount += 31622400; //闰年的秒钟数
|
||
else
|
||
seccount += 31536000; //平年的秒钟数
|
||
}
|
||
smon -= 1;
|
||
for( t=0; t<smon; t++ ) //把前面月份的秒钟数相加
|
||
{
|
||
seccount += (u32)mon_table[t]*86400; //月份秒钟数相加
|
||
if(CheckLeepYear(syear)&&t==1)
|
||
seccount += 86400; //闰年2月份增加一天的秒钟数
|
||
}
|
||
seccount += (u32)(sday-1)*86400 ; //把前面日的秒钟数相加
|
||
seccount += (u32)hour*3600 ; //小时秒钟数
|
||
seccount += (u32)min*60 ; //分钟秒钟数
|
||
seccount += sec ; //最后的秒钟加上去
|
||
|
||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
|
||
PWR_BackupAccessCmd(ENABLE); //使能RTC和后备寄存器访问
|
||
RTC_SetCounter(seccount); //设置RTC计数器的值
|
||
|
||
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
|
||
return (RunOK) ;
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: uint8_t RTC_Alarm_Set(uint16_t syear,uint8_t smon,uint8_t sday,uint8_t hour,uint8_t min,uint8_t sec)
|
||
* 功能说明: 设置RTC闹钟时间点
|
||
* 入口参数:
|
||
* @param1 syear: 闹钟时间点的年份
|
||
* @param2 smon: 闹钟时间点的月份
|
||
* @param3 sday: 闹钟时间点的天
|
||
* @param4 hour: 闹钟时间点的小时
|
||
* @param5 min: 闹钟时间点的分钟
|
||
* @param6 sec: 闹钟时间点的秒钟
|
||
* 出口参数:
|
||
* @param RunResult:返回值,返回函数运行结果.
|
||
* 说 明: 1970~2099年为合法年份
|
||
*************************************************************************************************/
|
||
RunResult RTC_Alarm_Set(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min,uint8_t sec)
|
||
{
|
||
uint16_t t ;
|
||
u32 seccount = 0 ;
|
||
if( syear<1970 || syear>2099 )
|
||
return (RunErr) ;
|
||
for( t=1970; t<syear; t++ ) //把所有年份的秒钟相加
|
||
{
|
||
if(CheckLeepYear(t))
|
||
seccount += 31622400 ; //闰年的秒钟数
|
||
else
|
||
seccount += 31536000 ; //平年的秒钟数
|
||
}
|
||
smon -= 1;
|
||
for( t=0; t<smon; t++ ) //把前面月份的秒钟数相加
|
||
{
|
||
seccount += (u32)mon_table[t]*86400 ; //月份秒钟数相加
|
||
if(CheckLeepYear(syear)&& (t==1) )
|
||
seccount += 86400 ; //闰年2月份增加一天的秒钟数
|
||
}
|
||
seccount += (u32)(sday-1)*86400 ; //把前面日期的秒钟数相加
|
||
seccount += (u32)hour*3600 ; //小时秒钟数
|
||
seccount += (u32)min*60; //分钟秒钟数
|
||
seccount += sec; //最后的秒钟加上去
|
||
|
||
// RCC_APB1PeriphClockCmd( RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE ) ; //使能PWR和BKP外设时钟
|
||
PWR_BackupAccessCmd( ENABLE ) ; //使能后备寄存器访问
|
||
RTC_EnterConfigMode() ; //RTC允许配置 CNT\ALR\PRL
|
||
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
|
||
RTC_SetAlarm(seccount);
|
||
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
|
||
RTC_ExitConfigMode(); //RTC退出配置 CNT\ALR\PRL
|
||
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
|
||
// PWR_BackupAccessCmd(DISABLE); //禁止后备寄存器访问
|
||
|
||
return (RunOK);
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: RunResult RTC_Get(Calendar_u *getCalendar)
|
||
* 功能说明: 设置RTC闹钟时间点
|
||
* 入口参数:
|
||
* @param1 *getCalendar: 指向Calendar_u类型数据的指针
|
||
* 出口参数:
|
||
* @param RunResult:返回值,返回函数运行结果.
|
||
* 说 明: RTC_Get(&uCalendar) ;
|
||
*************************************************************************************************/
|
||
RunResult RTC_Get(Calendar_u *getCalendar)
|
||
{
|
||
static uint16_t daycnt = 0 ; //static修饰 保证只有改变天数时才更新年月日
|
||
u32 timecount = 0 ;
|
||
u32 temp = 0 ;
|
||
uint16_t temp1 = 0 ;
|
||
vu8 hour;
|
||
vu8 min;
|
||
vu8 sec;
|
||
vu16 w_year;
|
||
vu8 w_month;
|
||
vu8 w_date;
|
||
vu8 week;
|
||
RTC_WaitForSynchro() ; //RTC读操作前等待
|
||
timecount = RTC_GetCounter() ;
|
||
temp = timecount/86400 ; //得到天数(秒钟数对应的)
|
||
if( daycnt != temp ) //超过一天了才会影响年月日
|
||
{
|
||
daycnt = temp;
|
||
temp1 = 1970; //从1970年开始
|
||
while( temp >= 365 )
|
||
{
|
||
if( CheckLeepYear(temp1) ) //是闰年
|
||
{
|
||
if( temp >= 366 )
|
||
temp -= 366 ; //闰年的秒钟数
|
||
else
|
||
{
|
||
temp1++ ;
|
||
break ;
|
||
}
|
||
}
|
||
else
|
||
temp -= 365 ; //平年
|
||
temp1++ ;
|
||
}
|
||
w_year = temp1 ; //得到年份
|
||
getCalendar->calendar.w_year[0] = w_year/1000+'0' ;
|
||
getCalendar->calendar.w_year[1] = (w_year%1000)/100+'0' ;
|
||
getCalendar->calendar.w_year[2] = ((w_year%1000)%100)/10 + '0' ;
|
||
getCalendar->calendar.w_year[3] = w_year%10 + '0' ;
|
||
temp1 = 0 ;
|
||
while( temp >= 28 ) //超过了一个月
|
||
{
|
||
if( CheckLeepYear(w_year)&&temp1==1 )//当年是不是闰年/2月份
|
||
{
|
||
if( temp >= 29 )
|
||
temp -= 29 ; //闰年的秒钟数
|
||
else
|
||
break ;
|
||
}
|
||
else
|
||
{
|
||
if( temp >= mon_table[temp1] )
|
||
temp -= mon_table[temp1] ;//平年
|
||
else
|
||
break ;
|
||
}
|
||
temp1++ ;
|
||
}
|
||
w_month = temp1+1 ; //得到月份
|
||
w_date = temp+1 ; //得到日期
|
||
getCalendar->calendar.w_month[0] = w_month/10+'0' ;
|
||
getCalendar->calendar.w_month[1] = w_month%10+'0' ;
|
||
getCalendar->calendar.w_date[0] = w_date/10+'0' ;
|
||
getCalendar->calendar.w_date[1] = w_date%10+'0' ;
|
||
}
|
||
temp = timecount%86400 ; //得到秒钟数
|
||
hour = temp/3600 ; //小时
|
||
min = (temp%3600)/60 ; //分钟
|
||
sec = (temp%3600)%60 ; //秒钟
|
||
getCalendar->calendar.hour[0] = hour/10+'0' ;
|
||
getCalendar->calendar.hour[1] = hour%10+'0' ;
|
||
getCalendar->calendar.min[0] = min/10+'0' ;
|
||
getCalendar->calendar.min[1] = min%10+'0' ;
|
||
getCalendar->calendar.sec[0] = sec/10+'0' ;
|
||
getCalendar->calendar.sec[1] = sec%10+'0' ;
|
||
// if(( hour == 0x15 )&& ( min == 0x00 )&&( sec == 0x00) ) //凌晨21:00:00设定闹钟
|
||
// {
|
||
// appConfigUnion.appFlashStruct.VolumeRank = 0x34 ;
|
||
// RTC_WaitForLastTask();
|
||
// randCount = rand()%1000+rand()%100+rand()%10 ;
|
||
// timecount = timecount+4*3600+randCount ;
|
||
// RTC_SetAlarm(timecount) ; //设置下次闹钟值
|
||
// RTC_ITConfig( RTC_IT_ALR, ENABLE ) ; //开闹钟中断
|
||
// }
|
||
// if(( hour == 0x07 )&& ( min == 0x00 )&&( sec == 0x00) ) //早上07:00:00恢复音量
|
||
// {
|
||
// Read_From_Flash(APP_FLASH_CONFIG_ADDR, appConfigUnion.appFlashBuffer, APPFLASHCONFIGLEN ) ; //恢复音量等级
|
||
// if( !IS_VolumeRank_ALL_PERIPH(appConfigUnion.appFlashStruct.VolumeRank)) //音量等级不合法的话直接设置为7级
|
||
// {
|
||
// appConfigUnion.appFlashStruct.VolumeRank = 0x37 ;
|
||
// Save_to_Flash(APP_FLASH_CONFIG_ADDR, appConfigUnion.appFlashBuffer, APPFLASHCONFIGLEN ) ;
|
||
// }
|
||
// }
|
||
week = RTC_Get_Week( w_year,w_month,w_date ) ;//获取星期
|
||
return (RunOK) ;
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: Ruint8_t RTC_Get_Week(uint16_t year, uint8_t month, uint8_t day)
|
||
* 功能说明: 获取现在是星期几
|
||
* 入口参数:
|
||
* @param1 year: 年分
|
||
* @param2 mon: 月份
|
||
* @param3 day: 日
|
||
* 出口参数:
|
||
* @param1 uint8_t: 星期几?
|
||
* @param RunResult:返回值,返回函数运行结果.
|
||
* 说 明: 输入公历日期得到星期(只允许1901-2099年)
|
||
*************************************************************************************************/
|
||
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 ;
|
||
if ( yearH>19 ) // 如果为21世纪,年份数加100
|
||
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) ;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|