RTC概述
实时时钟是一个独立的定时器。 RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后, RTC的设置和时间维持不变。
系统复位后,对后备寄存器和RTC的访问被禁止,这是为了防止对后备区域(BKP)的意外写操作。执行以下操作将使能对后备寄存器和RTC的访问:
● 设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟
● 设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问。
配置步骤
配置前需查看是否第一次配置(BKP_DR1寄存器数据是否为先前设置的值)
- 使能时钟
配置参数存在BKP寄存器中 由PWR管理供电
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE);
- 使能BKP访问并初始化
PWR_BackupAccessCmd(ENABLE);
BKP_DeInit();
将BKP设置为缺省值
RTC_PRL、 RTC_ALR、 RTC_CNT和RTC_DIV寄存器仅能通过备份域复位信号复位
- 使能LSI时钟
40KHz好计算
RCC_LSICmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET && Flag<255)
{
Flag++;
delay_ms(10);
}
if(Flag == 255) return 1;
使能后还要等待晶振稳定 如果超时则返回错误代码1
之前的学习中不用使能HSE时钟是因为启动文件中已经将HSE选为系统时钟并使能,而当时钟被选为系统时钟时不能被关闭
- 选择RTC时钟源并使能RTC时钟
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
RCC_RTCCLKCmd(ENABLE);
- 配置RTC时钟参数
先进入配置模式等待同步完成
时钟公式Time=(PRL+1)/RTCCLK
RTC_EnterConfigMode();
RTC_WaitForSynchro();
RTC_WaitForLastTask();
RTC_SetPrescaler(40000-1);
RTC_WaitForLastTask();
RTC_ITConfig(RTC_IT_SEC,ENABLE);
RTC_WaitForLastTask();
RTC_SetTime(2020,8,16,21,41,55);
RTC_WaitForLastTask();
RTC_ExitConfigMode();
每次对寄存器操作完成后都要等待写入完成
- 初始化RTC定时器秒中断
NVIC_InitTypeDef NVIC_RTC_Sec_Init;
NVIC_RTC_Sec_Init.NVIC_IRQChannel=RTC_IRQn;
NVIC_RTC_Sec_Init.NVIC_IRQChannelCmd=ENABLE;
NVIC_RTC_Sec_Init.NVIC_IRQChannelPreemptionPriority=1;
NVIC_RTC_Sec_Init.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&NVIC_RTC_Sec_Init);
之后获取时间且向BKP_DR1寄存器写入标识码即可
- 非第一次启动
使能秒中断并初始化中断即可
RTC_WaitForSynchro();
RTC_WaitForLastTask();
RTC_EnterConfigMode();
RTC_ITConfig(RTC_IT_SEC,ENABLE);
RTC_ExitConfigMode();
RTC_NVIC_INIT();
RTC_GetTime();
- 中断服务函数
void RTC_IRQHandler()
{
if(RTC_GetITStatus(RTC_IT_SEC))
{
RTC_GetTime();
RTC_ClearITPendingBit(RTC_IT_SEC);
}
}
写入获取时间
写入时间
void RTC_SetTime(unsigned short year,unsigned char mon,unsigned char day,unsigned char hour,unsigned char min,unsigned char sec)
{
unsigned int count=0,num;
for(num=1970;num<year;num++)
{
if(IS_Leap_Year(num)) count+=31622400; //366*24*60*60
else count+=31536000; //365*24*60*60
}
for(num=0;num<(mon-1);num++)
{
count+=(unsigned int)month[num]*86400; //24*60*60
}
if(IS_Leap_Year(year) && mon>2) count+=86400;
count+=(unsigned int)(day-1)*86400;
count+=(unsigned int)hour*3600; //60*60
count+=(unsigned int)min*60;
count+=sec;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE);
PWR_BackupAccessCmd(ENABLE);
RTC_EnterConfigMode();
RTC_WaitForSynchro();
RTC_WaitForLastTask();
RTC_SetCounter(count);
RTC_WaitForLastTask();
RTC_ExitConfigMode();
}
获取时间
void RTC_GetTime()
{
unsigned int count=RTC_GetCounter(),time;
unsigned short TimeCount;
time=count/86400; //60*60*24
for(TimeCount=1970;time>=365;TimeCount++)
{
if(IS_Leap_Year(TimeCount))
{
if(time>=366)
{
time-=366;
}
}
else
{
time-=365;
}
}
Time.year=TimeCount;
for(TimeCount=1;time>=month[TimeCount];TimeCount++)
{
time-=month[TimeCount];
}
Time.mon=TimeCount;
Time.day=time;
time=count%86400;
Time.hour=time/3600;
time%=3600;
Time.min=time/60;
Time.sec=time%60;
get_week();
}
获取星期
void get_week()
{
Time.week = (Time.day + 1+ 2*Time.mon+3*(Time.mon+1)/5+Time.year+Time.year/4-Time.year/100+Time.year/400)%7;
}