12:STM32---RTC实时时钟

2023-09-21 22:20:48

目录

一:时间相关

1:Unix时间戳

2: UTC/GMT

3:时间戳转化

二:BKP

1:简历

2:基本结构

三: RTC

1:简历

2: 框图

3:RTC基本结构

4:RTC操作注意

四:案例

A:读写备份寄存器

1:连接图

2: 步骤

 3: 代码 

B:实时时钟

1:连接图

2:函数介绍 

3:代码


一:时间相关

1:Unix时间戳

        Unix 时间戳(Unix Timestamp)定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数,不考虑闰秒

        时间戳存储在一个秒计数器中,秒计数器为32位/64位的整型变量

        世界上所有时区的秒计数器相同,不同时区通过添加偏移来得到当地时间

2: UTC/GMT

         GMT(Greenwich Mean Time)格林尼治标准时间是一种以地球自转为基础的时间计量系统。它将地球自转一周的时间间隔等分为24小时,以此确定计时标准

         UTC(Universal Time Coordinated)协调世界时是一种以原子钟为基础的时间计量系统。它规定铯133原子基态的两个超精细能级间在零磁场下跃迁辐射9,192,631,770周所持续的时间为1秒。当原子钟计时一天的时间与地球自转一周的时间相差超过0.9秒时,UTC会执行闰秒来保证其计时与地球自转的协调一致

3:时间戳转化

        C语言的time.h模块提供了时间获取和时间戳转换的相关函数,可以方便地进行秒计数器、日期时间和字符串之间的转换

二:BKP

1:简历

        BKP(Backup Registers)备份寄存器

        BKP可用于存储用户应用程序数据。当VDD(2.0~3.6V)电源被切断,他们仍然由VBAT(1.8~3.6V)维持供电。当系统在待机模式下被唤醒,或系统复位或电源复位时,他们也不会被复位

        TAMPER引脚产生的侵入事件将所有备份寄存器内容清除

        RTC引脚输出RTC校准时钟、RTC闹钟脉冲或者秒脉冲

        存储RTC时钟校准寄存器 用户数据存储容量:     

                20字节(中容量和小容量)/ 84字节(大容量和互联型)

2:基本结构

        BKP处于后备区域,  后备区域的作用:  当VDD主电源掉电时,后备区域仍然可以由VBAT的备用电池供电;  当VDD主电源上电时,后备区域供电会由VBAT切换到VDD,  也就是主电源有电时,VBAT不会用到,这样可以节省电池电量

        BKP里主要有 : 数据寄存器、控制寄存器、状态寄存器和RTC时钟校准寄存器,       其中数据寄存器是主要部分,用来存储数据的,  每个数据奇存器都是16位的,   一个数据奇存器可以存2个字节,  对于小的来说里面有DR1、DR2、一直到、DR10一共10个数据寄存器;    对于大容量和互联型里面有DR11~DR42的数据寄存器

        可以从PC13位置的TAMPERS脚引入一个检测信号器, 当TAMPER产生上升沿或者下降沿时, 清除BKP所有的内容,以保证安全-----对应简历中的第三条

        时钟输出,  可以把RTC的相关时钟,  从PC13位置的RTC引脚输出出去,供外部使用,  其中,输出校准时钟时,  再配合这个校准寄存器,可以对RTC的误差进行校准

三: RTC

1:简历

        RTC(Real Time Clock)实时时钟

        RTC是一个独立的定时器,可为系统提供时钟和日历的功能 RTC和时钟配置系统处于后备区域,系统复位时数据不清零,VDD(2.0~3.6V)断电后可借助VBAT(1.8~3.6V)供电继续走时

        32位的可编程计数器,可对应Unix时间戳的秒计数器

        20位的可编程预分频器,可适配不同频率的输入时钟

        可选择三种RTC时钟源:     

                HSE时钟除以128(通常为8MHz/128)     

                LSE振荡器时钟(通常为32.768KHz)     

                LSI振荡器时钟(40KHz)

2: 框图

3:RTC基本结构

        3个时钟,选择一个当作RTCCLK,  之后RTCCLK先通过预分频器,对时钟进行分频.

        余数寄存器(DIV) : 是一个自减计数器,存储当前的计数值.

        重装寄存器 : 是计数目标,决定分频值, 分频之后,得到1Hz的秒计数信号,  通向32位计数器一秒自增一次

        3个信号可以触发中断,  分别是秒信号、计数器溢出信号和闹钟信号,  三个信号先通过中断输出控制,  三个信号先通过中断输出控制,  使能的中断才能通向NVIC

4:RTC操作注意

        执行以下操作将使能对BKP和RTC的访问:   

                 设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟     

                设置PWR_CR的DBP,使能对BKP和RTC的访问

        若在读取RTC寄存器时,RTC的APB1接口曾经处于禁止状态,则软件首先必须等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置1

        必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、RTC_ALR寄存器--------其实这个操作在库函数中, 每个写奇存器的函数,  它都自动帮我们加上了这个操作, 所以我们就不用再单独调用代码,进入配置模式了

        对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是1时,才可以写入RTC寄存器

四:案例

A:读写备份寄存器

1:连接图

2: 步骤

  1: 设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟     

  2: 设置PWR_CR的DBP,使能对BKP和RTC的访问

 3: 代码 

简单的实现BKP的功能 

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"

void Key_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

uint8_t Key_GetNum(void)
{
	uint8_t KeyNum = 0;
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
	{
		Delay_ms(20);
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
		Delay_ms(20);
		KeyNum = 1;
	}
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
	{
		Delay_ms(20);
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);
		Delay_ms(20);
		KeyNum = 2;
	}
	
	return KeyNum;
}


/*
 执行以下操作将使能对BKP和RTC的访问:     

        1: 设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟     

        2: 设置PWR_CR的DBP,使能对BKP和RTC的访问

*/
uint16_t ArrayWrite[]={0x1234,0x5678};
uint16_t ArrayRead[2];
uint16_t KeyNum;
int main(void)
{	
	Key_Init();
	OLED_Init();
	//1: 设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟     
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);
	//2: 设置PWR_CR的DBP,使能对BKP和RTC的访问
	PWR_BackupAccessCmd(ENABLE);
	
	OLED_ShowString(1,1,"W:");
	OLED_ShowString(3,1,"R:");
	
	while (1)
	{
		KeyNum=Key_GetNum();
		if (KeyNum==1)
		{
			ArrayWrite[0]++;
			ArrayWrite[1]++;
			BKP_WriteBackupRegister(BKP_DR1,ArrayWrite[0]);
			BKP_WriteBackupRegister(BKP_DR2,ArrayWrite[1]);
			OLED_ShowHexNum(1,3,ArrayWrite[0],4);
			OLED_ShowHexNum(1,8,ArrayWrite[1],4);

		}
		ArrayRead[0]=BKP_ReadBackupRegister(BKP_DR1);
		ArrayRead[1]=BKP_ReadBackupRegister(BKP_DR2);
		OLED_ShowHexNum(3,3,ArrayRead[0],4);
		OLED_ShowHexNum(3,8,ArrayRead[1],4);
	
	}
	
}

 执行以下操作将使能对BKP和RTC的访问:     

        1: 设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟     

        2: 设置PWR_CR的DBP,使能对BKP和RTC的访问

B:实时时钟

1:连接图

2:函数介绍 

在stm32f10x_rcc.h的文件中-----时钟相关的函数

void RCC_LSEConfig(uint8_t RCC_LSE);

void RCC_LSICmd(FunctionalState NewState);

void RCC_RTCCLKConfig(uint32_t RCC_RTCCLKSource);
 

void RCC_RTCCLKCmd(FunctionalState NewState)

RCC_LSEConfig : 配置外部低速时钟(LSE)

RCC_LSICmd : 配置内部低速时钟(LSI)

RCC_RTCCLKConfig: 这个函数用来选择RTCCLK的时钟源 ,  实际上就是配置PPT的数据选择器

RCC_RTCCLKCmd : 使能--开启或者关闭RTC时钟

时钟在选择完毕后 , 需要获取标志位,等待标志完成后在操作

	RCC_LSEConfig(RCC_LSE_ON);//选择外部低速时钟
	
	while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET);//LSE准备ok了

在stm32f10x_rcc.h的文件中-----获取标志位函数

FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG);

在stm32f10x_rtc.h的文件中-----进入RTC配置模式

void RTC_EnterConfigMode(void)

 这个函数作用: 置CRL的CNF为1,进入配置模式

        必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、RTC_ALR寄存器

在stm32f10x_rtc.h的文件中-----退出RTC配置模式

void RTC_ExitConfigMode(void)

作用 : 就是把CNF位清零 

在stm32f10x_rtc.h的文件中-----CNT计数器相关

uint32_t RTC_GetCounter(void)
 

void RTC_SetCounter(uint32_t CounterValue)

RTC_GetCounter :  获取RTC计数器值

RTC_SetCounter :  写入CNT的值

在stm32f10x_rtc.h的文件中-----预分频器

void RTC_SetPrescaler(uint32_t PrescalerValue)

 RTC_SetPrescaler : 写入预分频器的值----这个值会写入到预分频器的PRL重装寄存器中,  用来配置预分频器的分频系数

在stm32f10x_rtc.h的文件中-----写入闹钟的值

void RTC_SetAlarm(uint32_t AlarmValue)

在stm32f10x_rtc.h的文件中-----读取预分频器中的DIV余数寄存器

 uint32_t  RTC_GetDivider(void);

余数奇存器是一个自减计数器 ,  获取余数奇存器的值,一般是为了得到更细致的时间

在stm32f10x_rtc.h的文件中-----等待完成操作

void RTC_WaitForLastTask(void);

void RTC_WaitForSynchro(void);

RTC_WaitForLastTask : 等待上次操作完成 

        对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是1时,才可以写入RTC寄存器

 RTC_WaitForSynchro :  等待同步----清除RSF标志位,然后循环,直到RSF为1

        若在读取RTC寄存器时,RTC的APB1接口曾经处于禁止状态,则软件首先必须等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置1

3:代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MYRTC.h"

uint16_t MyRTC_Time[] = {2023, 1, 1, 23, 59, 55};
void MYRTC_init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
	PWR_BackupAccessCmd(ENABLE);
	
	if(BKP_ReadBackupRegister(BKP_DR1)!=0xA5A5)
	{
		//LSE的频率是32.768KHz,也就是32768Hz
		RCC_LSEConfig(RCC_LSE_ON);//选择外部低速时钟
		
		while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET);//LSE准备ok了
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//配置RTC时钟RTCCLK
		RCC_RTCCLKCmd(ENABLE);//开启RTC时钟
		
		RTC_WaitForLastTask();//等待上次操作完成 
		RTC_WaitForSynchro();//等待同步时钟
		
		//写预分频器
		RTC_SetPrescaler(32768-1);
		RTC_WaitForLastTask();
		
		RTC_SetCounter(1672588795); //写入CNT
		RTC_WaitForLastTask();
		
		BKP_WriteBackupRegister(BKP_DR1,0xA5A5);
	
	}
	else
	{
			
		RTC_WaitForLastTask();//等待上次操作完成 
		RTC_WaitForSynchro();//等待同步时钟
		
	}

	
}

void MYRTC_Settime(void)
{
	//内部的定义typedef unsigned int time_t; /* date/time in unix secs past 1-Jan-70 */	
	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; //mktimer日期类型的时间数据类型--转化为--秒计数器数据类型
	RTC_SetCounter(time_cnt);//写入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;
}




int main(void)
{
	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, RTC_GetDivider(), 10);
	}
}

更多推荐

lv5 嵌入式开发-2 exec函数族

目录1进程–exec函数族1.1exec函数族特点1.2进程–execl/execlp使用方法1.3进程–execv/execvp2进程–system3exec族要点演示掌握:exec函数族、system1进程–exec函数族执行程序,通孔ps-elf发现,父进程是bash。这意味着该进程是由一个bashshell中启

Stable Diffusion AI绘图使用记录

1、下载安装使用官方网站https://github.com/AUTOMATIC1111/stable-diffusion-webui跟着一步步安装就行(英文版的)2、真人转二次元下载控制插件ControlnetGitHub-Mikubill/sd-webui-controlnet:WebUIextensionforC

HTML5 游戏开发实战 | 五子棋

五子棋是一种家喻户晓的棋类游戏,它的多变吸引了无数的玩家。本章首先实现单机五子棋游戏(两人轮流下),而后改进为人机对战版。整个游戏棋盘格数为15×15,单击鼠标落子,黑子先落。在每次下棋子前,程序先判断该处有无棋子,有则不能落子,超出边界不能落子。任何一方有横向、竖向、斜向、反斜向连到5个棋子则胜利。五子棋游戏的运行界

python学习之【with语句】

前言上一篇文章​​python学习之【文件读写】​​​中我们学习了python当中的文件读写,这篇文章接着学习python中文件读写的with语句。了解with语句在很多场景中,通过使用with语句可以让我们可以更好地来管理资源和简化代码,它可以看做是对try…finally模式的简化。with语句的语法格式witho

Baichuan2大模型本地部署

作为今年九月份开源的一个中午大语言模型,Baichuan2已经在各个维度上取得了亮眼的结果,效果已经超过了当前火热的ChatGLM2-6B,可以通过自然语言交互的方式为你提供以下服务:提供知识:我可以回答各领域的问题,并提供准确的信息和知识,帮你解决问题或获取所需要的信息文本生成:我可以创作不同体裁的内容,激发你的灵感

Spring 中经典的 9 种设计模式

1、简单工厂BeanFactory。Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。2、工厂方法FactoryBean接口。3、单例模式Spring依赖注入Bean实例默认是单例的。4、适配器模式S

华为云云耀云服务器L实例评测|拉取创建canal镜像配置相关参数 & 搭建canal连接MySQL数据库 & spring项目应用canal初步

前言最近华为云云耀云服务器L实例上新,也搞了一台来玩,本篇博客介绍如何在华为云上部署canal的docker镜像,以及在spring项目中的初步应用。其他相关的华为云云耀云服务器L实例评测文章列表如下:初始化配置SSH连接&安装MySQL的docker镜像&安装redis以及主从搭建&7.2版本redis.conf配置

uniapp后台播放音频功能制作

在UniApp中,你可以使用uni.getRecorderManager()方法来创建一个录音管理器实例。但是,请注意,录音管理器并不直接用于后台音频播放功能,而是用于录制音频。如果想要在后台播放音频,你需要使用uni.getBackgroundAudioManager()。以下是一个示例,演示了如何在UniApp中使

Go 多版本管理工具

Go多版本管理工具文章目录Go多版本管理工具一、goget命令1.1使用方法:二、Goenv三、GVM(GoVersionManager)四、voidint/g4.1安装4.2冲突4.3使用在平时开发中,本地新旧项目并行开发的过程中,你大概率会遇到一个令人头疼的问题,如何同时使用两个不同版本的GolangRuntime

9.2.3.1 【MySQL】XDES Entry链表

当段中数据较少的时候,首先会查看表空间中是否有状态为FREE_FRAG的区,也就是找还有空闲空间的碎片区,如果找到了,那么从该区中取一些零碎的页把数据插进去;否则到表空间下申请一个状态为FREE的区,也就是空闲的区,把该区的状态变为FREE_FRAG,然后从该新申请的区中取一些零碎的页把数据插进去。之后不同的段使用零碎

ElasticSearch集群shard均衡策略

ES集群的rebalance和allocation功能,可以自动均衡集群内部数据、分配分片,保证各个节点间尽量均衡。但是,在高访问量或者节点宕机的情况下,大范围的rebalance会影响到集群性能。所以,调整好集群相关参数,是重中之重。1-shard分配策略集群分片分配是指将索引的shard分配到其他节点的过程,会在如

热文推荐