STM32 —— RTC 入门

STM32 —— RTC 入门

简介

STM32 的 RTC 外设(Real Time Clock) ,实质是一个掉电后还继续运行的定时器。定时器的角度来说,相对于通用定时器 TIM 外设,它十分简单,只有很纯粹的计时和触发中断的功能;但从掉电还继续运行的角度来说,它却是 STM32 中唯一一个具有如此强大功能的外设。所以 RTC 外设的复杂之处并不在于它的定时功能,而在于它掉电还继续运行的特性。RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期

以上所说的掉电,是指主电源 VDD 断开的情况,为了 RTC外设掉电继续运行,必须接上锂电池给 STM32 的 RTC、备份发卡通过 VBAT引脚供电。当主电源 VDD有效时,由 VDD 给 RTC 外设供电; 而当 VDD掉电后,由 VBAT给 RTC 外设供电。但无论由什么电源供电,RTC 中的数据都保存在属于 RTC 的备份域中,若主电源 VDD 和 VBAT 都掉电,那么备份域中保存的所有数据将丢失。 备份域除了 RTC 模块的寄存器,还有 42 个 16 位的寄存器可以在 VDD 掉电的情况下保存用户程序的数据,系统复位或电源复位时,这些数据也不会被复位

RTC 模块和时钟配置系统( RCC_BDCR 寄存器)处于后备区域,即在系统复位或从待机模式唤醒后, RTC 的设置和时间维持不变

系统复位后,对后备寄存器和 RTC 的访问被禁止,这是为了防止对后备区域(BKP)的意外写操作。执行以下操作将使能对后备寄存器和RTC的访问:

· 设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟

· 设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问。

主要特性

可编程的预分频系数:分频系数最高为 \(2^{20}\)

32 位的可编程计数器,可用于较长时间段的测量,只能向上计数 。

2 个分离的时钟:用于 APB1 接口的 PCLK1 和 RTC 时钟( RTC 时钟的频率必须小于 PCLK1 时钟频率的四分之一以上)。

可以选择以下三种RTC的时钟源:

· 高速外部时钟的 128 分频 HSE / 128

· 低速内部时钟 LSE 振荡器时钟

· 低速外部时钟 LSI 振荡器时钟

使 HSE 分频时钟或 LSI 的话,在主电源 VDD掉电的情况下,这两个时钟来源都会受到影响,因此没法保证 RTC 正常工作。因此 RTC 一般使用低速外部时钟 LSE,在设计中, 频率通常为实时时钟模块中常用的 32.768KHz,这是因为 32768 = 215,分频容易实现,所以它被广泛应用到 RTC 模块,在主电源 VDD有效的情况下(待机), RTC 还可以配置闹钟事件使 STM32 退出待机模式

2 个独立的复位类型:

· APB1 接口由系统复位;

· RTC 核心(预分频器、闹钟、计数器和分频器)只能由后备域复位。

3 个专门的可屏蔽中断:

· 闹钟中断,用来产生一个软件可编程的闹钟中断。

· 秒中断,用来产生一个可编程的周期性中断信号(最长可达1秒)。

· 溢出中断,指示内部可编程计数器溢出并回转为0的状态。

主要功能

功能框图

RTC 由两个主要部分组成。第一部分( APB1 接口)用来和 APB1 总线相连。此单元还包含一组 16 位寄存器,可通过 APB1 总线对其进行读写操作. APB1 接口由 APB1 总线时钟驱动,用来与 APB1 总线接口。

另一部分( RTC 核心)由一组可编程计数器组成,分成两个主要模块。第一个模块是RTC的预分频模块,它可编程产生最长为1秒的 RTC 时间基准 TR_CLK 。RTC 的预分频模块包含了一个20位的可编程分频器( RTC 预分频器)。如果在 RTC_CR 寄存器中设置了相应的允许位,则在每个 TR_CLK 周期中 RTC 产生一个中断(秒中断)。第二个模块是一个 32 位的可编程计数器,可被初始化为当前的系统时间。系统时间按 TR_CLK 周期累加并与存储在 RTC_ALR 寄存器中的可编程时间相比较,如果 RTC_CR 控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断

框图中浅灰色的部分都是属于备份域的,在VDD掉电时可在VBAT的驱动下继续运行。这部分仅包括 RTC 的分频器,计数器,和闹钟控制器。若 VDD 电源有效, RTC 可以触发 RTC_Second(秒中断)、 RTC_Overflow(溢出事件)和 RTC_Alarm(闹钟中断)。从结构图可以分析到,其中的定时器溢出事件无法被配置为中断。若 STM32 原本处于待机状态,可由闹钟事件或 WKUP 事件(外部唤醒事件,属于 EXTI 模块,不属于 RTC)使它退出待机模式。闹钟事件是在计数器 RTC_CNT 的值等于闹钟寄存器 RTC_ALR 的值时触发的

在备份域中所有寄存器都是 16 位的, RTC 控制相关的寄存器也不例外。 它的计数器 RTC_CNT 的 32 位由 RTC_CNTL 和 RTC_CNTH 两个寄存器组成,分别保存定时计数值的低 16 位和高 16 位。在配置 RTC 模块的时钟时,通常把输入的 32768Hz 的 RTCCLK 进 32768 分频得到实际驱动计数器的时钟 TR_CLK = RTCCLK/32768= 1 Hz,计时周期为1秒,计时器在 TR_CLK 的驱动下计数,即每秒计数器 RTC_CNT 的值加 1

由于备份域的存在,使得 RTC 核具有了完全独立于 APB1 接口的特性,也因此对 RTC 寄存器的访问要遵守一定的规则

复位

除了 RTC_PRL、 RTC_ALR、 RTC_CNT 和 RTC_DIV 寄存器外,所有的系统寄存器都由系统复位或电源复位进行异步复位

RTC_PRL、 RTC_ALR、 RTC_CNT 和 RTC_DIV 寄存器仅能通过备份域复位信号复位

系统复位后,默认禁止访问后备寄存器和 RTC,防止对后备区域(BKP)的意外写操作。执行以下操作使能对后备寄存器和 RTC 的访问:

  1. 设置 RCC_APB1ENR 寄存器的 PWREN 和 BKPEN 位来使能电源和后备接口时钟。

  2. 设置 PWR_CR 寄存器的 DBP 位使能对后备寄存器和 RTC 的访问

读 RTC 寄存器

RTC 核完全独立于 RTC 的 APB1 接口

软件通过APB1接口访问RTC的预分频值、 计数器值和闹钟值。但是,相关的可读寄存器只在与 RTC 的 APB1时钟进行重新同步的RTC时钟的上升沿被更新。 RTC标志也是如此的

这意味着,如果 APB1 接口曾经被关闭,而读操作又是在刚刚重新开启 APB1 之后,则在第一次的内部寄存器更新之前,从 APB1 上读出的 RTC 寄存器数值可能被破坏了(通常读到 0 )。下述几种情况下能够发生这种情形:

  1. 发生系统复位或电源复位

  2. 系统刚从待机模式唤醒

  3. 系统刚从停机模式唤醒(参见第4.3节: 低功耗模式)。

所有以上情况中, APB1 接口被禁止时(复位、无时钟或断电) RTC 核仍保持运行状态因此,若在读取 RTC 寄存器时, RTC 的 APB1 接口曾经处于禁止状态,则软件首先必须等待 RTC_CRL 寄存器中的 RSF 位(寄存器同步标志)被硬件置 1

注: RTC 的 APB1 接口不受 WFI 和 WFE 等低功耗模式的影响

设置后备寄存器为可访问后,在第一次通过 APB1 接口访问 RTC 时,因为时钟频率的差异,所以必须等待 APB1 与 RTC 外设同步,确保被读取出来的 RTC 寄存器值是正确的。若在同步之后,一直没有关闭 APB1 的 RTC 外设接口,就不需要再次同步了

配置 RTC 寄存器

必 须 设 置 RTC_CRL 寄 存 器 中 的 CNF 位 , 使 RTC 进 入 配 置 模 式 后 , 才 能 写 入 RTC_PRL、RTC_CNT、 RTC_ALR 寄存器。

另外,对 RTC 任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询 RTC_CR 寄存器中的 RTOFF 状态位,判断 RTC 寄存器是否处于更新中。仅当 RTOFF 状态位是 1 时,才可以写入 RTC 寄存器

配置过程:

  1. 查询RTOFF位,直到RTOFF的值变为 1

  2. 置CNF值为1,进入配置模式

  3. 对一个或多个RTC寄存器进行写操作

  4. 清除CNF标志位,退出配置模式

  5. 查询RTOFF,直至RTOFF位变为’1’以确认写操作已经完成

仅当 CNF 标志位被清除时,写操作才能进行,这个过程至少需要 3 个 RTCCLK 周期。

RTC 标志的设置

在每一个 RTC 核心的时钟周期中,更改 RTC 计数器之前设置RTC秒标志( SECF )

在计数器到达 0x0000 之前的最后一个 RTC 时钟周期中,设置 RTC 溢出标志( OWF )

在计数器的值到达闹钟寄存器的值加 1 ( RTC_ALR+1 )之前的 RTC 时钟周期中,设置 RTC_Alarm 和 RTC 闹钟标志( ALRF )。对 RTC 闹钟的写操作必须使用下述过程之一与RTC秒标志同步:

  1. 使用 RTC 闹钟中断,并在中断处理程序中修改 RTC 闹钟和/或 RTC 计数器

  2. 等待 RTC 控制寄存器中的 SECF 位被设置,再更改 RTC 闹钟和/或 RTC 计数器

示例:

RTC 中断

这里时钟自带一个秒中断,每当计数加一的时候就会触发一次秒中断。

注意:这里所说的秒中断并非一定是一秒的时间,它是由 RTC 时钟源和分频值决定的“秒”的时间,当然也是可以做到1秒钟中断一次。我们通过往秒中断里写更新时间的函数来达到时间同步的效果

闹钟中断就是设置一个预设定的值,计数每自加多少次触发一次闹钟中断

UNIX 时间戳

在使用 RTC 外设前, 还需要知道什么是 UINX 时间戳

如果从现在起,把计数器 RTC_CNT 的计数值置 0,然后每秒加 1, RTC_CNT 什么时候会溢出呢?

由于 RTC_CNT 是 32 位寄存器,可存储的最大值为(232-1),即这样计时的话,在 232秒后溢出,即它将在今后的 136 年时溢出:

\[N = 2^{23}/365/24/60/60 ≈ 136 年 \]

目前,定时器被置 0 的这个时间被称为计时元年,相对计时元年经过的秒数称为时间戳,也就是计数器中的值

大多数操作系统都是利用时间戳和计时元年来计算当前时间的,而这个时间戳和计时元年大家都取了同一个标准—— UNIX 时间戳和 UNIX 计时元年

UNIX 计时元年被设置为格林威治时间 1970 年 1 月 1 日 0 时 0 分 0 秒,可能是为了纪念 UNIX 的诞生

在这个计时系统中,使用的是有符号的 32 位整型变量来保存 UNIX 时间戳的,即实际可用计数位数比我们上面例子中的少了一位,少了这一位, UNIX 计时元年也相对提前了,这个计时方法在 2038年 1月 19日 03时 14分 07秒将会发生溢出,这个时间离我们并不远。由于 UNIX 时间戳被广泛应用到各种系统中,溢出可能会导致系统发生严重错误,届时,很可能会重演一次“千年虫”的问题, 所以在设计预期寿命较长的设备需要注意

参考文档

  1. 《 STM32 中文参考手册 V10》

  2. 《 零死角玩转 STM32 —— F103 指南者》

  3. 《 STM32F103X8 中文数据手册 》

posted @ 2022-11-02 21:38  ppqppl  阅读(873)  评论(0编辑  收藏  举报