【菜鸟入门】stm32 之 实时时钟
最后更新于:2022-04-01 14:50:51
经过这么10天的瞎搞,我的库已经初具规模了,于是,不用每次都把所有的文件copy过去,直接在Option里面把path给加上就ok了。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576915b5db5af.jpg)
RTC的时钟配置,RTC的时间寄存器是2个32位的寄存器,无非就是一个计数器,大概可以这样理解吧,我们先看看时钟吧
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576915b6a84b0.jpg)
RTC的时钟可以从这3路来,我们需要PTCSEL寄存器来进行设置,
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576915b6be9d4.jpg)
上面这个图是摘自李想老师的课件里面的,我觉得这个是做的相对好的!
位了保证RTC正常工作,我们需要在系统断电时,RTC不受影响,当然我们一般都需要接一个Battery,作为rtc的后备电源,这里设计到电源管理,我们先来看看电源管理里面关于rtc的
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576915b6e19d0.jpg)
只要我们把第八位置1我们就可以对其进行正常供电,我们还发现,他也可以给后备寄存器供电,这个后备寄存器是是个什么东东呢?
有兴趣的可以研究研究备份寄存器(BKP),他的主要功能是侵入检查和RTC校准,他既然跟RTC有关系,我们就要好好看看他了;
复位和时钟控制里面有个备份域控制寄存器RCC_BDCR
注意: 备份域控制寄存器中(RCC_BDCR)的LSEON、LSEBYP、RTCSEL和RTCEN位处于备份域。因
此,这些位在复位后处于写保护状态,只有在电源控制寄存器(PWR_CR)中的DBP位置’1’后才
能对这些位进行改动。进一步信息请参考5.1节。这些位只能由备份域复位清除(见6.1.3节)。任
何内部或外部复位都不会影响这些位。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576915b71068b.jpg)
位31:17 保留,始终读为0。
BDRST:备份域软件复位(Backup domain software reset) 位16
由软件置’1’或清’0’
0:复位未激活;
1:复位整个备份域。
RTCEN:RTC时钟使能(RTC clock enable) 位15
由软件置’1’或清’0’
0:RTC时钟关闭;
1:RTC时钟开启。
位14:10 保留,始终读为0。
RTCSEL[1:0]:RTC时钟源选择(RTC clock source selection) 位9:8
由软件设置来选择RTC时钟源。一旦RTC时钟源被选定,直到下次后备域被复位,它不能在被
改变。可通过设置BDRST位来清除。
00:无时钟;
01:LSE振荡器作为RTC时钟;
10:LSI振荡器作为RTC时钟;
11:HSE振荡器在128分频后作为RTC时钟。
位7:3 保留,始终读为0。
LSEBYP:外部低速时钟振荡器旁路(External low-speed oscillator bypass) 位2
在调试模式下由软件置’1’或清’0’来旁路LSE。只有在外部32kHz振荡器关闭时,才能写入该位
0:LSE时钟未被旁路;
1:LSE时钟被旁路。
LSERDY:外部低速LSE就绪(External low-speed oscillator ready) 位1
由硬件置’1’或清’0’来指示是否外部32kHz振荡器就绪。在LSEON被清零后,该位需要6个外部
低速振荡器的周期才被清零。
0:外部32kHz振荡器未就绪;
1:外部32kHz振荡器就绪。
LSEON:外部低速振荡器使能(External low-speed oscillator enable) 位0
由软件置’1’或清’0’
0:外部32kHz振荡器关闭;
1:外部32kHz振荡器开启。
看来这一寄存器果真与RTC有很大的联系,我们需要启用外部32K的振荡器,所以RCC->BDCR |= 1<<0;
设置完了,我们还需要等待32K的时钟就绪,判断bit1的状态!
由于我们选用的32K的LSE作为RTC的时钟,所以上面我们提到的RTCSEL寄存器必须设置为1,设置完后我们就开启32K时钟
RCC->BDCR |= 1<<8;
RCC->BDCR |= 1<<15;
下面正式看RTC的寄存器,先从低位控制寄存器开始CRL
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576915b7261af.jpg)
位15:6 保留,被硬件强制为0。
位5 RTOFF:RTC操作关闭(RTC operation OFF) 位5
RTC模块利用这位来指示对其寄存器进行的最后一次操作的状态,指示操作是否完成。若此位
为’0’,则表示无法对任何的RTC寄存器进行写操作。此位为只读位。
0:上一次对RTC寄存器的写操作仍在进行;
1:上一次对RTC寄存器的写操作已经完成。
位4 CNF:配置标志(Configuration flag) 位4
此位必须由软件置’1’以进入配置模式,从而允许向RTC_CNT、RTC_ALR或RTC_PRL寄存器
写入数据。只有当此位在被置’1’并重新由软件清’0’后,才会执行写操作。
0:退出配置模式(开始更新RTC寄存器);
1:进入配置模式。
位3 RSF:寄存器同步标志(Registers synchronized flag)
每当RTC_CNT寄存器和RTC_DIV寄存器由软件更新或清’0’时,此位由硬件置’1’。在APB1复位
后,或APB1时钟停止后,此位必须由软件清’0’。要进行任何的读操作之前,用户程序必须等待
这位被硬件置’1’,以确保RTC_CNT、RTC_ALR或RTC_PRL已经被同步。
0:寄存器尚未被同步;
1:寄存器已经被同步。
位2 OWF:溢出标志(Overflow flag) 位2
当32位可编程计数器溢出时,此位由硬件置’1’。如果RTC_CRH寄存器中OWIE=1,则产生中
断。此位只能由软件清’0’。对此位写’1’是无效的。
0:无溢出;
1:32位可编程计数器溢出。
位1 ALRF:闹钟标志(Alarm flag) 位1
当32位可编程计数器达到RTC_ALR寄存器所设置的预定值,此位由硬件置’1’。如果RTC_CRH
寄存器中ALRIE=1,则产生中断。此位只能由软件清’0’。对此位写’1’是无效的。
0:无闹钟;
1:有闹钟。
位0 SECF:秒标志(Second flag) 位0
当32位可编程预分频器溢出时,此位由硬件置’1’同时RTC计数器加1。因此,此标志为分辨率可
编程的RTC计数器提供一个周期性的信号(通常为1秒)。如果RTC_CRH寄存器中SECIE=1,则
产生中断。此位只能由软件清除。对此位写’1’是无效的。
0:秒标志条件不成立;
1:秒标志条件成立。
感觉CRL更像SR,我在想为什么他有点功能不放到SR的里面呢?
好吧,CRL里的功能说的很清楚,看着不会有什么异议,我这里就不解释了,直接掠过,包括CRH。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576915b73a0f2.jpg)
在RTC计数器寄存器里面和RTC闹钟寄存器里面有这么一段话
RTC核有一个32位可编程的计数器,可通过两个16位的寄存器访问。计数器以预分频器产生的
TR_CLK时间基准为参考进行计数。RTC_CNT寄存器用来存放计数器的计数值。他们受
RTC_CR的位RTOFF写保护,仅当RTOFF值为’1’时,允许写操作。在高或低寄存器
(RTC_CNTH或RTC_CNTL)上的写操作,能够直接装载到相应的可编程计数器,并且重新装载
RTC预分频器。当进行读操作时,直接返回计数器内的计数值(系统时间)。
当可编程计数器的值与RTC_ALR中的32位值相等时,即触发一个闹钟事件,并且产生RTC闹钟
中断。此寄存器受RTC_CR寄存器里的RTOFF位写保护,仅当RTOFF值为’1’时,允许写操作。
所以我们在配置RTC_CNTx RTC_ALRx 寄存器时,不行把RTOFF寄存器置为1,当写完之后将CNT设为1,即进入配置模式,等待RTOFF配置完成,即RTOFF自动置为0,才完成对CNTx和ALRx两个寄存器进行修改!
为了实现计数的时间间隔,我们要对RTC预分频装载寄存器进行配置
我们需要1s钟计时一次,而我们用的是LSE 32KHz的振荡器,所以我们需要配置的分频器是?
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576915b78b6c5.jpg)
可以看出我们只需让RTC_PRLL = 0x7fff 即 32767即可得到1s的周期
当然我们需要两秒的话那就是0xffff了。
这样,整个就配置完成了,下面附上我的代码,大家可以研究下!
~~~
#include <stm32f10x.h>
#include "init.h"
#include "usart.h"
#define RTC_CF 0x01CD //Define RTC Config Flag
int rtc_init()
{
u8 temp = 0;
if(BKP->DR1 != RTC_CF)
{
RCC->APB1ENR |= 1<<28; //Power Interface Clock Enable
RCC->APB1ENR |= 1<<27; //Backup Interface Clock Enable
PWR->CR |= 1<<8; //Disable backup domain write protection
RCC->BDCR |= 1<<16;
RCC->BDCR &= ~(1<<16);
RCC->BDCR |= 1<<0;
while((!(RCC->BDCR&1<<1))&&(temp++)<250)
delay_ms(10);
if(temp>=250)return -1;
RCC->BDCR |= 1<<8;
RCC->BDCR |= 1<<15;
while(!(RTC->CRL & (1<<5)));
while(!(RTC->CRL & (1<<3)));
RTC->CRH |= 1<<0;
while(!(RTC->CRL & (1<<5)));
/* Config Time */
RTC->CRL |= 1<<4;
RTC->PRLH = 0;
RTC->PRLL = 32767;
RTC->CNTH = 0;//Config time
RTC->CNTL = 0;
RTC->ALRH = 0;
RTC->ALRL = 20;
RTC->CRL &= ~(1<<4);
while(!(RTC->CRL & (1<<5)));
BKP->DR1 = RTC_CF;
}
else{
while(!(RTC->CRL & (1<<3)));
RTC->CRH |= 1<<0;
while(!(RTC->CRL & (1<<5)));
}
init_interrupt(2,3,3,2);
rs232_send_int(RTC->CNTL);
return 0;
}
void RTC_IRQHandler(void)
{
rs232_send_str("INTER\n",6);
if(RTC->CRL & (1<<0))
{
rs232_send_int(RTC->CNTL);
rs232_send_byte('\n');
}
if(RTC->CRL & (1<<1))
{
RTC->CRL |= 1<<4;
RTC->CNTL = 1;
RTC->CRL &= ~(1<<4);
rs232_send_str("Time==>\n",8);
}
RTC->CRL &= ~(7<<0);//Clear all interrupt flag
while(!(RTC->CRL & (1<<5)));
}
~~~
多谢各位指导!