图文概述:
代码:
MyRTC.c:
#include "stm32f10x.h" // Device header
#include <time.h>
/* 设置显示的初始的日期时间结构体 */
struct tm MainTime = {57, 59, 23, 9, 10, 2023}; //秒 分 时 日 月(偏移量-1) 年(从1970开始算,有偏移)
void MyRTC_SetTime(void);
/**
* @brief MyRTC_Init---对RTC进行初始化
* @param 无
* @retval 无
*/
void MyRTC_Init(void)
{
//1.开启PWR和BKP外设的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
//2.PWR使能BKP
PWR_BackupAccessCmd(ENABLE);
/*
判断备用电源有没有断电:若断电过则备用寄存器的值会清除,
就需要重新初始化,若没有断电过则不需要再进行初始化
(0xA5A5相当于一个标志,用来查看是否还存在备用寄存器内)
*/
if(BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)
{
//3.开启LSE时钟,并等待LSE时钟启动完成
RCC_LSEConfig(RCC_LSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET);
//4.选择RTCCLK时钟源
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//选择LSE作为RTCCLK的时钟源
RCC_RTCCLKCmd(ENABLE); //使能时钟
//5.调用等待函数(等待同步和等待上一次写入操作完成)---防止因为时钟不同步,产生BUG
RTC_WaitForSynchro();
RTC_WaitForLastTask();
//6.配置预分频器
RTC_SetPrescaler(32768 - 1); //为了让LSE的32.768kHz频率产生1Hz的频率
RTC_WaitForLastTask();//等待上一次写入操作完成
//7.设置初始化的时间
MyRTC_SetTime();
BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);
}
else
{
//调用等待函数(等待同步和等待上一次写入操作完成)---防止因为时钟不同步,产生BUG
RTC_WaitForSynchro();
RTC_WaitForLastTask();
}
}
/**
* @brief MyRTC_SetTime---设置时间(即对初始的日期时间结构体中的变量值转换为秒数存放在RTC计数寄存器)
* @param 无
* @retval 无
*/
void MyRTC_SetTime(void)
{
time_t time_cnt;
//1.将日期时间转换为秒数
MainTime.tm_year -= 1900; //年份需要减去1900偏移量
MainTime.tm_mon -= 1; //月份需要减去1偏移量
time_cnt = mktime(&MainTime) - 8 * 60 * 60;//mktime---日期时间转换为秒数(规范为北京东八区时间)
//2.设置秒数到RTC计数寄存器
RTC_SetCounter(time_cnt); //将秒数写入到RTC计数寄存器中
RTC_WaitForLastTask();//等待上一次写入操作完成
}
/**
* @brief MyRTC_ReadTime---读取数据(即读取RTC计数寄存器中的值并且转换为日期时间结构体中的变量值)
* @param 无
* @retval 无
*/
void MyRTC_ReadTime(void)
{
time_t time_cnt;
//1.获取RTC计数寄存器的秒数
time_cnt = RTC_GetCounter() + 8 * 60 * 60;//(规范为北京东八区时间)
//2.将秒数转换为日期时间
MainTime = *localtime(&time_cnt);
MainTime.tm_year += 1900; //年份需要加上1900偏移量
MainTime.tm_mon += 1; //月份需要加上1偏移量
}
MyRTC.h:
#ifndef __MYRTC_H
#define __MYRTC_H
#include <time.h>
extern struct tm MainTime;
void MyRTC_Init(void);
void MyRTC_SetTime(void);
void MyRTC_ReadTime(void);
#endif
main.c:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyRTC.h"
/*
显示实时时钟,只要VBT备用电源不断电,就可以一直计数下去
*/
int main(void)
{
OLED_Init();
OLED_ShowString(1, 1, " - -");
OLED_ShowString(2, 1, " : :");
OLED_ShowString(3, 1, "CNT:");
OLED_ShowString(4, 1, "DIV:");
MyRTC_Init();
while(1)
{
MyRTC_ReadTime();
OLED_ShowNum(1, 1, MainTime.tm_year, 4);
OLED_ShowNum(1, 6, MainTime.tm_mon, 2);
OLED_ShowNum(1, 9, MainTime.tm_mday, 2);
OLED_ShowNum(2, 1, MainTime.tm_hour, 2);
OLED_ShowNum(2, 4, MainTime.tm_min, 2);
OLED_ShowNum(2, 7, MainTime.tm_sec, 2);
//获取计数寄存器的值
OLED_ShowNum(3, 5, RTC_GetCounter(), 10);
//获取余数寄存器的值(重装初始值由RTC预分频寄存器设置相关,自减直至每次越出0则计数寄存器+1)
OLED_ShowNum(4, 5, RTC_GetDivider(), 10);
}
}
关键步骤:
1.开启PWR和BKP外设的时钟以及PWR使能BKP:
//1.开启PWR和BKP外设的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
//2.PWR使能BKP
PWR_BackupAccessCmd(ENABLE);
对应下图注意事项:
2. 开启LSE时钟,并等待LSE时钟启动完成。并且选择RTCCLK时钟源为LSE
//3.开启LSE时钟,并等待LSE时钟启动完成
RCC_LSEConfig(RCC_LSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET);
//4.选择RTCCLK时钟源
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//选择LSE作为RTCCLK的时钟源
RCC_RTCCLKCmd(ENABLE); //使能时钟
对应下图操作:
3. 调用等待函数(等待同步和等待上一次写入操作完成)---防止因为时钟不同步,产生BUG
//5.调用等待函数(等待同步和等待上一次写入操作完成)---防止因为时钟不同步,产生BUG
RTC_WaitForSynchro();
RTC_WaitForLastTask();
对应下图注意事项:
4. 配置预分频器
//6.配置预分频器
RTC_SetPrescaler(32768 - 1); //为了让LSE的32.768kHz频率产生1Hz的频率
RTC_WaitForLastTask();//等待上一次写入操作完成
对应下图操作:
5. 设置时间(即对初始的日期时间结构体中的变量值转换为秒数存放在RTC计数寄存器)
/**
* @brief MyRTC_SetTime---设置时间(即对初始的日期时间结构体中的变量值转换为秒数存放在RTC计数寄存器)
* @param 无
* @retval 无
*/
void MyRTC_SetTime(void)
{
time_t time_cnt;
//1.将日期时间转换为秒数
MainTime.tm_year -= 1900; //年份需要减去1900偏移量
MainTime.tm_mon -= 1; //月份需要减去1偏移量
time_cnt = mktime(&MainTime) - 8 * 60 * 60;//mktime---日期时间转换为秒数(规范为北京东八区时间)
//2.设置秒数到RTC计数寄存器
RTC_SetCounter(time_cnt); //将秒数写入到RTC计数寄存器中
RTC_WaitForLastTask();//等待上一次写入操作完成
}
对应下图操作:
补充:
在MyRTC_Init初始化函数中,需要判断备用是否断电,如果没有断电则每次复位时不需要重新设置时间,若备用电源断电过,则下次复位时,需要重新设置时间,这个时候就需要加个if条件判断,根据BKP的备份寄存器特性来完成,在BKP的备份寄存器存入一个数值(断电标志),若下一次复位时进行MyRTC_Init初始化函数,先检查BKP的备份寄存器中的数值(断电标志)是否还存在,若被清除,则证明备用电源断电过。
/*
判断备用电源有没有断电:若断电过则备用寄存器的值会清除,
就需要重新初始化,若没有断电过则不需要再进行初始化
(0xA5A5相当于一个标志,用来查看是否还存在备用寄存器内)
*/
if(BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)
{
//3.开启LSE时钟,并等待LSE时钟启动完成
RCC_LSEConfig(RCC_LSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET);
//4.选择RTCCLK时钟源
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//选择LSE作为RTCCLK的时钟源
RCC_RTCCLKCmd(ENABLE); //使能时钟
//5.调用等待函数(等待同步和等待上一次写入操作完成)---防止因为时钟不同步,产生BUG
RTC_WaitForSynchro();
RTC_WaitForLastTask();
//6.配置预分频器
RTC_SetPrescaler(32768 - 1); //为了让LSE的32.768kHz频率产生1Hz的频率
RTC_WaitForLastTask();//等待上一次写入操作完成
//7.设置初始化的时间
MyRTC_SetTime();
BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);
}
else
{
//调用等待函数(等待同步和等待上一次写入操作完成)---防止因为时钟不同步,产生BUG
RTC_WaitForSynchro();
RTC_WaitForLastTask();
}