STM32F030CCT6实时时钟(RTC)配置总结

RTC的数据保存在后备域中,在主电源供电时,由VDD给RTC供电;当VDD掉电后,由备份域电源Vbat给RTC供电,在系统复位或者从待机模式唤醒后,RTC的设置会依然保持,时间继续走;当主电源VDD和Vbat都掉电时,备份域中的数据丢失。RTC的时钟源有LSE,HSE,LSI,当VDD掉电时,HSE和LSI都会有影响,所以一般选择LSE作为时钟源,另一个原因是精度比较高;LSI精度低,误差差不多1%。

在这里插入图片描述
在这里插入图片描述
stm32f030为最小化功耗,将预分频器氛围2个可编程预分频器,一个7位异步预分频,一个15位同步预分频,可将LSE时钟(刚好32768Hz = 2^ 15) 15分频得到,1hz的频率,即周期为1s的计数
(为降低功耗,建议将异步预分频值设置为最大值,即2^ 7 =128,将7位异步分频值设为128,15位同步分频值设为255)
f = 32.768/(127+1)/(255+1)=1Hz
在这里插入图片描述

typedef struct 
{
	u16 Year;
	u8 Month;
	u8 Day;
	u8 Week;
	u8 Hour;
	u8 Minute;
	u8 Second;
}_DateTime;
_DateTime SystemTime;
void RTC_Config(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);	//使能电源和后备接口时钟
	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器,以被允许访问RTC相关寄存器
	if(RTC_ReadBackupRegister(RTC_BKP_DR0) != 0x5a5a)
	{
		RCC_LSEConfig(RCC_LSE_ON);	//使能LSE
		while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);//等待LSE就绪
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);/	/外部低速时钟作为RTC时钟源,32.768MHz	
		SynchPrediv = 0xFF;		//255
		AsynchPrediv = 0x7F;	//127					32.768/(255+1)/(127+1)=1Hz
		RCC_RTCCLKCmd(ENABLE);			//使能RTC时钟
		RTC_WaitForSynchro();	//RTC区域时钟比APB时钟慢,访问前需要进行时钟同步,确保被读出来的RTC寄存器是正确的
		RTC_InitStructure.RTC_AsynchPrediv = AsynchPrediv;	//异步预分频值
		RTC_InitStructure.RTC_SynchPrediv = SynchPrediv;	//同步预分频值
		RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;
		while(RTC_Init(&RTC_InitStructure) == ERROR);
		SystemTime.Year = 2020;
		SystemTime.Month = 11;
		SystemTime.Day = 19;
		SystemTime.Week = 4;
		SystemTime.Hour = 13;
		SystemTime.Minute = 0;
		SystemTime.Second = 0;
		RTC_SetRTCTime(&SystemTime);
		DelayMs(50);
		RTC_GetRTCTime( &SystemTime);
		RTC_WriteBackupRegister(RTC_BKP_DR0, 0x5a5a);
	}
}

* Note	 : BIN = BCD/10 *16+BCD%10,如果RTC输入事件格式为BCD,而在读取时间使用BIN,则会导致
*			时分秒跳跃增加(9:9(跳跃)10:16,11:17...18:24,19:25(跳跃)20:32),秒数最大会变为
*			89(59读出来为0x59=89)
*************************************************************************************/	
u8 RTC_SetRTCTime(_DateTime *pDate)
{
	RTC_DateTypeDef RTC_DateStructure;
	RTC_TimeTypeDef RTC_TimeStructure;
	RTC_DateStructure.RTC_Year = pDate->Year - 2000;
	RTC_DateStructure.RTC_Month = pDate->Month;
	RTC_DateStructure.RTC_Date = pDate->Day;
	RTC_DateStructure.RTC_WeekDay = pDate->Week;
	RTC_SetDate(RTC_Format_BIN, &RTC_DateStructure);
	RTC_TimeStructure.RTC_H12 = RTC_H12_AM;
	RTC_TimeStructure.RTC_Hours = pDate->Hour;
	RTC_TimeStructure.RTC_Minutes = pDate->Minute;
	RTC_TimeStructure.RTC_Seconds = pDate->Second;
	RTC_SetTime(RTC_Format_BIN, &RTC_TimeStructure);
}





void RTC_GetRTCTime(_DateTime *DateStr)
{
	RTC_DateTypeDef RTC_DateStructure;
	RTC_TimeTypeDef RTC_TimeStructure;

	memset(&RTC_DateStructure, 0, sizeof(RTC_DateStructure));
	memset(&RTC_TimeStructure, 0, sizeof(RTC_TimeStructure));
	RTC_GetDate(RTC_Format_BIN, &RTC_DateStructure);
  	RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure);
	DateStr->Year = RTC_DateStructure.RTC_Year + 2000;
	DateStr->Month = RTC_DateStructure.RTC_Month;
	DateStr->Day = RTC_DateStructure.RTC_Date;
	DateStr->Week = RTC_DateStructure.RTC_WeekDay;
	DateStr->Hour = RTC_TimeStructure.RTC_Hours;
	DateStr->Minute = RTC_TimeStructure.RTC_Minutes;
	DateStr->Second = RTC_TimeStructure.RTC_Seconds;
	time2Stamp(DateStr);
}

void time2Stamp(_DateTime *pDate)
{
	u8 month;
	u16 i;
	u32 seccount = 0;
	
	if((pDate->Year < 1970) || (pDate->Year > 2099))	return;
	for(i = 1970; i < pDate->Year; i++)			//把所有年份的秒钟相加
	{
		if(Is_Leap_Year(i))
		{
			seccount += 31622400;			//闰年的秒钟数 366*24*60*60
		}
		else 
		{
			seccount += 31536000;			//平年的秒钟数 365*24*60*60
		}
	}
	month = pDate->Month - 1;
	for(i = 0; i < month; i++)				//把前面月份的秒钟数相加
	{
		seccount += (u32)mon_table[i] * 86400;	//月份秒钟数相加24*60*60
		if(Is_Leap_Year(pDate->Year) && i == 1)	seccount += 86400;		//闰年2月份增加一天的秒钟数
	}	
	seccount += (u32)(pDate->Day-1) * 86400;	//把前面日期的秒钟数相加
	seccount += (u32)pDate->Hour * 3600;		//小时秒钟数	
	seccount += (u32)pDate->Minute * 60;		//分钟秒钟数
	seccount += pDate->Second;					//最后的秒钟加上去
	TimeBase = seccount - 8 * 60 * 60;			//东8区
}


//判断是否是闰年 -----------------
u8 Is_Leap_Year(u16 year)
{
	if(((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
	{
		return 1;
	}
	return 0;
}

【注】
在系统复位之后,RTC寄存器会通过清除PWR_CR中的DBP位被保护,防止意外写入,可以通过置位PWR_CR中的DBP位使能访问RTC寄存器
在这里插入图片描述

RTC初始化及配置

在这里插入图片描述

在RTC域复位之后,所有的RTC寄存器会进入写保护状态,通过在RTC_WPR寄存器以此写入0xCA,0x53解除写保护(如果写入错误键值,会重新激活写保护)。系统复位不会影响写保护,只有RTC后备域复位时才会。
在这里插入图片描述

1.置RTC_ISR(初始化状态寄存器)初始化状态寄存器的INIT为1进入初始化模式,在此模式下,日历计数器会停止,写入的值允许更新
2.等待大约2个RTCCLK时钟(用于时钟同步),检测RTC_ISP寄存器INITF位是否为1,判断是否成功进入初始化模式
3.在RTC_PREP寄存器中写入两个预分频值,给日历计数器产生1hz时钟
4.将时间和日期值载入影子寄存器(RTC_TR和RTC_DR),通过RTC_CR的FMT位配置12或24小时制
5.清零RTC_ISR寄存器的INIT位退出初始化模式,在4个RTC时钟后,写入的日历值会被自动载入

初始完成后,日历开始计数,在读取日历值时,应该先检测
RTC_ISR的RSF是否为1

【注】
在这里插入图片描述

  • 当RTC_CR寄存器中BYPSHAD位为0时:
    日历值从影子寄存器中获取,每两个RTCCLK日历寄存器的值(RTC_SSR亚秒、RTC_TR时间、RTC_DR日期)被复制到影子寄存器,影子寄存器每RTCCLK时钟更新一次。另外注意APB时钟频率必须大于或等于RTC时钟频率的7倍(同步问题),否则必须读两次日历时间和日期寄存器,如果两次读取结果相同,表示读取正确,否则就要读第3次。每次日历寄存器的值被复制到影子寄存器时,RTC_ISR的RSF位会被置位。为保证三个值的连续性,读RTC_SSR或者RTC_TR会锁住影子寄存器,这样日期寄存器就不会被刷新,直到RTC_DR也被读取。为防止程序在小于2个RTCCLK周期内读取日历,RTC_ISR的RSF位必须在第一次读日历时清零。并且在下次读取RTC_SSR、RTC_TR、RTC_DR寄存器时,必须先等到RTC_ISR的RSF置1。

  • 当RTC_CR寄存器中BYPSHAD位为1时:
    日历值直接从日历计数器获取,因此不需要等RTC_ISR的RSF被置位。由于当设备进入停止模式时,影子寄存器的值不会被更新,所以这种模式在退出低功耗模式(停止或待机)时特别适用。但是这种情况下,如果在两次读寄存器之间不在一个RTCCLK时钟内,不同寄存器的值可能会不连续,(比如2020-3-31 23:59:59,如果前一个RTCCLK读取RTC_TR,时间是23点59分59秒,下一个RTCCLK读取RTC_DR,这时候日历值已经刷新,日期是2020年4月1,现象就是程序读取时间前一秒是2020-3-31 23:59:58,后一秒是2020-4-1 23:59:59)。程序必须读两次所有的寄存器,然后比较结果来确认数据是不是连续且正确。

【注】
复位BYPSHAD位为0,日历影子寄存器(RTC_SSR、RTC_TR、RTC_DR)和状态寄存器RTC_ISR会在系统复位之后复位到默认值,其余的RTC寄存器只有在RTC后备域复位时才会恢复到默认值,不受系统复位影响。

应用

在1s定时中断中调用直接调用RTC_GetRTCTime(&SystemTime)即可实时获得时间,但是如果读取时间间隔很短,就要注意以上问题,避免出现时间读取出错。
配合GSM模组就可以在模组注册上网络之后,获取网络同步时间,并设置到RTC中RTC_SetRTCTime(&M26Time);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值