实时时钟的代码步骤
第一步:注意事项开启BKP和PWR的时钟,使能BKP和RTC的访问。
第二步:启动RTC的时钟使用LSE作为系统时钟,因此要使用RCC模块里的函数开启LSE的时钟,LSE时钟默认是关闭的。
第三步:配置RTCCLK这个数据选择器,指定LSE为RTCCLK
第四步:调用注意事项当中的等待函数和等待上一次操作完成。
第五步:配置预分频器,给重装寄存器一个合适的值,保证输出给计数器的频率是1Hz
第六步:配置CNT的值,给RTC一个初始时间。
第七步:如果需要闹钟的话还可以配置闹钟,需要中断还可配置中断。
所需库函数
<RCC.h>
void RCC_LSEConfig(uint8_t RCC_LSE); //配置LSE外部低速时钟,用来启动LSE的时钟
void RCC_LSICmd(FunctionalState NewState); //配置LSI内部低速时钟(外部时钟不管用了可以使用内部时钟)
void RCC_RTCCLKConfig(uint32_t RCC_RTCCLKSource); //RTCCLK配置,用来选择RTCCLK的时钟源即数据选择器
void RCC_RTCCLKCmd(FunctionalState NewState); //启动RTCCLK
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG); //获取标志位,启动LSE之后等待该标志位为1的时候时钟才算启动完成,工作稳定。
<RTC.h>
void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState); //配置中断输出
void RTC_EnterConfigMode(void); //进入配置模式 CNF位变为1
void RTC_ExitConfigMode(void); //退出配置模式 CNF位变为0
uint32_t RTC_GetCounter(void); //获取计数器CNT的值
void RTC_SetCounter(uint32_t CounterValue); //写入CNT计数器的值
void RTC_SetPrescaler(uint32_t PrescalerValue); //写入预分频器,会写入到预分频器的重装寄存器当中。
void RTC_SetAlarm(uint32_t AlarmValue); //写入闹钟值
uint32_t RTC_GetDivider(void); //读取预分频器当中的余数寄存器。
void RTC_WaitForLastTask(void); //等待上次操作完成
void RTC_WaitForSynchro(void); //等待同步
//和标志位相关的函数。
FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG);
void RTC_ClearFlag(uint16_t RTC_FLAG);
ITStatus RTC_GetITStatus(uint16_t RTC_IT);
void RTC_ClearITPendingBit(uint16_t RTC_IT);
初始化函数
void MyRTC_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_BackupAccessCmd(ENABLE);
RCC_LSEConfig(RCC_LSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForSynchro();
RTC_WaitForLastTask();
RTC_SetPrescaler(32768 - 1);
RTC_WaitForLastTask();
RTC_SetCounter(1672588795);
RTC_WaitForLastTask();
}
设置时间
time.h文件的注意事项
1.time_t 是unsigned int 再stm32当中这是无符号的32位整型
2.time() 用来获取系统时钟,在stm32当中没有系统时钟,所以在RTC程序当中这个函数用不了
3.stm32也不知道当前是处于那个时区,所以gmtime()和localtime()将秒计数器转化为日期时间的功能就没有区别了。这两个函数永远都是0时区,伦敦时间。mktime()也始终都是0时区,伦敦时间。换时区的话自己再加偏移就可以了。
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;
}
每次上电的时候时间都会被重置的问题
我们每次上电都意味着重新调用了初始化函数,这样时间就会被重置,因此要有选择的调用初始化函数,为了不被掉电重置我们就需要检测备用电源是否被断开了,备用电源断开了我们才会调用初始化函数。我们可以通过检测BKP当中的某个数据是否够被清零来进行判断。
主函数
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyRTC.h"
int main()
{
OLED_Init();
MyRTC_Init();
OLED_ShowString(1, 1, "Date:XXXX-XX-XX");
OLED_ShowString(2, 1, "Time:XX:XX:XX");
OLED_ShowString(3, 1, "CNT :");
OLED_ShowString(4, 1, "DIV :");
while(1)
{
MyRTC_ReadTime();
OLED_ShowNum(1, 6, MyRTC_Time[0], 4);
OLED_ShowNum(1, 11, MyRTC_Time[1], 2);
OLED_ShowNum(1, 14, MyRTC_Time[2], 2);
OLED_ShowNum(2, 6, MyRTC_Time[3], 2);
OLED_ShowNum(2, 9, MyRTC_Time[4], 2);
OLED_ShowNum(2, 12, MyRTC_Time[5], 2);
OLED_ShowNum(3, 6, RTC_GetCounter(), 10);
OLED_ShowNum(4, 6, (32767 - RTC_GetDivider()) / 32767.0 * 999, 10);
}
}