首页 > 其他分享 >stm32 Unix时间戳&BKP备份寄存器&RTC实时时钟(读写备份寄存器&实时时钟)

stm32 Unix时间戳&BKP备份寄存器&RTC实时时钟(读写备份寄存器&实时时钟)

时间:2024-09-18 21:25:10浏览次数:3  
标签:MyRTC RTC 备份 Time BKP 寄存器 RCC void 时钟

理论

1.Unix时间戳

(1)Unix 时间戳

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

时间戳存储在一个秒计数器中,秒计数器为32位/64位的整型变量 世界上所有时区的秒计数器相同,不同时区通过添加偏移来得到当地时间

 (2)UTC/GMT

GMT

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

UTC

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

(3)时间戳转换

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

2.BKP

(1)BKP简介

BKP(Backup Registers)备份寄存器

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

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

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

存储RTC时钟校准寄存器

用户数据存储容量:

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

(2)BKP基本结构

TAMPER(入侵检测):如果硬件强行被拆开,那么BKP保存的数据会被清除。 

3.RTC

(1)RTC简介

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)

HSE:系统主时钟

LSE:VBAT(主要特征是用于断电后的RTC)

LSI :主要用于看门狗

(2)RTC框图

1.APB1接口接收RTCCLK数据:必须等RTCCLK数据(产生上升沿)进行刷新 ,由于             PCLK1与RTCCLK的频率不一样所以必须等待RTCCLK与PCLK1同频率

2.RTC_PRL(预设值),RTC_DIV(进行递减操作),RTC_CNT实现秒计数

3.RTC_Second每秒进一次中断,RTC_Overflow超出RTC_CNT计数值发生中断,                   RTC_Alarm(设置闹钟)

(4)RTC基本结构

图像简化

4.硬件电路

5.RTC操作注意事项

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

 设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟 (对PWR与BKP进行开启时钟)  

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

void PWR_BackupAccessCmd(FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  *(__IO uint32_t *) CR_DBP_BB = (uint32_t)NewState;
}

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

void RTC_WaitForSynchro(void)
{
  /* Clear RSF flag */
  RTC->CRL &= (uint16_t)~RTC_FLAG_RSF;
  /* Loop until RSF flag is set */
  while ((RTC->CRL & RTC_FLAG_RSF) == (uint16_t)RESET)
  {
  }
}

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

void RTC_SetPrescaler(uint32_t PrescalerValue)
{
  /* Check the parameters */
  assert_param(IS_RTC_PRESCALER(PrescalerValue));
  
  RTC_EnterConfigMode();
  /* Set RTC PRESCALER MSB word */
  RTC->PRLH = (PrescalerValue & PRLH_MSB_MASK) >> 16;
  /* Set RTC PRESCALER LSB word */
  RTC->PRLL = (PrescalerValue & RTC_LSB_MASK);
  RTC_ExitConfigMode();
}
void RTC_EnterConfigMode(void)
{
  /* Set the CNF flag to enter in the Configuration Mode */
  RTC->CRL |= RTC_CRL_CNF;
}

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

API学习

1.BKP

(1)BKP_DeInit

void BKP_DeInit(void);
/*
 *恢复配置清除配置
 */

(2)BKP_WriteBackupRegister

void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);
/*
 *写备份寄存器
 */

(3) BKP_ReadBackupRegister

uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);
/*
 *读备份寄存器
 */

2.RCC 

(1) RCC_LSEConfig

void RCC_LSEConfig(uint8_t RCC_LSE);
/*
 *启动RCC_LSE时钟 
 */

 (2) RCC_LSICmd

void RCC_LSICmd(FunctionalState NewState);
/*
 *启动LSI时钟
 */

(3)RCC_RTCCLKConfig

void RCC_RTCCLKConfig(uint32_t RCC_RTCCLKSource);
/*
 *选择RCC_RTCCLK的时钟源
 */

(4)RCC_RTCCLKCmd

void RCC_RTCCLKCmd(FunctionalState NewState);
/*
 *使能RCC_RTCCLK 
 */

3.RTC 

    (1) RTC_EnterConfigMode

void RTC_EnterConfigMode(void);
/*
 *进入配置模式
 */

(2)RTC_GetCounter

uint32_t  RTC_GetCounter(void);
/*
 *获取CNT计数器的值 
 */

(3)RTC_SetPrescaler

void RTC_SetPrescaler(uint32_t PrescalerValue);
/*
 *设置预分频的值
 */

(4)RTC_GetDivider

uint32_t  RTC_GetDivider(void);
/*
 *获取余数值
 */

(5)RTC_WaitForLastTask

void RTC_WaitForLastTask(void);
/*
 *等待上一次操作完成
 */

(6)RTC_WaitForSynchro

void RTC_WaitForSynchro(void);
/*
 *等待同步
 */

代码

读写备份寄存器

main.c

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

uint8_t KeyNum;					//定义用于接收按键键码的变量

uint16_t ArrayWrite[] = {0x1234, 0x5678};	//定义要写入数据的测试数组
uint16_t ArrayRead[2];						//定义要读取数据的测试数组

int main(void)
{
	/*模块初始化*/
	OLED_Init();				//OLED初始化
	Key_Init();					//按键初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "W:");
	OLED_ShowString(2, 1, "R:");
	
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);		//开启PWR的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);		//开启BKP的时钟
	
	/*备份寄存器访问使能*/
	PWR_BackupAccessCmd(ENABLE);							//使用PWR开启对备份寄存器的访问
	
	while (1)
	{
		KeyNum = Key_GetNum();		//获取按键键码
		
		if (KeyNum == 1)			//按键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(2, 3, ArrayRead[0], 4);				//显示读取的备份寄存器数据
		OLED_ShowHexNum(2, 8, ArrayRead[1], 4);
	}
}

实时时钟

main.c

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

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	MyRTC_Init();		//RTC初始化
	
	/*显示静态字符串*/
	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();							//RTC读取时间,最新的时间存储到MyRTC_Time数组中
		
		OLED_ShowNum(1, 6, MyRTC_Time[0], 4);		//显示MyRTC_Time数组中的时间值,年
		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);	//显示32位的秒计数器
		OLED_ShowNum(4, 6, RTC_GetDivider(), 10);	//显示余数寄存器
	}
}

MyRTC.c

#include "stm32f10x.h"                  // Device header
#include <time.h>

uint16_t MyRTC_Time[] = {2023, 1, 1, 23, 59, 55};	//定义全局的时间数组,数组内容分别为年、月、日、时、分、秒

void MyRTC_SetTime(void);				//函数声明

/**
  * 函    数:RTC初始化
  * 参    数:无
  * 返 回 值:无
  */
void MyRTC_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);		//开启PWR的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);		//开启BKP的时钟
	
	/*备份寄存器访问使能*/
	PWR_BackupAccessCmd(ENABLE);							//使用PWR开启对备份寄存器的访问
	
	if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)			//通过写入备份寄存器的标志位,判断RTC是否是第一次配置
															//if成立则执行第一次的RTC配置
	{
		RCC_LSEConfig(RCC_LSE_ON);							//开启LSE时钟
		while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET);	//等待LSE准备就绪
		
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);				//选择RTCCLK来源为LSE
		RCC_RTCCLKCmd(ENABLE);								//RTCCLK使能
		
		RTC_WaitForSynchro();								//等待同步
		RTC_WaitForLastTask();								//等待上一次操作完成
		
		RTC_SetPrescaler(32768 - 1);						//设置RTC预分频器,预分频后的计数频率为1Hz
		RTC_WaitForLastTask();								//等待上一次操作完成
		
		MyRTC_SetTime();									//设置时间,调用此函数,全局数组里时间值刷新到RTC硬件电路
		
		BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);			//在备份寄存器写入自己规定的标志位,用于判断RTC是不是第一次执行配置
	}
	else													//RTC不是第一次配置
	{
		RTC_WaitForSynchro();								//等待同步
		RTC_WaitForLastTask();								//等待上一次操作完成
	}
}

//如果LSE无法起振导致程序卡死在初始化函数中
//可将初始化函数替换为下述代码,使用LSI当作RTCCLK
//LSI无法由备用电源供电,故主电源掉电时,RTC走时会暂停
/* 
void MyRTC_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
	
	PWR_BackupAccessCmd(ENABLE);
	
	if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)
	{
		RCC_LSICmd(ENABLE);
		while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) != SET);
		
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
		RCC_RTCCLKCmd(ENABLE);
		
		RTC_WaitForSynchro();
		RTC_WaitForLastTask();
		
		RTC_SetPrescaler(40000 - 1);
		RTC_WaitForLastTask();
		
		MyRTC_SetTime();
		
		BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);
	}
	else
	{
		RCC_LSICmd(ENABLE);				//即使不是第一次配置,也需要再次开启LSI时钟
		while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) != SET);
		
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
		RCC_RTCCLKCmd(ENABLE);
		
		RTC_WaitForSynchro();
		RTC_WaitForLastTask();
	}
}*/

/**
  * 函    数:RTC设置时间
  * 参    数:无
  * 返 回 值:无
  * 说    明:调用此函数后,全局数组里时间值将刷新到RTC硬件电路
  */
void MyRTC_SetTime(void)
{
	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;	//调用mktime函数,将日期时间转换为秒计数器格式
													//- 8 * 60 * 60为东八区的时区调整
	
	RTC_SetCounter(time_cnt);						//将秒计数器写入到RTC的CNT中
	RTC_WaitForLastTask();							//等待上一次操作完成
}

/**
  * 函    数:RTC读取时间
  * 参    数:无
  * 返 回 值:无
  * 说    明:调用此函数后,RTC硬件电路里时间值将刷新到全局数组
  */
void MyRTC_ReadTime(void)
{
	time_t time_cnt;		//定义秒计数器数据类型
	struct tm time_date;	//定义日期时间数据类型
	
	time_cnt = RTC_GetCounter() + 8 * 60 * 60;		//读取RTC的CNT,获取当前的秒计数器
													//+ 8 * 60 * 60为东八区的时区调整
	
	time_date = *localtime(&time_cnt);				//使用localtime函数,将秒计数器转换为日期时间格式
	
	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

接线图

标签:MyRTC,RTC,备份,Time,BKP,寄存器,RCC,void,时钟
From: https://blog.csdn.net/2302_79504723/article/details/142342019

相关文章

  • PowerShell 命令来备份 Windows 10 的服务列表:CMD 批处理命令来备份 Windows 10 的服
    PowerShell命令来备份Windows10的服务列表:powershellCopyCodeGet-Service|Export-Csv-Path"C:\ServiceListBackup.csv"-NoTypeInformation这条命令会将所有服务信息导出到C:\ServiceListBackup.csv文件中。确保您有写入该路径的权限。CMD批处理命令来备份Windo......
  • 南大通用GBase 8c增量备份实践
    在当今信息化社会,数据的重要性不言而喻。对于企业来说,保护数据的完整性和可用性至关重要。数据库作为数据存储的核心组件,其备份与恢复策略的制定和实施是保障数据安全的关键一环。本文将详细介绍南大通用GBase8c数据库的增量备份实践操作,帮助读者掌握如何高效、可靠地进行数据备份......
  • GBase 8a支持在线备份功能
    GBase8a的gcbr.py工具提供基于快照的在线备份能力。实现在线库级和表级(包括批量表级备份)的全量备份/增量备份功能;在线备份过程分为元数据备份和表数据备份两个阶段,不同阶段对数据库使用的影响如下:(1)元数据备份阶段,阻塞DDL操作和DML操作。此阶段相对于表数据备份阶段来......
  • CMS32L051定时器时钟选择
    文章目录概要代码小结概要CMS32L051的定时器搭载两个通用定时器单元,每个单元含有4个通道。每个通用定时器单元有4个16位定时器。各16位定时器称为“通道”,既能分别用作独立的定时器,也能组合多个通道用作高级的定时器功能。定时器时钟选择寄存器m(TPSm)是16位寄存器,选......
  • [官翻]mysqlbackup的乐观备份
    乐观备份可以用来提升备份和恢复体量比较大的数据库(只有少量的表经常变更)的性能。2)在大型数据库的热备份过程中(例如,以TB为单位),当备份进行时,可能会在服务器上生成巨大的重做日志文件。由于重做日志文件的增长速度快于mysqlbackup处理的速度,因此当mysqlbackup无法赶上重做日志周期,并......
  • 易优CMS后台如何备份数据库
    步骤1:进入后台登录易优CMS后台。在后台左侧菜单栏中找到“功能地图”(低版本的程序点击“更多功能”)。步骤2:进入备份还原功能在“功能地图”中找到“备份还原”功能,并点击进入。步骤3:进行数据备份在“备份还原”页面中,点击“数据备份”。等待一段时间,直到备份完成。......
  • java读取寄存器数据
    在Java中直接读取硬件寄存器(如CPU寄存器、I/O端口等)通常不是一个直接的任务,因为Java设计之初就是为了跨平台的安全性和易用性,它并不直接提供访问底层硬件的API。不过,在嵌入式系统、工业控制或需要直接与硬件交互的特定场景中,可能会使用JNI(JavaNativeInterface)或JNA(JavaNativeAc......
  • k8s集群备份与迁移
    什么是Velero?Velero是一个用Go语言开发的开源工具,用于Kubernetes集群的备份、恢复、灾难恢复和迁移。Velero备份工作流程当用户发起velerobackupcreate时,会执行如下四个动作:velero客户端调用KubernetesAPI创建自定义资源并存储到etcd;BackupController通过Kuber......
  • 帝国CMS搬家以及帝国cms备份数据库方法
    帝国CMS(EmpireCMS,简称ECMS)搬家以及备份数据库的方法主要包括几个步骤:备份数据库、备份网站文件、在新服务器上恢复数据、测试新站点等。下面将详细说明这些步骤:1.备份数据库使用帝国CMS后台备份登录后台:登录帝国CMS的管理后台。进入备份与恢复数据页面:在后台导航中......
  • 帝国网站如何备份数据库
    帝国CMS(EmpireCMS,简称ECMS)提供了多种方法来备份数据库,包括使用内置的备份工具、通过数据库管理工具(如phpMyAdmin)或使用命令行工具(如mysqldump)。下面是几种常见的备份方法:1.使用帝国CMS后台备份步骤:登录帝国CMS后台:使用管理员账号登录帝国CMS的管理后台。进入备份与恢......