STM32处理器 RTC分析

来源:互联网 发布:冯提莫用什么软件唱歌 编辑:程序博客网 时间:2024/06/08 16:25

前言:
1.博客基于ARM Cortex-M3内核的STM32F103ZET6和标准3.5.0库;
2.如有不足之处,还请多多指教

一 RTC是什么?
1. 从结构上讲就是一个独立的定时器;
2. 从功能上来说就是为系统提供系统掉电不复位的日历时间;

这里写图片描述
RTC分为两个完全能独立的部分:1. APB1接口;2. RTC核心;
功能:
(1)APB1总线连接APB1接口并负责驱动APB1接口,接口内部包含一组16位寄存器,可以通过APB1总线对其进行读写操作。
(2)RTC核心由RTC20位预分频模块和32位可编程计数器模块组成;
Ⅰ RTC预分频模块包含一个20位的可编程分频器RTC_DIV。预分频模块为32位计数器模块提供时基单元,这个很重要;预分频器内的是一递变(递减)的值,当递减结束时如果开启相应的中断,将会触发中断,并且RTC_PRL重装载寄存器将会将之前写入的分频值再次装载到预分频器;
Ⅱ 计数器(RTC_CNT)概念很好理解,当时基单元是1S时,计数器可以记录136年左右的时间,够用了;若配置并使能了RTC_ALR(闹钟寄存器),当RTC_CNT的值与其相等时,将会触发闹钟中断;

二 RTC的时钟源的选择
这里写图片描述
(1)从图片中可以看出有三个时钟源:
1. HSE的128分频
2. LSE (用的最多)
3. LSI

三 相关寄存器
(1)备份域控制寄存器RCC_BDCR
这里写图片描述
LSEON,LSEBYP,RTCSEL和RTCEN位都处于备份域,这些位在复位后处于写保护的状态,只有在电源控制寄存器PWR_CR中的DBP位置1后才能对这些位进行改动;
RTCEN:RTC时钟使能位;0:RTC关闭;1:RTC开启
RTCSEL[1:0]:RTC时钟源选择 00:无时钟,01:LSE振荡器(用的最多),10:LSI振荡器,11:HSE振荡器/128
LSEBYP:外部低速时钟振荡器旁路(旁路可以理解为开关,被旁路既是被关闭);0:LSE时钟未被旁路(关闭);1:LSE时钟被旁路(关闭);
LSEON:外部低速振荡器使能;0:外部32KHz振荡器关闭;1:外部32KHz振荡器开启;

(2)控制寄存器(高位)RTC_CRH
这里写图片描述
RTC_CRH是用来控制RTC的相关中断的
OWIE:允许溢出中断位; 0:屏蔽RTC_CNT溢出中断;1:使能RTC_CNT溢出中断;
ALRIE:允许闹钟中断; 0:屏蔽闹钟中断;1:使能闹钟中断;
SECIE:允许秒中断; 0:屏蔽秒中断;1:使能秒中断;

(3)控制寄存器(低位)RTC_CRL(重要)
这里写图片描述
RTOFF(只读):RTC操作; 0:上次对RTC寄存器的写操作仍在进行;1:上次对RTC寄存器的写操作已经完成;对RTC寄存器操作时,必须要完成一次写操作之后(RTOFF=1)才能进行下次写操作
CNF:配置标志(重要); 在对ALR,DIV,CNT寄存器配置之前,必须先配置该位软件写1:进入如配置模式,对CNT,ALR,PRL进行赋值;0:退出配置模式(开始更新RTC寄存器);
RSF:寄存器同步标志;0:寄存器尚未同步;1:寄存器已经同步;
每当RTC_CNT和RTC_DIV由软件更新或清0时,此位由硬件置1;应用程序必须等待这位被硬件置1,以确保CNT,ALR,PRL已经被同步;
在APB1复位或APB1时钟停止后,此位必须由软件清0;
重要:由于APB1接口和RTC核心完全独立,处理器在复位,睡眠,停机环境下是继续工作的,但APB1接口停止了工作所以这里就出现了一个问题是:当处理器从复位睡眠停机情况下恢复过来的前一段时间,APB1接口和RTC核心还没有完成同步工作;此时不能读取RTC的值,不能对CRH/CRL进行配置,要等RSF=1后,才可以对RTC的值进行读取;

OWF:溢出标志; 0:CNT无溢出; 1:CNT溢出;
ALRF:闹钟标志: 0:CNT≠ALR,没有产生闹钟; 1:CNT≥ALR,闹钟产生;
SECF:秒标志; 0:RTC_DIV没有溢出;1:RTC_DIV溢出;

(4)RTC预分频装载寄存器RTC_PRLH(高)和RTC_PRLL(低)
这里写图片描述
这里写图片描述
这是两个寄存器(唉!搞不懂为什么要整两个,可能为后期分频系数增大做准备把)
分频公式为: Ftr_clk = Frtcclk/[19:0]+1
比如我们最常用的Frtc_clk = 32.768KHz ,([19:0]+1)=32768 ,代入得Ftr_clk = 1Hz = 1S;

(5)RTC预分频器余数寄存器RTC_DIVH(高)和RTC_DIVL(低)
这里写图片描述
这里写图片描述

(6)备份寄存器BKP
和其他寄存器不同,备份寄存器包含42个16位的寄存器(共84字节,中小容量STM芯片存储空间为20字节 );
功能
1. 侵入检测;2. RTC校准;3. (常用)用BKP来存储RTC的校验值或者记录些重要的数据,但是这里不要误理解为是EEPROM,功能类似,但是这个所在备份区域一旦完全掉电,就GG了;
特性:(重要)
1. BKP处于备份域,当Vdd掉电时,仍然有Vbat来供电。
2. 在系统待机唤醒,系统复位,掉电复位时,备份域内的数据将不会被复位,但一旦这些情况出现,备份域和RTC都不能被访问了,这是为了保护这些情况发生时备份域和RTC的值不会被改变;这时需要下面操作才能继续访问:
Ⅰ 配置寄存器RCC_APB1ENR的PWREN位和BKPEN位来打开电源和后备接口电源;
Ⅱ 电源控制寄存器PWR_CR的DBP位来使能对后备寄存器和RTC的访问;

四 编程步骤
(1)使能电源时钟和备份区域时钟;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE);
在上面解释备份寄存器中讲到的特性“Ⅰ”可以找到解释;

(2)取消备份区写保护;
PWR_BackupAccessCmd(ENABLE);
对备份区域写入保护是在每次复位后被使能,即复位后不能向备份区域写入数据或修改寄存器,RTC核心的属于备份区域,在进行时钟初始化的时候,不能对其写入数据是不合理的,所以必须要取消备份区写保护;在上面解释备份寄存器中讲到的特性“Ⅱ”可以找到解释;

(3)复位备份区;
BKP_DeInit();
在取消对备份区域的写保护完成后,备份区域可能还存在以前设置过的一些数据和配置;因为要重新配置,所以这里要初始化一下;

(4)开启外部低速振荡器
RCC_LSEConfig(RCC_LSE_ON); //配置RCC_BDCR的LSEON位,开启RTC常用的时钟源:LSE ;
特别注意:这个函数执行完毕后不能急着执行其他操作,因为是要启动外部晶振,所以需要一些时间;如果启动完成,备份域控制寄存器RCC_BDCR的LSERDY位将会被硬件置1;

(5)配置并使能RTC时钟源
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //配置RCC_BDCR的RTCSEL[1:0]位,三个时钟源,LSE最常用;
RCC_RTCCLKCmd(ENABLE); //配置RCC_BDCR的RTCEN位,启动RTC时钟
**特别注意:**RTC时钟源开启之后,需要等到时钟源RTC_CNT,ALR,PRL三个寄存器同步了,才能对RTC进行操作;
所以这里要等待一下:
RTC_WaitForSynchro(); //检测RTC_CRL的RSF位,知道同步完成,结束函数;

(6)配置RTC核心:分频和时钟;
Ⅰ RTC_EnterConfigMode(); //配置RTC_CRL的CNF位,允许配置
Ⅱ RTC_SetPrescaler( u32 ); //设置分频数 通常为32767
RTC_WaitForLastTask(); //等待分频数配置完成
Ⅲ RTC_Set(2009,12,2,10,0,55); //这个函数是自己写的;
Ⅳ RTC_ExitConfigMode(); //配置RTC_CRL的CNF位,禁止配置

(7)向备份区域写入完成时钟配置的信息;
BKP_WriteBackupRegister(BKP_DR1,0x5050);
//有写入函数必然会有读函数,关于这个应用的解释可以在上面的备份寄存器中找到解释;

补充:以上程序步骤中如果还要添加闹钟中断,秒中断,溢出中断,均可以添加;

下面为原子例程:

u8 RTC_Init(void){    u8 temp = 0;    if(BKP_ReadBackupRegister(BKP_DR1) != 0x5050)         {        RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | \                                                     RCC_APB1Periph_BKP,ENABLE);          PWR_BackupAccessCmd(ENABLE);                                                    BKP_DeInit();                                                                                       RCC_LSEConfig(RCC_LSE_ON);                                                      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_WaitForSynchro();               RTC_ITConfig(RTC_IT_SEC,ENABLE);           RTC_WaitForLastTask();               RTC_EnterConfigMode();        RTC_SetPrescaler(32767);             RTC_WaitForLastTask();             RTC_Set(2009,12,2,10,0,55);          RTC_ExitConfigMode();               BKP_WriteBackupRegister(BKP_DR1,0x5050);       }    else     {        RTC_WaitForLastTask();           RTC_ITConfig(RTC_IT_SEC,ENABLE);          RTC_WaitForLastTask();       }    RTC_NVIC_Config();        RTC_Get();                   return 0;               }
原创粉丝点击