STM32读写RTC内部时钟外设,设置和显示时钟

发布于:2024-04-28 ⋅ 阅读:(24) ⋅ 点赞:(0)

今天学习了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:

#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:

#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:

#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余数寄存器
	}
}

编译下载后,就能看到本次的实验结果了:


网站公告

今日签到

点亮在社区的每一天
去签到