今天学习了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
-
- 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余数寄存器
- }
- }
编译下载后,就能看到本次的实验结果了: