BKP(备份寄存器)&RTC(实时时钟)

BKP简介:
BKP(Backup Registers)备份寄存器
BKP 可用于存储用户应用程序数据。当 VDD(2.03.6v)电源被断开,他们仍由VBAT(1.83.6V)维持供电。当系统在待机模式下被唤醒,或系统复位或电源复位时,他们也不会被复位
TAMPER 引脚产生的侵入事件将所有备份寄存器内容清除
RTC 引脚输出 RTC 校准时钟、RTC 闹钟脉冲或者秒脉冲
存储 RTC 时钟校准寄存器
用户数据存储容量:
20 字节(中容量和小容量)/ 84 字节(大容量和互联型)

(本节使用的STM32F103C8T6是中容量单片机,所以只能存储20字节的数据,共有10个DR数据寄存器,每个寄存器能存储两个字节的数据)
这里BKP存储器和W25Q64的Flash存储器类似,都是用来存储数据的,但Flash的数据是真的掉电不丢失,而BKP的数据是需要VBAT接上备用电源来维持,这是BKP备份寄存器的特性。

配置BKP的基本结构
在这里插入图片描述
BKP是位于后备区的,每个DR寄存器都能存储2个字节数据,

初始化配置模板:

	RCC_APB1PeriphClockCmd (RCC_APB1Periph_PWR,ENABLE );
	RCC_APB1PeriphClockCmd (RCC_APB1Periph_BKP,ENABLE );
	
	//使能对BKP和RTC的访问
	PWR_BackupAccessCmd(ENABLE );		//备份域访问控制使能

上面代码对应下面图片注意事例:
在这里插入图片描述
要访问BKP,得先使能PWR和BKP的时钟,两者都是挂载在APB1上的设备
PWR_BackupAccessCmd(ENABLE);函数能使能对BKP和RTC的访问
接着使用BKP_WriteBackupRegister和BKP_ReadBackupRegister这两个函数就能实现写入BKP寄存器和读取BKP寄存器了。
例子:

BKP_WriteBackupRegister(BKP_DR1,ArrayWrite[0]);		//将数据写入DR1寄存器,每个寄存器能存储2个字节
ArrayRead[0]=BKP_ReadBackupRegister(BKP_DR1);

RTC简介:
在这里插入图片描述

我们一般使用LSE外部低速时钟作为RTC时钟源,该时钟源经过分频器就能输出1hz(一秒)的信号给RTC秒计数器

配置RTC基本结构:
在这里插入图片描述
这里的DIV余数寄存器与计数器的CNT是不同的。
1、该预分频器和计数器的工作流程:
首先把 PRL 的值装入 DIV 余数寄存器,这里每来 1 个 RTCCLK 时钟脉冲,DIV 寄存器就会自动减 1,当DIV寄存器减到零时就会输出 1 个秒脉冲(Second 信号,给 32 位 CNT 计数器 + 1),接着自动把 PRL 重装回 DIV,开始下一轮递减。
例子:这里RTCCLK选择LSE(频率为32.768khz)作为时钟源,那么PRL=32768-1(减1是因为把0算进去了),这样就实现了每1秒CNT加1。
2、该图的工作流程:
我们先使能LSE外部低速时钟,然后选择LSE作为RTC的时钟源,再配置RTC各个参数就可以了,本节不使用闹钟和中断的功能。(RTC的配置不像之前一样用结构体来配置,只需要选择外部时钟源,设置分频器的值就好了)

初始化配置模板:

	//配置RTCCLK时钟
	RCC_APB1PeriphClockCmd (RCC_APB1Periph_BKP,ENABLE );
	RCC_APB1PeriphClockCmd (RCC_APB1Periph_PWR,ENABLE );
	
	PWR_BackupAccessCmd(ENABLE );		//使能对BKP和RTC的访问
	
	RCC_LSEConfig(RCC_LSE_ON);		//开启LSE外部低速时钟,频率32.768khz
	while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)!=SET );		//等待LSE震荡时钟
	
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);		//选择时钟源,这里选择LSE
	RCC_RTCCLKCmd(ENABLE );		//使能时钟
	
	//这里调用是防止时钟不同步
	RTC_WaitForSynchro();		//等待同步函数
	RTC_WaitForLastTask();		//等待上一次写入完成

	
	//配置预分频器
	RTC_SetPrescaler(32768-1);	//PRL=RTCCLK-1,输出1Hz的时钟源给秒计数器
	RTC_WaitForLastTask();		//每次写入RCT寄存器都需要等待上一次写入完成
	
	RTC_SetCounter(time_cnt);		//设置一个初始时间,默认为零,在Time.h就是1970年
	RTC_WaitForLastTask();

RTC操作注意事项:
在这里插入图片描述
代码前三步和BKP初始化一样,使能对RTC的访问。
这里开启LSE时钟不像之前一样使用 RCC_APB1PeriphClockCmd 函数,而是RCC_LSEConfig(RCC_LSE_ON);接着还要判断LSE是否准备完成,用判断标志位的函数。
接着使用RCC_RTCCLKConfig选择RTC的时钟源,而且还要使能RTC。
注意事项里的第二点和第四点,用两个无参的函数就能完成。分别是RTC_WaitForSynchro()//等待同步函数和
RTC_WaitForLastTask()//等待上一次写入完成函数,注意在每次写操作后都要调用第二个函数。
接着设置分频器的值,我们选择LSE作为时钟源,频率为32.768KHz,所以分频值PRL=32768。

接着是配置实时时钟的代码:

uint16_t MyRTC_Time[]={2026,7,9,16,32,55};

void MyRCT_SetTime(void);

void MyRTC_Init(void)
{
	//配置RTCCLK时钟
	RCC_APB1PeriphClockCmd (RCC_APB1Periph_BKP,ENABLE );
	RCC_APB1PeriphClockCmd (RCC_APB1Periph_PWR,ENABLE );
	
	PWR_BackupAccessCmd(ENABLE );		//使能对BKP和RTC的访问
	
	RCC_LSEConfig(RCC_LSE_ON);		//开启LSE外部低速时钟,频率32.768khz
	while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)!=SET );		//等待LSE震荡时钟
	
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);		//选择时钟源,这里选择LSE
	RCC_RTCCLKCmd(ENABLE );		//使能时钟
	
	//这里调用是防止时钟不同步
	RTC_WaitForSynchro();		//等待同步函数
	RTC_WaitForLastTask();		//等待上一次写入完成

	
	//配置预分频器
	RTC_SetPrescaler(32768-1);	//PRL=RTCCLK-1,这里设置为一秒间隔不断自增
	RTC_WaitForLastTask();		//每次写入RCT寄存器都需要等待上一次写入完成
	
//	RTC_SetCounter(time_cnt);		//设置一个初始时间,默认为零,在Time.h就是1970年
//	RTC_WaitForLastTask();
	MyRCT_SetTime();
	
}

//把数组的时间转化为秒数,写入CNT中,实现时间自增
void MyRCT_SetTime(void)
{
	time_t time_cnt;		//存秒数
	struct tm time_date;		//秒计数器转化为日期时间
	
	if(BKP_ReadBackupRegister(BKP_DR1)!=0xA5A5)		//这里调用BKP备份寄存器是为了实现备用电源没有断开就不会时间重置
	{
		time_date.tm_year =MyRTC_Time[0]-1900;	// 填充结构体年份:数组是真实年份,tm_year要求偏移1900,所以减1900,不然就是你写入的年份加上1900
		time_date.tm_mon  =MyRTC_Time[1]-1;
		time_date.tm_mday  =MyRTC_Time[2];
		time_date.tm_hour =MyRTC_Time[3];
		time_date.tm_min  =MyRTC_Time[4];
		time_date.tm_sec  =MyRTC_Time[5];
		
		time_cnt=mktime(&time_date)-8*60*60;		//将日期时间转化为秒计数器,数组上写的是北京时间,所以这里要减8小时
		
		RTC_SetCounter(time_cnt);			//把指定的秒数写到CNT
		RTC_WaitForLastTask();
		BKP_WriteBackupRegister(BKP_DR1,0xA5A5);
	}
	else
	{
		RTC_WaitForSynchro();			
		RTC_WaitForLastTask();
	}

}

void MyRTC_ReadTime(void)
{
	time_t time_cnt;		//存秒数
	struct tm time_date;
	
	time_cnt=RTC_GetCounter()+8*60*60;
	
	time_date=*localtime(&time_cnt);		//*localtime(&time_cnt)将秒计数器转化为日期
	
	MyRTC_Time[0]=time_date.tm_year+1900;
	MyRTC_Time[1]=time_date.tm_mon+1;
	MyRTC_Time[2]=time_date.tm_mday;
	MyRTC_Time[3]=time_date.tm_hour ;
	MyRTC_Time[4]=time_date.tm_min;
	MyRTC_Time[5]=time_date.tm_sec;	
}

这里的流程就是设置时间和读取时间
设置时间:把MyRTC_Time[]全局数组的年月日时分秒转化为秒数,再放到Time_Cnt里。
读取时间:把读到的秒数转化为年月日时分秒放到MyRTC_Time[]全局数组里。

重要函数:
PWR_BackupAccessCmd(ENABLE ); //使能对BKP和RTC的访问
RCC_LSEConfig(RCC_LSE_ON); //开启LSE外部低速时钟,频率32.768khz
RCC_GetFlagStatus(RCC_FLAG_LSERDY); //读取 RCC 时钟状态标志位
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //选择时钟源
RCC_RTCCLKCmd(ENABLE ); //使能时钟
RTC_WaitForSynchro(); //等待同步函数
RTC_WaitForLastTask(); //等待上一次写入完成
RTC_SetPrescaler(); //配置预分频器
RTC_SetCounter(time_cnt); //设置一个初始时间,默认为零,在Time.h就是1970年

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值