今天学习了STM32单片机的内部时钟外设,学会了更改内部时钟和提取时钟数值的操作,只要后备电池有电,该时钟就会一直走,时间不会复位,哪怕没有给单片机供电。好了,直接记录步骤吧:
首先初始化流程,和BKP一样,在使用RTC外设之前,同样得执行注意事项的的第一点:
第一步:开启PWR和BKP的时钟,使能PWR和BKP的访问
如果读后备寄存器不是写入的值(证明包括电池断过电)需要重写时间
第二步:启动RTC的时钟,使用RCC模块里的RCC_LSEConfig函数,开启LSE的时 钟。LSE 需要手动开启,不然用不了。
第三步:配置RTCCLK数据选择器,指定LSE为RTCCLK(这个函数也在RCC模块里面)
第四步:调用等待的函数(RTC模块里等待同步,等待上一次操作完成)
第五步:配置预分频器(RTC里给PRL重装寄存器一个分频值,确保输出频率是1Hz)
第六步:配置CNT的值(给RTC一个初始时间)需要闹钟的话,配置个闹钟;需要中断的 话, 配置中断。
否则是不需要重新写时间的:
第四步:调用等待的函数(等待同步,等待上一次操作完成)
写两个函数:
一个是设置时间,
一个是读取时间。
设置时间的函数:
声明存放时间戳的变量time_CNT
创建时间结构体的变量time_data
第一步:把我们数组指定的时间,填充到struct tm结构体里
向结构体赋值年份 减去偏移
向结构体赋值月份 减去偏移
向结构体赋值日份
向结构体赋值小时
向结构体赋值分钟
向结构体赋值秒钟
第二步:把结构体中的时间转换成时间戳
把结构体中的时间转换成时间戳
第三步:把时间戳写入到RTC中当做初始时间
把时间戳写入到RTC中当做初始时间
等待写入操作完成
读取时间的函数:
声明存放时间戳的变量time_CNT
创建时间结构体的变量time_data
第一步:读取时间戳放到时间戳变量中
第二步:把时间戳转换成时间,存放到结构体中
第三步:把结构体中的时间存放到我们指定的全局变量数组中。
取结构体赋值年份 +偏移
取结构体赋值月份 +偏移
取结构体赋值日份
取结构体赋值小时
取结构体赋值分钟
取结构体赋值秒钟
好了,写的我都有点恶心了,还是直接看源文件吧:
MyRTC.c:
cs
#include "stm32f10x.h" // Device header
#include "MyRTC.h"
#include <time.h>
uint16_t MyRTC_Time[] = {2024, 4, 24, 9, 33, 59}; //存储时间的数组
/*
首先初始化流程,和BKP一样,在使用RTC外设之前,同样得执行注意事项的的第一点:
第一步:开启PWR和BKP的时钟,使能PWR和BKP的访问
第二步:启动RTC的时钟,使用RCC模块里的RCC_LSEConfig函数,开启LSE的时钟。LSE需要手动开启,不然用不了。
第三步:配置RTCCLK数据选择器,指定LSE为RTCCLK(这个函数也在RCC模块里面)
第四步:调用等待的函数(RTC模块里等待同步,等待上一次操作完成)
第五步:配置预分频器(RTC里给PRL重装寄存器一个分频值,确保输出频率是1Hz)
第六步:配置CNT的值(给RTC一个初始时间)需要闹钟的话,配置个闹钟;需要中断的话,配置中断。
*/
void MyRTC_Init(void)
{
//第一步:开启PWR和BKP的时钟,使能PWR和BKP的访问
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_BackupAccessCmd(ENABLE);
if(BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5) //如果读后备寄存器不是写入的值(证明包括电池断过电)需要重写时间
{
//第二步:启动RTC的时钟,使用RCC模块里的RCC_LSEConfig函数,开启LSE的时钟。LSE需要手动开启,不然用不了。
RCC_LSEConfig(RCC_LSE_ON);
//等待LSE时钟启动完成
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET);
//第三步:配置RTCCLK数据选择器,指定LSE为RTCCLK(这个函数也在RCC模块里面)
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE); // 使能LSE的时钟
//第四步:调用等待的函数(等待同步,等待上一次操作完成)
RTC_WaitForSynchro();
RTC_WaitForLastTask();
//第五步:配置预分频器(RTC里给PRL重装寄存器一个分频值,确保输出频率是1Hz)
RTC_SetPrescaler(32768 - 1); // 32768Hz除以32768正好等于1
RTC_WaitForLastTask(); //等待写入操作完成
//第六步:配置CNT的值(给RTC一个初始时间)需要闹钟的话,配置个闹钟;需要中断的话,配置中断。
//下面两条在其他函数内执行,这里就不用执行了。
// RTC_SetCounter(1672588795);
// RTC_WaitForLastTask(); //等待写入操作完成
MyRTC_SetTime(); //设置时间
BKP_WriteBackupRegister(BKP_DR1, 0xA5A5); //BKP1后备寄存器写入0xA5A5
}
else //否则是不需要重新写时间的
{
//第四步:调用等待的函数(等待同步,等待上一次操作完成)
RTC_WaitForSynchro();
RTC_WaitForLastTask();
}
}
/*
写两个函数:
一个是设置时间,
一个是读取时间。
*/
// 设置时间的函数
void MyRTC_SetTime(void)
{
time_t time_CNT; //声明存放时间戳的变量time_CNT
struct tm time_date; //创建时间结构体的变量time_data
//第一步:把我们数组指定的时间,填充到struct tm结构体里
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); //把结构体中的时间转换成时间戳
//第三步:把时间戳写入到RTC中当做初始时间
RTC_SetCounter(time_CNT); //把时间戳写入到RTC中当做初始时间
RTC_WaitForLastTask(); //等待写入操作完成
}
//读取时间的函数
void MyRTC_ReadTime(void)
{
time_t time_CNT; //声明存放时间戳的变量time_CNT
struct tm time_date; //创建时间结构体的变量time_data
//第一步:读取时间戳放到时间戳变量中
time_CNT = RTC_GetCounter();
//第二步:把时间戳转换成时间,存放到结构体中
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; //取结构体赋值秒钟
}
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
主函数main.c:
cs
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "MyRTC.h"
int main(void)
{
OLED_Init(); //oled 屏幕初始化
MyRTC_Init(); //RTC初始化
OLED_ShowString(1,1,"Data:xxxx-xx-xx"); //1行1列显示日期
OLED_ShowString(2,1,"Time:xx:xx:xx"); //2行1列显示时间
OLED_ShowString(3,1,"CNT:"); //3行1列显示:CNT
OLED_ShowString(4,1,"DIV:"); //4行1列显示:DIV余数寄存器
while(1)
{
MyRTC_ReadTime(); //读取时间
OLED_ShowNum(1,6, MyRTC_Time[0],4); //1行6列显示:年份
OLED_ShowNum(1,11, MyRTC_Time[1],2); //1行11列显示:月份
OLED_ShowNum(1,14, MyRTC_Time[2],2); //1行14列显示:日子
OLED_ShowNum(2,6, MyRTC_Time[3],2); //2行6列显示:小时
OLED_ShowNum(2,9, MyRTC_Time[4],2); //2行9列显示:分钟
OLED_ShowNum(2,12, MyRTC_Time[5],2); //2行12列显示:秒钟
OLED_ShowNum(3,5, RTC_GetCounter(),10); //3行5列显示:CNT
OLED_ShowNum(4,5, RTC_GetDivider(),10); //4行5列显示:DIV余数寄存器
}
}
编译下载后,就能看到本次的实验结果了: