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年
&RTC(实时时钟)&spm=1001.2101.3001.5002&articleId=162737911&d=1&t=3&u=d05ae2ba4b974286bf37915fe3b8a972)
251

被折叠的 条评论
为什么被折叠?



