12:STM32---RTC实时时钟

news2024/11/24 21:37:14

目录

一:时间相关

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);
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1029686.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何与Linamar Corp 建立EDI连接?

Linamar Corp(以下简称Linamar)是一家全球领先的汽车零部件制造商,总部位于加拿大。随着业务的不断扩展,Linamar 需要与其供应商、分销商和合作伙伴之间实现更高效的业务交流和数据共享。为了提高业务流程的自动化水平&#xff0c…

1.虚拟机无法连接网络,且无法ping通的问题解决

1.介绍 今天操作Jedis连接虚拟机的redis数据库时,连接不上,找了很多解决方案,都解决不了,最后发现是虚拟机的配置问题,虚拟机无法连接网络,且没有设置本机ip地址,所以ifconfig的根本就查不出ip…

C++之智能指针类型转换应用总结(二百二十九)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…

数据结构:线性表之-队列

目录 什么是队列? 详解: 功能介绍 代码实现 定义队列基本结构 1,初始化 2, 销毁 3,尾入数据 4,头出数据 5,取队头的数据 6,取队尾的数据 7,判断是否为空 8,计算队列中的元素 成品 Queue.h Queue.c test.c 队列的讲解将建立在…

Oracle使用遇到的问题

一、Navicat 连接Orcle提示 Oracle library is not loaded 1.前往官网下载客户端 http://www.oracle.com/technetwork/database/database-technologies/instant-client/downloads/index.html 2.选择与系统匹配的“Instant Client”和sqlpus。

App的专项测试

文章目录 App的专项测试都从哪个方面测试安装和卸载版本升级消息推送弱网测试兼容性测试交互性测试 App的专项测试都从哪个方面测试 在APP测试过程中,除了功能测试外,还需要进行一些专项测试来发现更为深层的问题,这些问题主要是针对某个特殊…

时空智友企业流程化管控系统文件存在任意文件上传漏洞 附POC

、 文章目录 时空智友企业流程化管控系统文件存在任意文件上传漏洞 附POC1. 时空智友企业流程化管控系统文件简介2.漏洞描述3.影响版本4.fofa查询语句5.漏洞复现6.POC&EXP7.整改意见8.往期回顾 时空智友企业流程化管控系统文件存在任意文件上传漏洞 附POC 免责声明&#…

【Redis】深入探索 Redis 的哨兵(Sentinel)机制原理,基于 Docker 模拟搭建 Redis 主从结构和哨兵分布式架构

文章目录 一、对 Redis Sentinel 的认识1.1 什么是 Redis Sentinel1.2 为什么要使用 Redis Sentinel1.2.1 主从复制问题1.2.2 人工恢复主节点故障 二、Redis Sentinel 原理剖析2.1 Redis Sentinel 架构2.2 Raft 算法和领袖节点2.3 哨兵节点2.4 故障检测2.5 故障切换2.6 监控和通…

Android调用相册并展示选中的图片

调用相册 //定义请求码int PICK_PHOTO_REQUEST 1234;int RESULT_CANCELED 0;//定义取消码//触发监听后调用相册findViewById(R.id.image_gallery).setOnClickListener(new View.OnClickListener() {Overridepublic void onClick(View view) {//创建一个意图并开启startActivi…

Visual Studio 2017 安装

C自学精简实践教程 目录(必读) 这篇文章会保证你第一次安装VS2017就成功运行Hello World! 下载Visual Studio Installer Gitee 下载 VS2017/vs2017_Community.exe CalmReason/VisualStudio - 码云 - 开源中国 (gitee.com) 百度云下载 链接:https://pan.baidu…

ArangoDB关键知识点汇总大全(不定时更新)

二、ArangoDB数据模型与索引 2.1 数据模型 ArangoDB的数据模型分为数据库(databse)、集合(collection)、文档(document),分别与RDBMS中的数据库、表、行对应。 数据类型包括:string、boolean、number、array、document/object Collection&#xff1a…

【详细图文】Windows下安装RustRover和配置Rust环境

前言 Rust已经火了挺长时间了,连微软的Windows内核都用它来重新改写,可想而知其厉害之处。之前有看过Rust的教程,但一直没有去尝试。今天看到JetBrains出了Rust 专用的IDE:RustRover。作为JetBrains的粉丝,决定进行一…

【强化学习】02—— 探索与利用

文章目录 1. 探索与利用2. 探索策略3. 多臂老虎机3.1. 形式化描述3.2. 估计期望奖励3.3. 懊悔regret函数 4. 贪心策略和 ϵ − g r e e d y \epsilon-greedy ϵ−greedy策略5. 积极初始化6. 显示地考虑动作的价值分布7. UCB上置信界算法8. 汤普森采样算法总结参考 1. 探索与利用…

Hive 的权限管理

目录 ​编辑 一、Hive权限简介 1.1 hive中的用户与组 1.1.1 用户 1.1.2 组 1.1.3 角色 1.2 使用场景 1.2.1 hive cli 1.2.2 hiveserver2 1.2.3 hcatalog api 1.3 权限模型 1.3.1 Storage Based Authorization in the Metastore Server 1.3.2 SQL Standards Based …

【Vue.js】vue-cli搭建SPA项目并实现路由与嵌套路由---详细讲解

一,何为SPA SPA(Single Page Application)是一种 Web 应用程序的开发模式,它通过使用 AJAX 技术从服务器异步加载数据,动态地更新页面内容,实现在同一个页面内切换不同的视图,而无需整页刷新 1.…

部署Kafka

kafka:kafka_2.13-3.5.1 NOTE: Your local environment must have Java 8 installed. Apache Kafka can be started using ZooKeeper or KRaft. To get started with either configuration follow one the sections below but not both. 1 Windows单机 1.1 Kafka w…

wx.canvasToTempFilePath导出的图片不清晰

使用wx.canvasToTempFilePath接口,导出的canvas图片在手机上看不清晰 解决办法:本质上就是生成一个更大的图片,因为手机的屏幕设备的像素比现在一般都是超过2的。实际上我们只需要在使用wx.canvasToTempFilePath的时候,设置参数d…

LabVIEW在运行时调整表控件列宽

LabVIEW在运行时调整表控件列宽 如何在LabIEW中运行时调整表控件的列宽大小? 在VI运行时,有两种不同的方法可以更改表中列的宽度。首先,可以使用鼠标手动更改它们;其次,可以从框图中以编程方式更改它们。 手动更改列宽 只有在…

Unity中UI组件对Shader调色

文章目录 前言一、原理在Shader中直接暴露的Color属性,不会与UI的Image组件中的Color形成属性绑定。因为UI的Image组件中更改的颜色是顶点颜色,如果需要在修改组件中的颜色时,使Shader中的颜色也同时改变。那么就需要在应用程序阶段传入到顶点…

Docker容器启动失败:找不到映像

Docker容器启动失败:找不到映像 Docker容器启动失败:找不到映像摘要 😕引言 😮正文 🤓为什么会找不到映像? 😕1. 映像不存在2. 映像标签错误3. 映像不兼容 如何预防和解决问题? &…