stm32_ota/RTC/rtc.c

412 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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 *setCalendarCalendar_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) ;
}