STM32之RTC实时时钟

关于RTC呢,其实他就是一个计数器,用一直不停的计数来记录时间的流逝。
在STM32F103中,RTC是挂载在APB1总线上的,但是这个APB1总线并不直接给RTC提供时钟源,它只是提供读写操作的,通过APB1接口可以访问RTC的相关寄存器(预分频值,计数器值,闹钟值)。而RTC的计数时钟源可以来自于以下三种

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

HSE来自于外部的晶振(4-16MHz),精度较高;
LSE为外部固定晶振产生(32.768KHz),一般用于RTC;
LSI为内部RC振荡器(40KHz)产生,被用于RTC时钟或者独立看门狗时钟IWDGCLK;
由于RTC是产生时钟的,也就是时间,一旦时间开启了,我们就希望它可以一直走下去,所以来说,我们单片机系统的复位并不会影响RTC的工作,而RTC要想复位,必须通过后备域,后备域我也不多说了,我也没查太多关于他的资料。
RTC工作框图

其中RTCCLK就是RTC的时钟源,RTC_DIV是我们设置的分频系数,就是我们把RTC的时钟源分频之后产生的时钟信号给了RTC_CNT计数器,RTC_PRL就是我们存储我们设置的分频系数的寄存器。注意我们经过分频器出来的时钟TR_CLK,它的频率最低是1Hz,也就是可以产生最长1秒的时基信号。
然后32为可编程计数器RTC_CNT就是我们记录时间流逝的计数器,来一个时钟,他就加1。然后RTC_ALR装载的就是我们设置的闹钟时间,当计数器中的数值和RTC_ALR中的数值相等的时候,就会引发闹钟中断,如果我们写了闹钟中断服务函数并且使能了闹钟中断,那么就会进入闹钟中断服务函数。再往右边看,就是3个中断

SECIE:秒中断
OWIE:溢出中断
ALRIE:闹钟中断

同时还有三个中断标志位

SECF:秒中断标志位
OWF:溢出中断标志位
ALRF:闹钟中断标志位

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

BKP备份寄存器

备份寄存器是42个16位的寄存器。可用来存储84个字节数据。
它们处在备份区域,当VDD电源切断,仍然由VBAT维持供电。
当系统在待机模式下被唤醒,或者系统复位或者电源复位,它们也不会复位。
执行以下操作将使能对后备寄存器和RTC访问:
设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备时钟。
设置寄存器PWR_CR的DBP位,使能对RTC和后备寄存器的访问。

这个备份寄存器主要存储的时一些系统配置信息和一些中断标志位。当系统电源VDD被切断时,备份寄存器由备份电源供电。

关于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
同步函数:
void RTC_WaitForLastTask(void);//等待上次操作完成:CRL位RTOFF
void RTC_WaitForSynchro(void);//等待时钟同步:CRL位RSF
相关状态位获取清除函数:
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配置一般步骤

1、使能PWR和BKP时钟:RCC_APB1PeriphClockCmd();
2、使能后备寄存器访问:   PWR_BackupAccessCmd();
3、配置RTC时钟源,使能RTC时钟:
      RCC_RTCCLKConfig();
      RCC_RTCCLKCmd();
      如果使用LSE,要打开LSE:RCC_LSEConfig(RCC_LSE_ON);
4、设置RTC预分频系数:RTC_SetPrescaler();
5、设置时间:RTC_SetCounter();
6、开启相关中断(如果需要):RTC_ITConfig();
7、编写中断服务函数:RTC_IRQHandler();
8、部分操作要等待写操作完成和同步。
   RTC_WaitForLastTask();//等待最近一次对RTC寄存器的写操作完成
   RTC_WaitForSynchro();    //等待RTC寄存器同步 

部分代码

//实时时钟配置
//初始化RTC时钟,同时检测时钟是否工作正常
//BKP->DR1用于保存是否第一次配置的设置
//返回0:正常
//其他:错误代码

u8 RTC_Init(void)
{
    //检查是不是第一次配置时钟
    u8 temp=0;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);    //使能PWR和BKP外设时钟   
    PWR_BackupAccessCmd(ENABLE);    //使能后备寄存器访问  
    if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050)      //从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎,其中0X5050这个数是随便写的,随便写一个数据都行,它只是起到一个标志位的作用,但是在这里设的数值要和一会下面设的数值保持一致
        {                
        BKP_DeInit();   //复位备份区域    
        RCC_LSEConfig(RCC_LSE_ON);  //设置外部低速晶振(LSE),使用外设低速晶振
        while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)   //检查指定的RCC标志位设置与否,等待低速晶振就绪,但是也不是一直无限等待下去,设一个temp变量,让他隔段时间加1,当它加到250的时候,我们就不再等了
            {
            temp++;
            delay_ms(10);
            }
        if(temp>=250)return 1;//初始化时钟失败,晶振有问题       
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);     //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    
        RCC_RTCCLKCmd(ENABLE);  //使能RTC时钟  
        RTC_WaitForLastTask();  //等待最近一次对RTC寄存器的写操作完成
        RTC_WaitForSynchro();       //等待RTC寄存器同步  
        RTC_ITConfig(RTC_IT_SEC, ENABLE);       //使能RTC秒中断
        RTC_WaitForLastTask();  //等待最近一次对RTC寄存器的写操作完成
        RTC_EnterConfigMode();/// 允许配置  
        RTC_SetPrescaler(32767); //设置RTC预分频的值
        RTC_WaitForLastTask();  //等待最近一次对RTC寄存器的写操作完成
        RTC_Set(2018,8,31,16,28,50);  //设置时间    
        RTC_ExitConfigMode(); //退出配置模式  
        BKP_WriteBackupRegister(BKP_DR1, 0X5050);   //向指定的后备寄存器中写入用户程序数据
        }
    else//系统继续计时
        {

        RTC_WaitForSynchro();   //等待最近一次对RTC寄存器的写操作完成
        RTC_ITConfig(RTC_IT_SEC, ENABLE);   //使能RTC秒中断
        RTC_WaitForLastTask();  //等待最近一次对RTC寄存器的写操作完成
        }
    RTC_NVIC_Config();//RCT中断分组设置                                
    RTC_Get();//更新时间    
    return 0; //ok
}   

上段代码中我们就是大概看一下配置RTC的整体过程即可,里面调用的一些函数我并没有贴出来,比如说RTC_Set(2018,8,31,16,28,50);RTC_Get();
在这两个函数里面,我们主要是把计数器中的数值转变为我们能够直接看懂的时间值,时间起点是1970年1月1日0时。

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值