STM32 里面RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后,RTC的设置和时间维持不变。因为系统对后备寄存器和RTC相关寄存器有写保护,所以如果想要对后备寄存器和RTC进行访问,则需要通过操作相应的寄存器来解除某些限制。
文章有点长,想要理解RTC原理的可以认真阅读。如果只想要源码的,可以直接拉到文章最下面
接下来进入正题:
一、解除寄存器操作限制
第一步首先需要设置RCC_APB1ENR的PWREN和BKPEN位。使能电源和后备接口时钟
第二步是设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问
二、配置RTC
完成上面两步之后,我们就可以操作后备寄存器和RTC相关的寄存器了。但我们先来看看RTC的简单框图吧
从图可以看出来,要想让RTC工作,得让它先有一个时钟,也就是图中RTCCLK时钟的选择。
1、RTC时钟的选择
RTCCLK时钟源可以由HSE/128、LSE或LSI时钟提供。除非备份域复位,此选择不能被改变。
LSE时钟在备份域里,但HSE和LSI时钟不是。因此:
● 如果LSE被选为RTC时钟:
─ 只要V BAT 维持供电,尽管V DD 供电被切断,RTC仍继续工作。
● 如果LSI被选为自动唤醒单元(AWU)时钟:
─ 如果V DD 供电被切断, AWU状态不能被保证。
● 如果HSE时钟128分频后作为RTC时钟:
─ 如果V DD 供电被切断或内部电压调压器被关闭(1.8V域的供电被切断),则RTC状态不确定。
RTC的时钟源有三个,但只有LSE(外部低速振荡器,一般为32.678kHz)在VDD供电被切断后,仍能继续工作,因此我们一般都选择它。
RTC时钟源的选择需要操作备份域控制寄存器(RCC_BDCR)
配置时钟步骤
1)打开外部振荡器(LSEON置1)
2)然后等待LSE就绪,也就是等待LSERDY置1
3)选择RTC时钟源,也就是配置RTCSEL[1:0]
4)使能RTC时钟(RTCEN置1)
2、配置RTC相关寄存器
从RTC框图可以知道,RTC时钟选择后就应该配置RTC预分频器(RTC_PRL寄存器)和RTC_CNT计数器和闹钟计数器RTC_ALR。
一般我们通过预分频器将RTCCLK的时钟进行分频,让预分频器的输出时钟TR_CLK的频率变成1Hz,也就是周期为1s。然后RTC_CNT在TR_CLK频率下递增。如果RTC_CNT里面的值和RTC_ALR里面的数值相等,则会触发闹钟标志,即ALRF标志位置1。在每个TR_CLK的周期都会触发一次秒标志,即SECF标志位会置1.
一般如果用于时钟时钟的话,RTC_CNT可以设置为当前的时间。
如果需要配置RTC的 RTC_PRL、RTC_CNT、RTC_ALR寄存器。则必须判断RTC寄存器是否处于更新中,只有RTC寄存器不是处于跟新中才可以进行配置,可以通过RTC_CR寄存器里面的RTOFF位来判断。在配置前还必须将RTC_CRL寄存器里面的CNF位置1,进入配置模式,等待配置后,还要退出配置模式。
配置过程
1)查询RTOFF位,直到RTOFF的值变为‘1’
2)置CNF为1,进入配置模式
3)对一个或多个RTC寄存器进行写操作
4)清除CNF标志位,退出配置模式
5)查询RTOFF,直到RTOFF变为1,才代表写操作完成
注意:只有当CNF标志位被清除时,写操作才能进行,这个过程至少需要3个RTCCLK周期
在正式进入配置之前我们先来看看RTC几个寄存器
RTC控制寄存器高位RTC_CRH
这几位是用来使能中断的,可以配合前面的RTC框图“食用”
RTC控制寄存器低位RTC_CRL
注意:标志位都需要由软件清零
RTC预分频转载寄存器(RTC_PRLH/RTC_PRLL)
该寄存器是用于配置预分频器的分频比的,只有前20位有效,即PRL[19:0]有效,总共20位。
时钟计算公式fTR_CLK = fRTCCLK /(PRL[19:0]+1)。
当LSE位32.678kHz时,只需将RTC_PRLL配置成32677即可。
RTC计数器寄存器(RTC_CNTH/RTC_CNTL)
该32寄存器可以通过配置来设定初值,并且在TR_CLK的基准下进行计数
RTC闹钟寄存器(RTC_ALRH/RTC_ALRL)
该32位寄存器用来配置闹钟的数值。
现在基本知识框架都已经介绍好了正式进入配置阶段
首先我先给出直接操作寄存器的版本,后面我也会给出操作固件库的版本。
我相信通过前面的讲解,直接操作寄存器反而会更简单!代码也很容易看懂
//寄存器版本
void RTC_Init(void)
{
//这里是第一步解除写保护
RCC->APB1ENR |= RCC_APB1ENR_PWREN;//电源接口时钟使能
RCC->APB1ENR |= RCC_APB1ENR_BKPEN;//备份接口时钟开启
PWR->CR |= PWR_CR_DBP;//允许写入RTC和后备寄存器
//这里是第二步进入配置
RCC->BDCR |= RCC_BDCR_LSEON;//打开外部32kHz振荡器
while(!(RCC->BDCR & RCC_BDCR_LSERDY));//等待外部32kHz振荡器就绪
RCC->BDCR |= RCC_BDCR_RTCSEL_LSE;//选择外部32kHz振荡器作为RTC时钟源
RCC->BDCR |= RCC_BDCR_RTCEN;//RTC时钟使能
while(!(RTC->CRL &RTC_CRL_RTOFF));//等待上一次写操作完成
RTC->CRL |= RTC_CRL_CNF;//进入配置模式
RTC->PRLL = 32767;//fTR_CLK = fRTCCLK /(PRL[19:0]+1),周期为1Hz
RTC->CNTL = 0;//配置当前时间
RTC->CNTH = 0;
RTC->ALRH = 0;//配置闹钟时间
RTC->ALRL = 2;
RTC->CRH |= (RTC_CRH_ALRIE + RTC_CRH_SECIE);//使能秒中断和闹钟中断
RTC->CRL &= ~(RTC_CRL_CNF);//退出配置模式
}
我们一些喜欢嵌入式的朋友一起建立的一个技术交流平台,本着大家一起互相学习的心态而建立,不太成熟,希望志同道合的朋友一起来。QQ1群372991598