读写BKP&实时时钟

读写BKP

思路:

  1. 使能PWR和BKP时钟,
  2. 通过PWR函数void PWR_BackupAccessCmd(FunctionalState NewState); 使能BKP RTC的访问,
  3. 调用BKP的读写函数void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);对BKP进行读写。

没有备用电源,主电源断开后BKP中的数据是维持不了的。读出数据0000

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

uint16_t ArrayWrite[2]={0x1234,0x5678};
uint16_t ArrayRead[2];
uint8_t keynum;
int main(void)
{
	OLED_Init();
	Key_Init();
	
	OLED_ShowString(1,1,"W");
	OLED_ShowString(2,1,"R");
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);
	
	PWR_BackupAccessCmd(ENABLE);
	
	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(2,3, ArrayRead[0],4);
		OLED_ShowHexNum(2,8, ArrayRead[1],4);
	}
}

实时时钟

思路(根据RTC基本结构)

  1. 与读写BKP的步骤1、2一样。
  2. 开启LSE时钟(默认关闭,使用时需要手动调用函数开启)
  3. 配置RTCCLK数据选择器,指定LSE为RTCCLK
  4. 注意事项中第二点调用函数等待同步,第四点调用函数等待写操作结束
  5. 配置预分频器确保给计数器时钟频率为1hz
  6. 配置cnt,给RTC初始值,需要闹钟或者中断再进行配置

MyRTC模块

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

uint16_t RTC_Time[6]={2023,1,1,23,59,55};

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)//借助BKP的特性,置一个数当作标志位实习复位断电不重置时间
	{
		RCC_LSEConfig(RCC_LSE_ON);
		while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)//调用函数等RCC有个标志位LSERDY置1时时钟启动完成

		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
		RCC_RTCCLKCmd(ENABLE);
		
		RTC_WaitForSynchro();
		RTC_WaitForLastTask();
		
		//注意事项第三点。函数本身调用了进入配置模式函数,所以不用再每次调用。
		RTC_SetPrescaler(32768 - 1);	//时钟频率1hz
		RTC_WaitForLastTask();			//对RTC寄存器的写操作要等待写操作结束
		
		RTC_SetTime();					//每次复位后调用MyRTC_Init()会将时间重置。
		
		BKP_WriteBackupRegister(BKP_DR1,0XA5A5);
	}
	else
	{
		RTC_WaitForSynchro();
		RTC_WaitForLastTask();
	}
	
}

void RTC_SetTime(void)
{
	time_t time_cnt;
	struct tm time_date;
	
	time_date.tm_year=RTC_Time[0] - 1900;
	time_date.tm_mon=RTC_Time[1] - 1;
	time_date.tm_mday=RTC_Time[2];
	time_date.tm_hour=RTC_Time[3];
	time_date.tm_min=RTC_Time[4];
	time_date.tm_sec=RTC_Time[5];
	
	time_cnt = mktime(&time_date) - 8*60*60;
	
	RTC_SetCounter(time_cnt);
	RTC_WaitForLastTask();
}

void RTC_ReadTime(void)
{
	time_t time_cnt;
	struct tm time_date;
	
	time_cnt=RTC_GetCounter() + 8*60*60;
	time_date = *localtime(&time_cnt);
	
	RTC_Time[0] = time_date.tm_year + 1900;
	RTC_Time[1] = time_date.tm_mon + 1;
	RTC_Time[2] = time_date.tm_mday;
	RTC_Time[3] = time_date.tm_hour;
	RTC_Time[4] = time_date.tm_min;
	RTC_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)
	{
		RTC_ReadTime();
		
		OLED_ShowNum(1,6,RTC_Time[0],4);
		OLED_ShowNum(1,11,RTC_Time[1],2);
		OLED_ShowNum(1,14,RTC_Time[2],2);
		OLED_ShowNum(2,6,RTC_Time[3],2);
		OLED_ShowNum(2,9,RTC_Time[4],2);
		OLED_ShowNum(2,12,RTC_Time[5],2);
		
		OLED_ShowNum(3,6,RTC_GetCounter(),10);
		
		//DIV每自减一轮,cnt+1;自减范围32767~0
		OLED_ShowNum(4,6,RTC_GetDivider(),10); 
	}
}

DIV的一个用途:如果要计数到毫秒等,按线性关系。DIV每自减一轮,cnt+1;自减范围32767~0

32767------ 0

0 ----------- 999

用32767-RTC_GetDivider()使范围变为0-32767;

再进行缩放 (32767-RTC_GetDivider())/32767.0 *999;

这样DIV由原来的32767-0变换为0-999数值表示毫秒。

猜你喜欢

转载自blog.csdn.net/m0_70732442/article/details/131941050