RTC(Real Time Clock)实时时钟
RTC实时时钟本质上是一个独立的定时器。RTC模块拥有一组连续计数的32位无符号计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒 后,RTC的设置和时间维持不变,需要注意前提得有备用电源VBAT供电保持走时,即VDD(2.0~3.6V)断电后可借助VBAT(1.8~3.6V)供电继续走时。
参考手册给出的结构框图如下:
简单介绍流程,就是一个定时器根据时钟确定每秒钟增加多少秒,往往我们是用于看时间,也就是说对于实际分频过后的时钟要有为:1HZ,才能使得定时器每秒钟加1。我们可以通过time.h对时间戳/计数值处理来得到当前的日期时间。
1.时钟来源:我们实际选择时钟源,可选择三种RTC时钟源HSE时钟除以128(通常为8MHz/128) , LSE振荡器时钟(通常为32.768KHz) , LSI振荡器时钟(40KHz)三种
对照手册:
我们也可以通过时钟树看出:
2.RTC实际配置步骤:
1.开启RTC访问权:
对于stm32来说,实际正常运行不会启动RTC,因为RTC主要由备份电源VBAT供电使用,所以我们使用时候需要开启访问,而BKP和RTC在stm32当中是统一的,开启RTC就相当于开启BKP同样开启BKP也相当于开启RTC。
2.设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问,需要我们手动开启PWR的DBP位,这样才能对RTC写入操作。
**3.选择时钟源:**对于RTC来说,我们实际上LSE是专门用于RTC的时钟信号,对应外部晶振32.768KHZ。其他主要是作为当第一种无法使用的备份选择。本质上它主要是由备份电源VBAT供电。
4.等待时钟同步,以及写入完成:
注意:每一次写入操作都需要我们对等待写入完成,调用库等待函数,或者等待相应标志位RTOFF置0。
5.配置计数器CNT(如果需要余数寄存器也可以在此配置,不过写入后需要等待写入完成)
主要是写入当前时间戳,不知道可以参考之前时间戳讲解。
6.等待写入完成。
实际也可以使用寄存器,不过我使用的是库函数,寄存器可以参考手册如下:
实例代码:
我使用了两种,一种数组,一种时间戳,具体可以根据需要采用
方案一:时间戳:
MYRTC.h
cs
#ifndef __MYRTC_H__
#define __MYRTC_H__
extern struct tm tm_RTC;
void MyRTC_Init(void);
void RTC_SetTime(void);
void RTC_readtime(void);
#endif
MYRTC.c
cs
uint32_t RTC_time=1705582640;//此时此刻的时间戳
struct tm tm_RTC;
void RTC_SetTime(void);
void MyRTC_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);//开启PWR
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);//开启RTC时钟
PWR_BackupAccessCmd(ENABLE);;//设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问
RCC_LSEConfig(RCC_LSE_ON);//开启LSE
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)!= SET);
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//选择RTC时钟源:外部时钟源
RCC_RTCCLKCmd(ENABLE);//使能时钟
RTC_WaitForSynchro();//等待RTC_CNT, RTC_ALR and RTC_PRL同步
RTC_WaitForLastTask();//等待RTOFF置1,写操作完成
RTC_SetPrescaler(32768-1);//配置预分频器系数
RTC_WaitForLastTask();
RTC_SetTime();
RTC_WaitForLastTask();
BKP_WriteBackupRegister(BKP_DR1, 0xFFFF);
}
void RTC_SetTime(void)
{
RTC_SetCounter(RTC_time+8*60*60);
}
void RTC_readtime(void)
{
uint32_t Read_time=0;
Read_time=RTC_GetCounter();
RTC_WaitForLastTask();
tm_RTC=*localtime(&Read_time);
}
方案二:数组
MYRTC.h:
cs
#ifndef __MYRTC_H
#define __MYRTC_H
extern uint16_t MYRTC_Time[];
void MYRTC_Init(void);
void MYRTC_SetTime(void);
void MYRTC_ReadTime(void);
#endif
MYRTC.c:
cs
#include "stm32f10x.h" // Device header
#include <time.h>
uint16_t MYRTC_Time[] = {2024, 1, 18, 22, 53, 55};
void MYRTC_SetTime(void);
void MYRTC_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);
RCC_LSEConfig(RCC_LSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET);
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForSynchro();
RTC_WaitForLastTask();
RTC_SetPrescaler(32768 - 1);
RTC_WaitForLastTask();
MYRTC_SetTime();
}
void MYRTC_SetTime(void)
{
time_t time_cnt;
struct tm time_date;
time_date.tm_year = MYRTC_Time[0] - 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;
RTC_SetCounter(time_cnt);
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);
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;
}
时间的转化当中涉及一定偏移,不知道可以参考之前时间戳讲解。