目录
一、RTC简介
STM32 的 RTC 外设**(Real Time Clock)**,实质是一个掉电后还继续运行的定时器。从定时器的角度来说,相对于通用定时器 TIM 外设,它十分简单,只有很纯粹的计时和触发中断的功能;但从掉电还继续运行的角度来说,它却是 STM32 中唯一一个具有如此强大功能的外设。所以 RTC外设的复杂之处并不在于它的定时功能,而在于它掉电还继续运行的特性。
**以上所说的掉电,是指主电源 VDD 断开的情况,为了 RTC 外设掉电继续运行,必须接上锂电池给 STM32 的 RTC、备份发卡通过 VBAT 引脚供电。**当主电源 VDD 有效时,由 VDD 给 RTC 外设供电;而当 VDD 掉电后,由 VBAT 给 RTC 外设供电。但无论由什么电源供电, RTC 中的数据都保存在属于 RTC 的备份域中,若主电源 VDD 和 VBAT 都掉电,那么备份域中保存的所有数据将丢失。备份域除了 RTC 模块的寄存器,还有 42 个 16 位的寄存器可以在 VDD 掉电的情况下保存用户程序的数据,系统复位或电源复位时,这些数据也不会被复位。
二、RTC特性
它是一个 32 位的计数器,只能向上计数。它使用的时钟源有三种,分别为高速外部时钟的 128 分频(HSE/128)、低速内部时钟 LSI 以及低速外部时钟 LSE;使 HSE分频时钟或 LSI 的话,在主电源 VDD 掉电的情况下,这两个时钟来源都会受到影响,因此没法保证 RTC 正常工作。因此 RTC 一般使用低速外部时钟 LSE,在设计中,频率通常为实时时钟模块中常用的 32.768KHz,这是因为 32768 = 2^15,分频容易实现,所以它被广泛应用到 RTC 模块。在主电源 VDD 有效的情况下 (待机), RTC 还可以配置闹钟事件使 STM32 退出待机模式。
三、RTC外设框图解析
一般使用LSI,LSE:内部低速时钟、外部低速时钟。
**框图中浅灰色的部分都是属于备份域的,在 VDD 掉电时可在 VBAT 的驱动下继续运行。**这部分仅包括 RTC 的分频器,计数器,和闹钟控制器。若 VDD 电源有效, RTC 可以触发 RTC_Second(秒中断)、 RTC_Overflow(溢出事件) 和 RTC_Alarm(闹钟中断)。从结构图可以分析到,其中的定时器溢出事件无法被配置为中断。若 STM32 原本处于待机状态,可由闹钟事件或 WKUP 事件 (外部唤醒事件,属于 EXTI 模块,不属于 RTC) 使它退出待机模式。闹钟事件是在计数器 RTC_CNT 的值等于闹钟寄存器 RTC_ALR 的值时触发的。
四、配置原理
在配置 RTC 模块的时钟时,通常把输入的 32768Hz 的 RTCCLK 进行 32768 分频得到实际驱动计数器的时钟 TR_CLK =RTCCLK/32768= 1 Hz,计时周期为 1 秒,计时器在 TR_CLK 的驱动下计数,即每秒计数器 RTC_CNT 的值加 1。
系统复位后,默认禁止访问后备寄存器和 RTC,防止对后备区域 (BKP) 的意外写操作。执行以下操作使能对后备寄存器和 RTC 的访问:
(1) 设置 RCC_APB1ENR 寄存器的 PWREN 和 BKPEN 位来使能电源和后备接口时钟。
(2) 设置 PWR_CR 寄存器的 DBP 位使能对后备寄存器和 RTC 的访问。
**设置后备寄存器为可访问后,在第一次通过 APB1 接口访问 RTC 时,因为时钟频率的差异,所以必须等待 APB1 与 RTC 外设同步,确保被读取出来的 RTC 寄存器值是正确的。**若在同步之后,一直没有关闭 APB1 的 RTC 外设接口,就不需要再次同步了。
如果内核要对 RTC 寄存器进行任何的写操作,在内核发出写指令后, RTC 模块在 3 个 RTCCLK时钟之后,才开始正式的写 RTC 寄存器操作。由于 RTCCLK 的频率比内核主频低得多,所以每次操作后必须要检查 RTC 关闭操作标志位 RTOFF,当这个标志被置 1 时,写操作才正式完成。
五、UNIX时间戳
使用 RTC 外设前,还需要引入 UNIX 时间戳的概念。
如果从现在起,把计数器 RTC_CNT 的计数值置 0,然后每秒加 1, RTC_CNT 什么时候会溢出呢?由于 RTC_CNT 是 32 位寄存器,可存储的最大值为 (2^32-1),即这样计时的话,在 2^32 秒后溢出,即它将在今后的 136 年时溢出。
假如某个时刻读取到计数器的数值为 X = 60*60*24*2,即两天时间的秒数,而假设又知道计数器是在 2011 年 1 月 1 日的 0 时 0 分 0 秒置 0 的,那么就可以根据计数器的这个相对时间数值,计算得这个 X 时刻是 2011 年 1 月 3 日的 0 时 0 分 0 秒了。而计数器则会在 (2011+136) 年左右溢出,也就是说到了(2011+136)年时,如果我们还在使用这个计数器提供时间的话就会出现问题。在这个例子中,定时器被置 0 的这个时间被称为计时元年,相对计时元年经过的秒数称为时间戳,也就是计数器中的值。
这个时间戳和计时元年大家都取了同一个标准------UNIX 时间戳和 UNIX 计时元年。 UNIX 计时元年被设置为格林威治时间1970 年 1 月 1 日 0 时 0 分 0 秒,大概是为了纪念 UNIX 的诞生的时代吧,而 UNIX 时间戳即为当前时间相对于 UNIX 计时元年经过的秒数。
在这个计时系统中,使用的是有符号的 32 位整型变量来保存 UNIX 时间戳的,即实际可用计数位数比我们上面例子中的少了一位,少了这一位, UNIX 计时元年也相对提前了,这个计时方法在 2038 年 1 月 19 日 03 时 14 分 07 秒将会发生溢出,这个时间离我们并不远。