轻松搞懂STM32F4实时时钟RTC

闲聊:最近STM32价格真的是越涨越高,感觉STM32很快会被其他产品取代,到时候又要再学。最近大学生电子设计竞赛已经推荐使用TI的芯片了。但是同级别芯片里就32的资源最全,哪位大佬来指点指点这嵌入式的路该咋走,好难啊x_x。

 

切入正题:

RTC(Real Time Clock)实时时钟,主要作用就是使单片机可以得到准确的日历时间。

先祭出官方时钟图

建议下载个Snipaste截图工具,可以把截图钉在屏幕上,这样就可以边看文章边看框图,不用上下来回翻了^_^

 

1:得到日历与亚秒时间

第一步是选择时钟,RTC的时钟来源可以为三个来源,LSE,LSI,HSE(不知道这是啥的请移步我的另一篇文章,轻松搞懂STM32F4时钟树)。

日历的时间的实现:时钟发出的脉冲频率进入RTC,先校准一下得到RTCCLK。之后通过7位分频器进行分频。得到ck_apre,ck_apre再经过15位分频器得到ck_spre,经过两个分频器期望得到一个1Hz的ck_spre。也就是ck_spre的周期为1秒,这样就给日历寄存器(日历寄存器有两个,一个提供时间,一个提供日期)提供了时钟。这个日历寄存器是以ck_spre作为计数。当ck_spre每发送一次时钟脉冲,日历寄存器加一秒,并自动进位小时,日期,月份等,并且会自动算上润年润月等。

ck_spre的周期Fck_spre计算公式为:Fck_spre = FRTCCLK / (七位分频器 * 15位分频器);

亚秒的时间的实现:除了日历寄存器外还有一个亚秒寄存器,作用是得到亚秒的时间。亚秒就是次于秒的时间,通过定义亚秒,他和定时器的计数寄存器的向下计数是非常像的。首先从15位分频器的分频数作为一个初值放入亚秒寄存器,随后亚秒根据ck_apre递减。当递减到0时,15位分频器作为自动重载寄存器将其分频数重新放入亚秒寄存器。这样就得到了更精确的时间。亚秒的时钟也就是日历时钟Fck_spre*(1 /  (15位分频器数的值+1)  ),如Fck_spre = 1Hz,15位自动重装载寄存器的值为255,那么亚秒寄存器的时钟Fck_apre = 1 * (1/255+1) = 256分之1秒。

注:有童鞋可能会问,直接使用LSE的时钟不行么,非要搞这么复杂干啥。但是实际上这样做是有一定的考虑的,当时钟进来后,我们尽可能的在满足需求的同时使用小一些的频率,这样可以大大减少运行的功耗。那么为什么不把日历寄存器和亚秒寄存器做成一个寄存器,只不过是在日历寄存器的最小位后加上代表亚秒的几位就可以了,为什么要这样复杂呢?这一共有两个方面的考虑。一方面原因是如果不需要亚秒这样精确的时间的话,就可以节省一些功耗,一方面是如果我们需要亚秒这样的时间的话,就可以根据需求和功耗的限制,自由定义亚秒的单位时间。在实际工作或者比赛,如电子设计竞赛,在完成需求的情况下,越优的功耗和成本就可以打越高的分!

时间的存储:以BCD码为存储方式的计算日历的时钟。BCD就是把十进制数字用四个bit位来存储,就如存储十进制三二进制为0011,那么存储就是第一位1,二位1,三位0,四位0。也可以使用寄存器的两位存储一位和二位为1。(这里使用的是8421标准,其他标准请自行百度)

影子寄存器:无论是日历寄存器还是亚秒寄存器,都各自有一个影子寄存器,这样可以避免对本体寄存器的误操作。当然也可以直接对本体寄存器进行读写。每过两个RTCCLK周期本体寄存器就会自动更新到影子寄存器中去。每更新一次都会有相应的寄存器位置1,并由硬件自动置为或者软件自动置为。当处于停机和待机状态下不会更新。(停机和待机状态为STM32低功耗部分的内容,在这里不做过多介绍  (其实我TM也不会,慢慢来把,学完一样学一样)  )。影子寄存器的复位为系统复位,按下复位按键就属于系统复位,在程序里也有系统复位的库函数。

 

除了日历外,RTC还具备可编程闹钟功能

两个闹钟,一个A一个B,当闹钟寄存器内的时间与日历寄存器以及亚秒定时器中的值相等,则触发某个寄存器位置为,如果使能闹钟中断的话还会触发闹钟中断,闹钟中断会发送电平信号到对应的引脚上。并可选择中断电平的极性(高低电平选择)。在比较日历寄存器与闹钟寄存器的值的时,使用掩码,这玩意就是那些需要相等,那些不需要,如日期需要小时不需要。那么就掩住小时。在库函数环境开发时有个固定函数设置那些位是有效的,那些位是无效的(即掩住);需要注意的是,当选择闹钟的秒为有效时,同步15位分配器分频系数必须大于3。

除了日历和闹钟外,RTC还具备了自动可唤醒定时器功能。

自动唤醒功能由一个16位计数器组成,由LSE或ck_spre提供时钟,当时钟为LSE(32.768 kHz)且四位预分配器分频系数为不分频时,定时范围为122微妙到32秒之间(正经人谁用LSE做自动唤醒?那不纯粹给自己找麻烦么)。当时钟为ck_spre(1Hz)时,定时范围为1秒到36小时,且定时分为两部分,一部分为1s到18h,另一部分为18h到36h。通过寄存器选择使用那个范围进行定时。其实也就是说2的16次方为65535,而65535 = 18小时。那么一个16位计数器在1Hz频率输出下最多计时18小时。而STM32为了放大这以时间,在硬件优化为了从0s计时到从18h计时。这挺牛逼的,非常方便!而且像手机一样,手机在设置定时开关机时,关机状态下也在计时否则就不知道何时开机。32也一样,自动唤醒定时器在系统复位以及关机,低功耗模式,等情况下,也依旧在计时!!!需要注意的是,唤醒标志位需要软件置位!自动唤醒功能也会产生中断,可以由RTC_ALARM输出。但是当中断被使能时,会使器件推出低功耗模式!!!

除了日历和闹钟以及自动唤醒外,还具备了时间戳功能以及入侵检测

这玩意就是当发生时间戳中断时记录当前的日历信息用的,可以设置入侵的极性(还可以设置采样等方式),当在对应的复用引脚上检测到对应的极性电平,则记录电平发生的时间。入侵检测的方式请自行翻阅手册(太多了,用到的时候翻手册吧,各个版本都不同没必要全记住)。

寄存器保护功能:

让我非常服气的是写入RTC寄存器居然还需要先输入密码,这你要是没注意到这点又没好好看手册,估计死活就卡这了。0XCA写入RTC_WPR,再写入0X53再次写入到RTC_WPR,切记配置寄存器的时候最好不要出错,如果一但出错就立马会被锁上,然后必须再次输入密码,而且这个保护机制免疫系统复位以及断电等操作!!!

 

RTC部分就到这里,因为一些缘故恕我不能公开代码。这篇文章从中午写到晚上10点左右,查阅了很多资料。如果作为一个初学者单去看野火或者正电的文章很多东西是不全的,比如野火的里面有时间戳但是没有自动唤醒,正电的里面没有时间戳但是有自动唤醒,而作为一个初学者直接去看官方手册又非常头疼。那么我便花出时间来,一方面为了让我的底子更加扎实,一方面为了记录我的学习路程,更重要的一方面是为了结合资料,结合出一份即通俗易懂,又比较全面的文章来。能让大家少走点坑。如有建议欢迎评论。

 

另外希望各位土豪们能打个赏啥的,让我买瓶护发素什么的。

 

  • 9
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是基于STM32F4xx的RTC实时时钟代码,包括初始化和设置时间的函数: ```c #include "stm32f4xx.h" void RTC_Init(void) { /* 使能PWR和BKP的时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); /* 解锁BKP区域 */ PWR_BackupAccessCmd(ENABLE); /* 复位BKP区域 */ BKP_DeInit(); /* 使能LSE时钟 */ RCC_LSEConfig(RCC_LSE_ON); while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) { } /* 选择LSE作为RTC时钟源 */ RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); /* 使能RTC时钟 */ RCC_RTCCLKCmd(ENABLE); /* 等待RTC同步 */ RTC_WaitForSynchro(); /* 设置RTC预分频器 */ RTC_SetPrescaler(32767); /* 等待RTC同步 */ RTC_WaitForSynchro(); } void RTC_SetTime(uint8_t hour, uint8_t minute, uint8_t second) { RTC_TimeTypeDef RTC_TimeStructure; /* 等待RTC同步 */ RTC_WaitForSynchro(); /* 设置RTC时间 */ RTC_TimeStructure.RTC_Hours = hour; RTC_TimeStructure.RTC_Minutes = minute; RTC_TimeStructure.RTC_Seconds = second; RTC_TimeStructure.RTC_H12 = RTC_H12_AM; RTC_SetTime(RTC_Format_BIN, &RTC_TimeStructure); /* 等待RTC同步 */ RTC_WaitForSynchro(); } ``` 在主函数中,可以先调用RTC初始化函数,然后再调用RTC设置时间函数,例如: ```c int main(void) { /* 初始化RTC */ RTC_Init(); /* 设置RTC时间为12:34:56 */ RTC_SetTime(12, 34, 56); while (1) { } } ``` 需要注意的是,RTC模块需要连接外部低速晶振(LSE),并且需要在STM32的RCC寄存器中设置LSE作为RTC时钟源。同时,还需要在PWR寄存器中解锁BKP区域,才能够使用RTC模块。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值