目录
-
RTC简介
实时时钟(RTC)是一个独立的定时器。RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后,RTC的设置和时间维持不变。
系统复位后,对后备寄存器和RTC的访问被禁止,这是为了防止对后备区域(BKP)的意外写操作。执行以下操作将使能对后备寄存器和RTC的访问:
● 设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟
● 设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问
-
RTC时钟框图
时钟可配置为
- HSE的128分频
- LSE
- LSI
-
RTC工作原理图
RTC由两个主要部分组成(参见上图)。
第一部分(APB1接口)用来和APB1总线相连。此单元还包含一组16位寄存器,可通过APB1总线对其进行读写操作(参见16.4节)。APB1接口由APB1总线时钟驱动,用来与APB1总线接口。
另一部分(RTC核心)由一组可编程计数器组成,分成两个主要模块。第一个模块是RTC的预分频模块,它可编程产生最长为1秒的RTC时间基准TR_CLK。RTC的预分频模块包含了一个20位的可编程分频器(RTC预分频器)。如果在RTC_CR寄存器中设置了相应的允许位,则在每个TR_CLK周期中RTC产生一个中断(秒中断)。第二个模块是一个32位的可编程计数器,可被初始化为当前的系统时间。系统时间按TR_CLK周期累加并与存储在RTC_ALR寄存器中的可编程时间相比较,如果RTC_CR控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。
-
RTC相关寄存器(详细配置过程参考STM32参考手册)
- RTC控制寄存器 (RTC_CRH, RTC_CRL)
- RTC预分频装载寄存器 (RTC_PRLH, RTC_PRLL)
- RTC预分频余数寄存器 (RTC_DIVH, RTC_DIVL)
- RTC计数器寄存器 (RTC_CNTH, RTC_CNTL)
- RTC闹钟寄存器 (RTC_ALRH ,RTC_ALRL)
RTC_CRH
RTC_CRL
RTC_PRLH/RTC_PRLL
RTC_DIVH / RTC_DIVL
RTC_CNTH / RTC_CNTL
RTC_ALRH/RTC_ALRL
-
部分代码分析
rtc初始化
void rtc_init()
{
u8 t;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP,ENABLE); //使能PWR电源和BKP后备区
//使能后备寄存器访问
PWR_BackupAccessCmd(ENABLE);
if(BKP_ReadBackupRegister(BKP_DR1) != 0x0002) /*@@*/
{
BKP_DeInit();
/********少代码**********/ /*已加*/
RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振 /******* 注意 : 内部代码关闭了RTC时钟使能!*******/
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&t<250) //检查指定的RCC标志位设置与否,等待低速晶振就绪
{
t++;
delay_ms(10);
}
if(t>=250)return;
//配置RTC时钟源
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForLastTask();
RTC_WaitForSynchro(); //等待RTC寄存器同步完成
RTC_EnterConfigMode(); //允许配置
RTC_ITConfig(RTC_IT_SEC|RTC_IT_ALR,ENABLE); //开启闹钟和秒中断
RTC_WaitForLastTask();
RTC_SetPrescaler(32767);
RTC_WaitForLastTask();
rtc_set_time(2018,12,1,19,2,30);
RTC_ExitConfigMode(); //关闭配置
}
else
{
/*******若已经配置过相应时间,1.则直接等待同步,ps: 只有同步时在可以读取寄存器的值 2. 开启相应中断 ************/
RTC_WaitForSynchro();
RTC_ITConfig(RTC_IT_SEC|RTC_IT_ALR,ENABLE);
RTC_WaitForLastTask();
}
rtc_nvic_config();
/********少代码*更新时间*******/ /*已加*/
rtc_updata_time();//更新时间
BKP_WriteBackupRegister(BKP_DR1,0x0002); /*@@*/ //*用于防止重复操作 0x0002 : 随机值*//
}
rtc时间设置函数
void rtc_set_time(u16 year,u8 month,u8 day,u8 hour,u8 minute,u8 second)
{
u32 value = 0;
u16 i;
if(year<1970 || year>2099) return ;
for(i=1970 ; i<year ; i++)
{
if( rtc_judge_year(i) )
value += 31622400; //闰年时间(s)
else
value += 31536000; //平年时间
}
for(i=0 ; i<(month-1) ; i++)
{
value += (u32)month_days[i] * 86400; //每个月对应的时间
if( rtc_judge_year(year) && i==1)
value += 86400;
}
value += (u32)(day-1)*86400;
value += (u32)hour*3600;
value += (u32)minute*60;
value += second;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP,ENABLE); //使能时钟 /*保证时钟使能 实际初始化已使能 程序无作用*/
PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问 /* ps ; 和RTC_EnterConfigMode()不同 RTC配置使能*/
RTC_SetCounter(value); /*设置计数器的值*/
RTC_WaitForLastTask();
}
rtc星期获取
/* 基姆拉尔森公式根据日期计算星期*/
u8 rtc_get_week(u16 year,u8 month,u8 day)
{
if(month <3)
{
month+= 12;
--year;
}
return (day+1+2*month+3*(month+1)/5+year+(year/4)-year/100+year/400) % 7;
}
-
附录
RTC实时时钟实验代码(基于正点原子战舰 : https://github.com/kks1234/STM32/tree/master/15.RTC%E6%97%B6%E9%92%9F