实时时钟(RTC)
概述
RTC(real-time clock),实时时钟是一个独立的时钟,RTC被使用时提供连续不断的技术,用这个功能可以提供时间或者日历服务。
可以计数到2的32次方
1.1RTC使用的时钟
2.1可以使用的中断
2.1第一个中断可以选择到什么时间产生中断RTC_IT_ALR
2.2第二个可以每秒产生一个中断RTC_IT_SEC
2.3第三个是数据溢出时的中断信号,计数到了2的32次方RTC_IT_OW:
@arg RTC_IT_ALR: Alarm interrupt
@arg RTC_IT_SEC: Second interrupt
@arg RTC_IT_OW: Overflow interrupt
2.4RTC的中断配置函数
3.功能框图
可见RTC的核心是32位的可编程计数器,它的计数时机来自于APB1,我们来看一下APB1的时钟树
它可以有外部的LSE直接产生,或者HSE128分频,LSI内部震荡时钟。如果使用断电仍能计数的时钟,只能使用LSE。
它可以产生三个信号,分别是RTC_Second,RTC_OverFlow RTC_ALARM.
4.RTC的寄存器
4.1控制寄存器
4.1.1RTC控制寄存器高位(RTC_CRH)
这三个寄存器用来执行中断请求
我们相应的对应RTC的中断使能函数来看
fun 1.
void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState) //可以带入上边所所述的三个中断
{
if (NewState != DISABLE)
{
RTC->CRH |= RTC_IT;
}
else
{
RTC->CRH &= (uint16_t)~RTC_IT;
}
}
RTC控制寄存器的低位
我们来看5标志位对应的库函数
fun 2.
void RTC_WaitForLastTask(void)
{
/* Loop until RTOFF flag is set */
while ((RTC->CRL & RTC_FLAG_RTOFF) == (uint16_t)RESET)
{
}
}
对应来说,就是等待RTOFF操作完
4对应的库函数
fun 3
void RTC_EnterConfigMode(void)
{
/* Set the CNF flag to enter in the Configuration Mode */
RTC->CRL |= RTC_CRL_CNF;
}
fun 4
void RTC_ExitConfigMode(void)
{
/* Reset the CNF flag to exit from the Configuration Mode */
RTC->CRL &= (uint16_t)~((uint16_t)RTC_CRL_CNF);
}
fun 5
rsf寄存器
/**
* @brief Waits until the RTC registers (RTC_CNT, RTC_ALR and RTC_PRL)
* are synchronized with RTC APB clock.
* @note This function must be called before any read operation after an APB reset
* or an APB clock stop.
* @param None
* @retval None
*/
void RTC_WaitForSynchro(void)
{
/* Clear RSF flag */
RTC->CRL &= (uint16_t)~RTC_FLAG_RSF;
/* Loop until RSF flag is set */
while ((RTC->CRL & RTC_FLAG_RSF) == (uint16_t)RESET)
{
}
}
这里每当RTC_CNT寄存器和RTC_DIV寄存器由软件更新或清’0’时,此位由硬件置’1’。在APB1复位后,或APB1时钟停止后,此位必须由软件清’0’。要进行任何的读操作之前,用户程序必须等待这位被硬件置’1’,以确保RTC_CNT、RTC_ALR或RTC_PRL已经被同步。 0:寄存器尚未被同步; 1:寄存器已经被同步。
4.2 RTC预分频装载寄存器(RTC_PRLH/RTC_PRLL)
/**
* @brief Sets the RTC prescaler value.
* @param PrescalerValue: RTC prescaler new value.
* @retval None
*/
void RTC_SetPrescaler(uint32_t PrescalerValue)
{
/* Check the parameters */
assert_param(IS_RTC_PRESCALER(PrescalerValue));
RTC_EnterConfigMode();
/* Set RTC PRESCALER MSB word */
RTC->PRLH = (PrescalerValue & PRLH_MSB_MASK) >> 16;
/* Set RTC PRESCALER LSB word */
RTC->PRLL = (PrescalerValue & RTC_LSB_MASK);
RTC_ExitConfigMode();
}
设置分频数,如果晶振是32768HZ,如果prescalervalue是32768,那么计数周期就是1s
4.3
/**
* @brief Gets the RTC divider value.
* @param None
* @retval RTC Divider value.
*/
uint32_t RTC_GetDivider(void)
{
uint32_t tmp = 0x00;
tmp = ((uint32_t)RTC->DIVH & (uint32_t)0x000F) << 16;
tmp |= RTC->DIVL;
return tmp;
}
4.4RTC计数器寄存器 (RTC_CNTH / RTC_CNTL)
uint32_t RTC_GetCounter(void)
{
uint16_t tmp = 0;
tmp = RTC->CNTL;
return (((uint32_t)RTC->CNTH << 16 ) | tmp) ;
}
void RTC_SetCounter(uint32_t CounterValue)
{
RTC_EnterConfigMode();
/* Set RTC COUNTER MSB word */
RTC->CNTH = CounterValue >> 16;
/* Set RTC COUNTER LSB word */
RTC->CNTL = (CounterValue & RTC_LSB_MASK);
RTC_ExitConfigMode();
}
4.5RTC闹钟寄存器(RTC_ALRH/RTC_ALRL)
/**
* @brief Sets the RTC alarm value.
* @param AlarmValue: RTC alarm new value.
* @retval None
*/
void RTC_SetAlarm(uint32_t AlarmValue)
{
RTC_EnterConfigMode();
/* Set the ALARM MSB word */
RTC->ALRH = AlarmValue >> 16;
/* Set the ALARM LSB word */
RTC->ALRL = (AlarmValue & RTC_LSB_MASK);
RTC_ExitConfigMode();
}
5.RTC使用
head file
#ifndef _USER_RTC_H
#define _USER_RTC_H
/*头文件*/
#include "stm32f10x.h"
/*宏定义*/
//选择时钟源
#define RTC_CLOCK_SOURCE_LSE
//设置备份的寄存器
#define RTC_BKP_DXR BKP_DR1
//写入到备份寄存器的数据
#define RTC_BKP_DATA 0xa5a5
//北京地区的时间差
#define TIME_ZOOM (8)
/*变量*/
struct rtc_time {
int rtc_sec;
int rtc_min;
int rtc_hour;
int rtc_day;
int rtc_mon;
int rtc_year;
int rtc_wday;
};
/*函数*/
uint8_t RTC_Configure(uint8_t is_set,uint32_t time);
void RTC_GetYear(void);
void RTC_GetMonth(void);
void RTC_GetDay(void);
void RTC_GetHour(void);
void RTC_GetMinute(void);
void RTC_GetSecond(void);
void RTC_GetTime(void);
#endif
source file
#include "user_rtc.h"
/**
*名称:RTC获取系统时间函数
*作者:陈冲
*时间:2018年12月
*版本:1.0
*更新:1.0:无
**/
//定义的到具体时间的时间
//定义的到具体时间的时间
static uint32_t all_second = 0; //读取寄存器的时间
static uint32_t year_second = 0; //计算到今年的1月1日 00:00是多少秒
static uint32_t month_second = 0; //计算到今年的某月的1日 00:00是多少秒
static uint32_t day_second = 0; //计算到今天的00:00是多少秒
static uint32_t hour_second = 0; //计算到今天这个小时00是多少秒
static uint32_t minute_second = 0; //计算到今天这个小时的这个分钟00是多少秒
//定义系统时间变量
struct rtc_time systime =
{
0,0,0,1,1,2000,0
};
/*
*name:void RTC_GetYear()
*fun :获取当前年份
*para:void
*ret :void
*/
void RTC_GetYear()
{
year_second = 0;
for(systime.rtc_year = 1970;year_second<=all_second-366*60*60*24;systime.rtc_year ++)
{
if((systime.rtc_year %4 == 0 && systime.rtc_year %100!= 0)||systime.rtc_year%400 == 0)
year_second+= 366*60*60*24;
else
year_second+= 365*60*60*24;
}
}
/*
*name:void RTC_GetMonth()
*fun :获取当前月份
*para:void
*ret :void
*/
void RTC_GetMonth()
{
month_second = 0;
for(systime.rtc_mon = 1;month_second <=all_second - year_second-31*24*60*60;systime.rtc_mon++)
{
switch(systime.rtc_mon)
{
case 1:case 3:case 5:case 7:case 8:case 10:case 12: month_second+=31*24*60*60;break;
case 4:case 6:case 9:case 11: month_second+=30*24*60*60;break;
case 2: if((systime.rtc_year%4 == 0 && systime.rtc_year %100!= 0)||systime.rtc_year%400 == 0) month_second+=29*24*60*60; else month_second+=28*24*60*60;break;
}
}
}
/*
*name:void RTC_GetDay()
*fun :获取当前日
*para:void
*ret :void
*/
void RTC_GetDay()
{
day_second = 0;
for(systime.rtc_day = 1;day_second<= all_second - year_second - month_second - 24*60*60 ;systime.rtc_day++)
{
day_second += 60*24*60;
}
}
/*
*name:void RTC_GetHour()
*fun :获取当前小时
*para:void
*ret :void
*/
void RTC_GetHour()
{
hour_second = 0;
for(systime.rtc_hour = 0;hour_second<= all_second - year_second - month_second - day_second -60*60;systime.rtc_hour++)
{
hour_second += 60*60;
}
systime.rtc_hour+=TIME_ZOOM;
}
/*
*name:void RTC_GetMinute()
*fun :获取当前分
*para:void
*ret :void
*/
void RTC_GetMinute()
{
minute_second = 0;
for(systime.rtc_min = 0;minute_second<= all_second - year_second - month_second - day_second -hour_second-60;systime.rtc_min++)
{
minute_second += 60;
}
}
/*
*name:void RTC_GetSecond()
*fun :获取当前秒,并刷新其他时间
*para:void
*ret :void
*/
void RTC_GetSecond()
{
systime.rtc_sec = all_second - year_second - month_second - day_second -hour_second -minute_second;
}
/*
*name:void RTC_GetSecond()
*fun :刷新所有时间时间
*para:void
*ret :void
*/
inline void RTC_GetTime()
{
all_second = RTC_GetCounter();
RTC_WaitForLastTask();
RTC_GetYear();
RTC_GetMonth();
RTC_GetHour();
RTC_GetDay();
RTC_GetMinute();
RTC_GetSecond();
}
/*
*name:uint8_t RTC_Configure(uint8_t is_set,uint32_t time)
*fun :RTC初始化配置函数
*para:is_set:如果要设置当前的时间戳,将其设置为1,下一次设置为0 time:当前的时间戳
*ret :1 :数据没有丢失,0:数据丢失
*/
uint8_t RTC_Configure(uint8_t is_set,uint32_t time)
{
NVIC_InitTypeDef NVIC_InitsStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitsStruct.NVIC_IRQChannel = RTC_IRQn;
NVIC_InitsStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitsStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitsStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitsStruct);
uint8_t flag = 1;
//检查数据是否丢失;
if(BKP_ReadBackupRegister(RTC_BKP_DXR) != RTC_BKP_DATA || is_set == 1 )
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP,ENABLE);
//允许访问backup区域
PWR_BackupAccessCmd(ENABLE);
//复位backup区域
BKP_DeInit();
//打开外部晶振
RCC_LSEConfig(RCC_LSE_ON);
//等待LSE准备好
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET){}
//使用外部晶振
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
//使能RTC
RCC_RTCCLKCmd(ENABLE);
//等待外部时钟同步
RTC_WaitForSynchro();
RTC_WaitForLastTask();
RTC_SetPrescaler(32767);
RTC_WaitForLastTask();
BKP_WriteBackupRegister(RTC_BKP_DXR,RTC_BKP_DATA);
RTC_WaitForLastTask();
RTC_SetCounter(time);
flag = 0;
}
RTC_ITConfig(RTC_IT_SEC,ENABLE);
RTC_GetTime();
return flag;
}
/*
*name:void RTC_IRQHandler(void)
*fun :RTC中断服务程序
*para:is_set:void
*ret :void
*/
/*
#include "rtc.h"
extern struct rtc_time systime;
void RTC_IRQHandler(void)
{
if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
{
// Clear the RTC Second interrupt
RTC_ClearITPendingBit(RTC_IT_SEC);
//RTC刷新时间
RTC_GetTime();
// Wait until last write operation on RTC registers has finished
RTC_WaitForLastTask();
}
}
*/