STM32入门——实时时钟(20)

STM32入门——实时时钟(20)

RTC简介

  • RTC(Real Time Clock)实时时钟

  • RTC是一个独立的定时器,可为系统提供时钟和日历的功能

  • RTC和时钟配置系统处于后备区域,系统复位时数据不清零,VDD(2.03.6V)断电后可借助VBAT(1.8V3.6V)供电继续走时。

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

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

  • 可选择三种RTC时钟源:
    HSE时钟除以128(通常为8MHz/128)

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

    LSI振荡器时钟(40KHz)

image-20260405180507651

image-20260405180530919

#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);	//显示余数寄存器
	}
}

#include "stm32f10x.h"
#include "time.h"

uint16_t MyRTC_Time[] = {2026,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_LSERDY) != 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 * 8 * 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;

}


#ifndef __MYRTC_H
#define __MYRTC_H

extern uint16_t MyRTC_Time[];

void MyRTC_Init(void);
void MyRTC_SetTime(void);
void MyRTC_ReadTime(void);

#endif

posted @ 2026-04-05 19:11  Q&25  阅读(1)  评论(1)    收藏  举报