当前位置: 首页 > article >正文

【STM32-学习笔记-11-】RTC实时时钟

文章目录

  • RTC实时时钟
    • 一、RTC简介
    • 二、RTC框图
    • 三、RTC基本结构
    • 四、RTC操作注意事项
    • 五、RTC函数
    • 六、配置RTC
        • MyRTC.c
    • 七、示例:实时时钟
      • ①、main.c
      • ②、MyRTC.c
      • ③、MyRTC.h

RTC实时时钟

一、RTC简介

  • RTC(Real Time Clock)实时时钟

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

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

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

  • 20位的可编程预分频器,可适配不同频率的输入时钟(产生稳定的1Hz的时钟

  • 可选择三种RTC时钟源:

    • 高速外部时钟信号)HSE时钟除以128(通常为8MHz/128)
      • 主要作为系统时钟
    • 低速外部时钟信号)LSE振荡器时钟(通常为32.768KHz)
      • 主要用于RTC时钟,可由VBAT供电
      • 2 15 = 32768 2^{15}=32768 215=32768 则使用15位的计数器,计数值从0~32767,自然溢出即可产生1Hz的时钟信号
    • 低速内部时钟信号)LSI振荡器时钟(40KHz)
      • 主要作为看门狗时钟
    • image-20250115103618025

二、RTC框图

  • RTC寄存器由RTCCLK寄存器驱动

image-20250115104623366

  • 灰色区域在主电源掉电之后可由VBAT供电
  • RTC_PRL 预分频装载寄存器,用来保存RTC预分频器的周期计数值(写入R则是R+1分频
  • RTC_DIV 预分频器余数寄存器
    • 每来一个脉冲,计数值-1;自减到0时,再来一个脉冲,则会产生一个溢出信号(TR_CLK);同时DIV从PRL获取一个重装值,再继续自减
    • 该溢出信号即为所需的1Hz时钟
  • RTC_CNT (秒计数器)相当于Unix时间戳,再利用<time.h>库函数即可计算出时间
  • RTC_ALR 设置闹钟
    • RTC_ALR=RTC_CNT时,则会产生RTC_Alarm闹钟信号通往NVIC中断控制器

三、RTC基本结构

image-20250115110845391

image-20250115111150460

四、RTC操作注意事项

  1. 使能对BKP(备份寄存器)和RTC的访问

    • 需要设置RCC_APB1ENR寄存器的PWRENBKPEN位,以**

      •   RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
        
    • 需要设置PWR_CR寄存器的DBP位,以使能对BKPRTC的访问

      •   PWR_BackupAccessCmd(ENABLE);
        
  2. 等待寄存器同步标志位被置位

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

      •   while (!(RTC->CRL & RTC_CRL_RSF));  // 等待寄存器同步标志位被置位
        
    • 这是因为在APB1接口禁止状态下,寄存器的值可能不一致,需要等待同步标志位被置位以确保寄存器值的一致性

  3. 进入配置模式

    • 必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL(预分频器)、RTC_CNT(计数器)、RTC_ALR(闹钟寄存器)等寄存器

      •   RTC->CRL |= RTC_CRL_CNF;  // 设置配置模式位
          while (!(RTC->CRL & RTC_CRL_CNF));  // 等待配置模式位被硬件清除
        
  4. 写操作的时序要求:等待前一次写操作结束

    • 对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行

    • 可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中

    • 仅当RTOFF状态位是1时,才可以写入RTC寄存器。这是因为RTC寄存器在更新过程中是不允许写入的,必须等待更新完成(即RTOFF位被置位)后才能进行下一次写操作

      •   while (!(RTC->CR & RTC_CR_RTOFF));  // 等待上一次写操作完成
          RTC->CNT = new_value;  // 写入新的值
        

五、RTC函数

// 配置RTC中断
void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState);

// 进入RTC配置模式
void RTC_EnterConfigMode(void);
// 退出RTC配置模式
void RTC_ExitConfigMode(void);

// 获取RTC计数器CNT的值
uint32_t  RTC_GetCounter(void);
// 设置RTC计数器CNT的值
void RTC_SetCounter(uint32_t CounterValue);

// 设置RTC预分频值(写入PRL重装寄存器中)
void RTC_SetPrescaler(uint32_t PrescalerValue);

// 设置RTC闹钟值ALR
void RTC_SetAlarm(uint32_t AlarmValue);

// 获取RTC分频值(获取余数寄存器RTC_DIV的值)
uint32_t RTC_GetDivider(void);

// 等待RTC上一个任务完成
void RTC_WaitForLastTask(void);

// 等待RTC同步(等待RSF【寄存器同步标志】置1)
void RTC_WaitForSynchro(void);

// 获取RTC标志位状态
FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG);
// 清除RTC标志位
void RTC_ClearFlag(uint16_t RTC_FLAG);

// 获取RTC中断状态
ITStatus RTC_GetITStatus(uint16_t RTC_IT);
// 清除RTC中断待处理位
void RTC_ClearITPendingBit(uint16_t RTC_IT);

六、配置RTC

  1. 开启电源(PWR)和备份寄存器(BKP)时钟

    •   	//开启电源(PWR)和备份寄存器(BKP)时钟
        	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
        	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
      
  2. 使能对BKPRTC的访问

    •   	//使能对BKP和RTC的访问
        	PWR_BackupAccessCmd(ENABLE);
      
  3. 开启LSE(外部低速时钟)时钟(RCC模块中)

  4. 配置RTCCLK数据选择器,指定LSERTCCLK的时钟源(RCC模块中)

  5. 等待:

    1. 前一次写操作结束
    2. 等待寄存器同步标志位被置位
  6. 配置PRL重装寄存器

  7. 配置CNT

  8. 选配闹钟和中断

MyRTC.c
#include "stm32f10x.h"                  // Device header

void MyRTC_Init(void)
{
	//开启电源(PWR)和备份寄存器(BKP)时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
	
	PWR_BackupAccessCmd(ENABLE);//使能对BKP和RTC的访问
	
	RCC_LSEConfig(RCC_LSE_ON);//开启LSE时钟
	while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == !SET);//等待LSE启动完成
	
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//指定LSE为RTC的时钟源
	RCC_RTCCLKCmd(ENABLE);//使能RTCCLK的时钟
	
	RTC_WaitForLastTask();//等待RTC上一个任务完成
	RTC_WaitForSynchro();//等待RTC同步
	
	RTC_SetPrescaler(32768 - 1);//(0~32767所以要减1)配置预分频器的值(32.768kHz/32768 = 1)
	RTC_WaitForSynchro();//写入寄存器时都需要等待RTC同步
	
	RTC_SetCounter(1735660800);//配置计数器CNT的值(时间戳)【2025-01-1 0:0:00】
	RTC_WaitForSynchro();//等待RTC同步
}

七、示例:实时时钟

①、main.c

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

int main(void)
{
  OLED_Init();
	MyRTC_Init();

	OLED_ShowString(1,1,"Date:    -  -");
	OLED_ShowString(2,1,"Time:  :  :");
	OLED_ShowString(3,1,"CNT :");
	OLED_ShowString(4,6,"DIV:");
	OLED_ShowHexNum(4,1, BKP_ReadBackupRegister(BKP_DR1), 4);
	
	struct tm* DATA;
    while(1)
    {
		DATA = MyRTC_Get_Time();
		
		OLED_ShowNum(1,6, DATA->tm_year + 1900,4);//年
		OLED_ShowNum(1,11,DATA->tm_mon + 1,2);		//月
		OLED_ShowNum(1,14,DATA->tm_mday,2);				//日
		OLED_ShowNum(2,6, DATA->tm_hour,2);				//时
		OLED_ShowNum(2,9, DATA->tm_min ,2);				//分
		OLED_ShowNum(2,12,DATA->tm_sec ,2);				//秒
		
		OLED_ShowNum(3,6,RTC_GetCounter(),10);
		OLED_ShowNum(4,10,RTC_GetDivider(),6);
    }
}

②、MyRTC.c

可通过设置Timestamp时间戳变量来设置系统时间

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

#define TIME_ZONE_OFFSET (8 * 60 * 60)// 东八区时区偏移量(秒)

time_t Timestamp = 1735660780;//所设置时间的时间戳

void MyRTC_Init(void)
{
	//开启电源(PWR)和备份寄存器(BKP)时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
	
	PWR_BackupAccessCmd(ENABLE);//使能对BKP和RTC的访问
	
	//加上if_else是为了防止重复初始化和时间重置
	if(BKP_ReadBackupRegister(BKP_DR1) != 0x1212)
	{
		RCC_LSEConfig(RCC_LSE_ON);//开启LSE时钟
		while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET);//等待LSE启动完成
		
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//指定LSE为RTC的时钟源
		RCC_RTCCLKCmd(ENABLE);//使能RTCCLK的时钟
		
		RTC_WaitForSynchro();//等待RTC同步
		RTC_WaitForLastTask();//等待RTC上一个任务完成
		
		RTC_SetPrescaler(32768 - 1);//(0~32767所以要减1)配置预分频器的值(32.768kHz/32768 = 1)
		RTC_WaitForLastTask();//每一次写入操作都需要等待RTC上一个任务完成
		
//		RTC_SetCounter(1735660800);//配置计数器CNT的值(时间戳)【2025-01-1 0:0:00】
//		RTC_WaitForLastTask();//等待RTC上一个任务完成
		MyRTC_Set_Time();
		
		//在备份寄存器写入自己规定的标志位,用于判断RTC是不是第一次执行配置
		BKP_WriteBackupRegister(BKP_DR1, 0x1212);
	}
	else
	{
		RTC_WaitForSynchro();//等待RTC同步
		RTC_WaitForLastTask();//等待RTC上一个任务完成
	}
}

void MyRTC_Set_Time(void)//设置时间
{	
	RTC_SetCounter(Timestamp);//配置计数器CNT的值(时间戳)
	RTC_WaitForSynchro();//等待RTC同步
}

struct tm* MyRTC_Get_Time(void)//获取时间
{
	time_t time_cnt;
	time_cnt = RTC_GetCounter() + TIME_ZONE_OFFSET;//东八区 加上8天(即8*60*60秒)
	struct tm* Temp;
	Temp = localtime(&time_cnt);
	
	return Temp;
}

③、MyRTC.h

#ifndef __MYRTC_H__
#define __MYRTC_H__
#include "stdint.h"
#include <time.h>

extern time_t Timestamp;//所设置时间的时间戳

void MyRTC_Init(void);
void MyRTC_Set_Time(void);//设置时间
struct tm* MyRTC_Get_Time(void);//获取时间

#endif


http://www.kler.cn/a/511684.html

相关文章:

  • 【神经网络基础】
  • Output
  • AudioGPT全新的 音频内容理解与生成系统
  • 利用 LNMP 实现 WordPress 站点搭建
  • 【Go】Go数据类型详解—指针
  • 文件操作:系统IO
  • SpringCloud+Vue+Python人工智能(fastAPI,机器学习,深度学习)前后端架构各功能实现思路——主目录(持续更新)
  • 【机器学习实战入门项目】使用深度学习创建您自己的表情符号
  • Selenium工具使用Python 语言实现下拉框定位操作
  • 深入了解JSON:Python中JSON的全面应用指南
  • deeply c-函数栈帧(函数栈帧的过程)
  • VLAN基础理论
  • Unity 学习指南与资料分享
  • Python操作Excel——openpyxl使用笔记(1)
  • matlab实现了一个完整的语音通信系统的模拟,包括语音信号的读取、编码(PCM 和汉明码)、调制
  • redux 结合 @reduxjs/toolkit 的使用
  • 【机器学习实战入门】泰坦尼克号生存预测
  • matlab实现一个雷达信号处理的程序,涉及到对原始图像的模拟、加权、加噪以及通过迭代算法对图像进行恢复和优化处理
  • 三格电子——CAN转WIFI网关
  • Web安全|渗透测试|网络安全
  • oracle 的物化视图介绍
  • 小白误入(需要一定的vue基础 )使用node建立服务器——vue前端登录注册页面连接到数据库
  • Linux系统服务管理
  • 基于VSCODE+GDB+GDBSERVER远程单步调试设备篇(可视化界面)
  • 哈尔滨有双线服务器租用吗?
  • Redis 学习指南与资料分享