stm32miniRTC实时时钟——HAL库

RTC简介

实时时钟(RTC)是一个独立的定时器。STM32 的 RTC 模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。

RTC 模块和时钟配置系统(RCC_BDCR 寄存器)是在后备区域,即在系统复位或从待机模式唤醒后 RTC 的设置和时间维持不变。但是在系统复位后,会自动禁止访问后备寄存器和 RTC,以防止对后备区域(BKP)的意外写操作。所以在要设置时间之前, 先要 取消备份区域(BKP)写保护

和通用定时器主要区别:

功能十分简单,只有定时功能(也可以触发中断),特殊之处在于断电之后仍可以独立运行,只要RTC备用电源供电,就可以一直运行。

两个 32 位寄存器包含二进码十进数格式 (BCD) 的秒、分钟、小时( 12 或 24 小时制)、星期几、日期、月份和年份。此外,还可提供二进制格式的亚秒值。系统可以自动将月份的天数补偿为 28、29(闰年)、30 和 31 天。

上电复位后,所有RTC寄存器都会受到保护,以防止可能的非正常写访问。

无论器件状态如何(运行模式、低功耗模式或处于复位状态),只要电源电压保持在工作范围内,RTC使不会停止工作。

RTC特征

编程的预分频系数:分频系数高为220。
● 32位的可编程计数器,可用于较长时间段的测量。
● 2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟频率的四分之一以上)。

● 可以选择以下三种RTC的时钟源:

● HSE时钟除以128
● LSE振荡器时钟
● LSI振荡器时钟

● 2个独立的复位类型:
● APB1接口由系统复位;
● RTC核心(预分频器、闹钟、计数器和分频器)只能由后备域复位

● 3个专门的可屏蔽中断:
● 1.闹钟中断,用来产生一个软件可编程的闹钟中断。
● 2.秒中断,用来产生一个可编程的周期性中断信号(长可达1秒)。
● 3.溢出中断,指示内部可编程计数器溢出并回转为0的状态。

RTC原理

RTC工作原理图

在这里插入图片描述

RTC 由两个主要部分组成

第一部分(APB1 接口)用来和 APB1 总线相连。此单元还包含一组 16 位寄存器,可通过 APB1 总线对其进行读写操作。APB1 接口由 APB1 总线时钟驱动,用来与 APB1 总线连接。

另一部分(RTC 核心)由一组可编程计数器组成,分成两个主要模块。第一个模块是 RTC 的预分频模块 ,它可编程产生 1 秒的 RTC 时间基准 TR_CLK。RTC 的预分频模块包含了一个 20位的可编程分频器(RTC 预分频器)。如果在 RTC_CR 寄存器 中设置了相应的允许位,则在每个TR_CLK 周期中 RTC 产生一个中断(秒中断)。第二个模块是一个 32 位的可编程计数器 ,可被初始化为当前的系统时间,一个 32 位的时钟计数器,按秒钟计算,可以记录 4294967296 秒,约合 136 年左右。

这里注意
RTC内核完全独立于APB1接口,软件通过APB1接口对RTC相关寄存器访问,
但是相关寄存器只在RTC APB1时钟进行重新同步的RTC时钟的上升沿被更新。
所以软件必须先等待寄存器同步标志位被硬件置1才读(否则通常读到0)。

RTC相关寄存器

BKP备份寄存器
RTC控制寄存器    (RTC_CRH,RTC_CRL)
RTC预分频装载寄存器 (RTC_PRLH,RTC_PRLL)
RTC预分频余数寄存器 (RTC_DIVH,RTC_DIVL)
RTC计数器寄存器   (RTC_CNTH,RTC_CNTL)
RTC闹钟寄存器    (RTC_ALRH,RTC_ALRL)

BKP寄存器

在这里插入图片描述
控制寄存器 RTC_CRH

设置最低位为1,允许秒中断
在这里插入图片描述
控制寄存器 RTC_CRL

用到的是该寄存器的 0、3~5 这几个位,
第 0 位是秒钟标志位,我们在进入闹钟中断的时候,通过判断这位来决定是不是发生了秒钟中断。然后必须通过软件将该位清零(写0)。
第 3 位为寄存器同步标志位,我们在修改控制寄存器 RTC_CRH/CRL 之前,必须先判断该位,是否已经同步了,如果没有则等待同步,在没同步的情况下修改 RTC_CRH/CRL 的值是不行的。
第 4 位为配置标位,在软件修改 RTC_CNT/RTC_ALR/RTC_PRL 的值的时候,必须先软件置位该位,以允许进入配置模式。
第 5 位为 RTC 操作位,该位由硬件操作,软件只读。通过该位可以判断上次对 RTC 寄存器的操作是否完成,如果没有,我们必须等待上一次操作结束才能开始下一次操作。

在这里插入图片描述
预分频装载寄存器 RTC_PRLH

用来配置 RTC 时钟的分频数,如使用外部 32.768K 的晶振作为时钟的输入频率,则设置这两个寄存器的值为 32767,以得到一秒钟的计数频率

在这里插入图片描述
预分频装载寄存器 RTC_PRLH

在这里插入图片描述
预分频器余数寄存器RTC_DIVH 和 RTC_DIVL

这两个寄存器的作用就是用来获得 比秒钟更为准确的时钟 ,比如可以得到 0.1 秒,或者 0.01 秒等。该寄存器的值自减的,用于保存还需要多少时钟周期获得一个秒信号。在一次秒钟更新后,由硬件重新装载。

计数器寄存器 RTC_CNT

用来记录秒钟值(一般情况下),在修改这个寄存器的时候要先进入配置模式。

闹钟寄存器RTC_ALRH 和 RTC_ALRL

用来标记闹钟产生的时间(以秒为单位),如果 RTC_CNT 的值与 RTC_ALR 的值相等,并使能了中断的话,会产生一个闹钟中断。该寄存器的修改也要进入配置模式才能进行。

配置RTC寄存器

在这里插入图片描述
这里注意RTC时钟选择:
使用HSE分频时钟或者LSI的时候,在主电源VDD掉电的情况下,这两个时钟来源都会受到影响.所以RTC一般都使用时钟低速外部时钟LSE,频率为实时时钟模块中常用的32.768KHz,因为32768 = 2^15,容易实现

RTC相关库函数

/*RTC时钟源和时钟源操作函数*/
 void RCC_RTCCLKConfig(uint32_t  CLKSource)//时钟源选择
 void RCC_RTCCLKCmd(FunctionalState NewState)//时钟使能
 
 /*RTC配置函数*/
 void RTC_SetPrescaler(uint32_t PrescalerValue);//预分频配置:PRLH/PRLL
 void RTC_SetCounter(uint32_t CounterValue)//设置计数器值:CNTH/CNTL
 void RTC_SetAlarm(uint32_t AlarmValue)//闹钟设置:ALRH/ALRL
 
/*RTC中断设置函数*/
 void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState);//CRH
 
/*RTC配置函数*/
 void RTC_EnterConfigMode(void);//允许RTC配置 :CRL位 CNF
 void RTC_ExitConfigMode(void);//退出配置模式:CRL位 CNF

/*RTC同步函数*/
 void RTC_WaitForLastTask(void)//等待上次操作完成:CRL位RTOFF
 void RTC_WaitForSynchro(void)//等待时钟同步:CRL位RSF
 
/*RTC相关状态获取清除函数*/
 FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG);
 void RTC_ClearFlag(uint16_t RTC_FLAG);
 ITStatus RTC_GetITStatus(uint16_t RTC_IT);
 void RTC_ClearITPendingBit(uint16_t RTC_IT);
 
/*BKP相关函数*/
PWR_BackupAccessCmd();//BKP后备区域访问使能
RCC_APB1PeriphClockCmd();//使能PWR和BKP时钟
RCC_LSEConfig();//开启LSE,RTC选择LSE作为时钟源	
PWR_BackupAccessCmd();//BKP后备区域访问使能
uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);//读BKP寄存器
void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);//写BKP

RTC配置步骤

在这里插入图片描述

程序

u8 RTC_initConfig()  
{  
    u8 temp = 0;  
    NVIC_InitTypeDef NVIC_InitStructure;  
      
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP|RCC_APB1Periph_PWR, ENABLE); // 使能APB1总线上的BKP与PWR的时钟  
    PWR_BackupAccessCmd(ENABLE); // 取消后备区域写保护  
    delay_init(); // delay函数初始化  
      
    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;  
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  
    NVIC_Init(&NVIC_InitStructure); // 配置RTC的NVIC中断通道  
      
    if(BKP_ReadBackupRegister(BKP_DR1) == 0x5050) // 首次执行程序段  
    {  
        BKP_DeInit(); // BKP外设时钟复位  
        RCC_LSEConfig(RCC_LSE_ON); // LSE低速时钟使能  
          
        while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET && temp < 250)  
        {  
            temp++;  
            delay_ms(10);  
        }  
        if(temp>=250) return 1;  
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);  
        RCC_RTCCLKCmd(ENABLE);  
          
        RTC_WaitForLastTask(); // 等待RTC操作完成  
        RTC_WaitForSynchro(); // 等待APB1与RTC时钟同步  
          
        RTC_EnterConfigMode(); // 进入RTC配置模式  
          
        RTC_WaitForLastTask(); // 等待RTC操作完成  
        RTC_WaitForSynchro(); // 等待APB1与RTC时钟同步  
          
        RTC_SetPrescaler(32768-1); // 1s溢出一次  
          
        RTC_WaitForLastTask(); // 等待RTC操作完成  
        RTC_WaitForSynchro(); // 等待APB1与RTC时钟同步  
          
        RTC_ITConfig(RTC_IT_OW|RTC_IT_SEC,ENABLE); // RTC中断配置  
          
        RTC_WaitForLastTask(); // 等待RTC操作完成  
        RTC_WaitForSynchro(); // 等待APB1与RTC时钟同步  
          
        RTC_Set(2015,1,14,17,42,55);  //将时间转化为以秒为单位的数值加载到32位可编程计数器当中      
          
        RTC_WaitForLastTask(); // 等待RTC操作完成  
        RTC_WaitForSynchro(); // 等待APB1与RTC时钟同步  
          
        RTC_ExitConfigMode(); // 退出配置模式,并且执行在此之前写入的命令  
          
        BKP_WriteBackupRegister(BKP_DR1,0x5050); // 向BKP_DR1(16位寄存器)寄存器写入0x5050这个16位数据  
    }  
    else // 再次进行执行的程序段(系统/电源复位后执行)  
    {  
        RTC_WaitForLastTask(); // 等待RTC操作完成  
        RTC_WaitForSynchro(); // 等待APB1与RTC时钟同步  
          
        RTC_ITConfig(RTC_IT_OW|RTC_IT_SEC,ENABLE); // 系统/电源复位后执行后,RTC的CR寄存器被复位因此需要重新配置RTC中断  
          
        RTC_WaitForLastTask(); // 等待RTC操作完成  
    }  
    return 0;  
}  

其它

这里注意,由于RTC不会重复初始化,所以我们在更改当前时间时,
也要更改标志位0x50的值重新初始化

if(HAL_RTCEx_BKUPRead(&RTC_Handler,RTC_BKP_DR1)!=0X5050)//是否第一次配置
	{
		RTC_Set(2019,11,26,18,8,0); //设置日期和时间,2019年11月27日,18点08分0秒		 									  
		HAL_RTCEx_BKUPWrite(&RTC_Handler,RTC_BKP_DR1,0X5050);//标记已经初始化过了
	 	printf("FIRST TIME\n");
	}

同样值得注意的还有如何判断初始化成功与否,晶振是否有问题

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值