Bootstrap

RTC实时时钟&BKP备份寄存器

Unix时间戳
  • Unix 时间戳(Unix Timestamp)定义为从UTC/GMT的1970年1月1日0时0分0秒(伦敦时间)开始所经过的秒数,不考虑闰秒

  • 时间戳存储在一个秒计数器中,秒计数器为32位/64位的整型变量(早期的unix系统中,秒数用32位有符号的整形变量来存储 2^32 - 1 / 2 )

  • 世界上所有时区的秒计数器相同,不同时区通过添加偏移来得到当地时间

时间戳是一个计数器数值,该数值表示一个1970年1月1日0时0分0秒开始所经过的秒数

使用很大的秒计数器好处

  • 简化硬件电路。在设计RTC硬件电路时,直接设计一个很大的秒计数器

  • 在进行一些时间间隔计算时,非常方便

  • 存储方便

使用很大的秒计数器坏处

  • 占用软件资源,每次进行秒计数器和日期时间的转换时,软件都要经过复杂的运算。


GMT/UTC

GMT

GMT(Greenwich Mean Time)格林尼治标准时间是一种以地球自转为基础的时间计量系统。它将地球自转一周的时间间隔等分为24小时,以此确定计时标准

GMT,以前全球计时的时间标准,(标准,不同时区再加上对应的小时偏移)

问题:地球自转一周时间不固定

UTC

UTC(Universal Time Coordinated)协调世界时是一种以原子钟为基础的时间计量系统。它规定铯133原子基态的两个超精细能级间在零磁场下跃迁辐射9,192,631,770周所持续的时间为1秒。当原子钟计时一天的时间与地球自转一周的时间相差超过0.9秒时,UTC会执行闰秒来保证其计时与地球自转的协调一致

UTC:现行的时间标准

闰秒机制(消除计时一天和地球自转一周的误差):当原子钟计时一天的时间与地球自转一周的时间相差超过0.9秒时,UTC会执行闰秒来保证其计时与地球自转的协调一致

所谓闰秒,就是计时标准是恒定不变的,但是地球越转越慢,误差超过0.9秒时,计时系统就多走一秒,来等一下地球自转

比如上一次闰秒时刻是北京时间2017-1-1-7-59-59 下一秒就是2017-1-1-7-59-60 这样就会出现 一分钟 61秒的情况


时间戳转换

秒计时器时间和日期时间转换

C语言的time.h模块提供了时间获取和时间戳转换的相关函数,可以方便地进行秒计数器、日期时间和字符串之间的转换


BKP简介
  • BKP(Backup Registers)备份寄存器

  • BKP可用于存储用户应用程序数据(一些存储器,可以存储自定义的数据)。特性:当VDD(系统主电源)(2.0~3.6V)电源被切断,他们仍然由VBAT(备用电池电源)(1.8~3.6V)维持供电当系统在待机模式下被唤醒,或系统复位或电源复位时,他们也不会被复位

如果要使用STM32内部的BKP和RTC,VBAT这个引脚就必须要接备用电池,用来维持BKP和RTC,在VDD主电源掉电后的供电

f103e8t6的备用电池只有一根正极的供电引脚,接电池时,电池正极接到VBAT,负极和主电源的负极接在一起,共地

VBAT的作用:当VDD断电时,BKP会切换到BKP供电,这样可以持续维持BKP里面的数据,如果VDD断电且VBAT没电,BKP里面的数据会清零,因为BKP本质上是RAM存储器,没有掉电不丢失的能力

  • TAMPER引脚(接到STM32的一个外部引脚)产生的侵入事件将所有备份寄存器内容清除

TAMPER引脚是一个安全保障设计,设备需要防拆功能且BKP中存储敏感数据,可以使用TAMPER引脚的侵入检测功能,设计电路时TAMPER引脚可以加一个默认的上拉或者下拉电阻,然后引一根线到设备外壳的防拆开关或者触点,拆开设备,就会在TAMPER引脚产生上升沿或者下降沿,这样STM32检测到侵入世界,BKP里的数据清零,并且申请中断,在中断中可以继续保护数据。

主电源VDD断电后,侵入检测仍然有效

  • RTC引脚输出RTC校准时钟、RTC闹钟脉冲或者秒脉冲(RTC时钟输出功能)

  • 存储RTC时钟校准寄存器

  • 用户数据存储容量:20字节(中容量和小容量设备)/ 84字节(大容量和互联型设备)


BKP基本结构

橙色部分:称为后备区域

STM32后备区域的特性:当VDD主电源掉电时,后备区域仍然可以由VBAT的备用电池供电,当VDD主电源上电时,后备区域供电会由VBAT切换到VDD,即主电源有电时VBAT不会被用到,这样节省电量

BKP处于后备区域,但后备区域不只有BKP,还有RTC的相关电路

BKP中主要有数据寄存器,控制寄存器,状态寄存器,RTC时钟校准寄存器等

每个数据寄存器都是16位的,即一个数据寄存器可以存储两个字节

对于小和中容量设备:数据寄存器有DR1----DR10 20个字节

对于大和互联型设备:数据寄存器有DR1----DR42 84个字节

如图PC13-TAMPER—RTC 是共用一个引脚

BKP的几个功能

  • 侵入检测:可以从PC13位置的TAMPER引脚引入一个检测信号,当TAMPER产生上升沿或者下降沿时,清除BKP中所有的内容,以保证安全

  • 时钟输出:可以把RTC相关时钟从PC13的位置输出,供外部使用

输出校准时钟时,再配合RTC时钟校准寄存器,可以对RTC的误差进行校准


RTC简介
  • RTC(Real Time Clock)实时时钟(一般指提供年月日时分秒,这种日期时间的计时装置)

DS1302芯片是外置的RTC芯片,可以独立计时,需要设置时间或读取时间

  • RTC是一个独立的定时器,可为系统提供时钟和日历的功能(STM32有这个外设,可以在内部直接实现RTC的功能)

  • RTC和时钟配置系统处于后备区域,系统复位时数据不清零,VDD(2.0~3.6V)断电后可借助VBAT(1.8~3.6V)供电继续走时

  • 32位的可编程计数器,可对应Unix时间戳的秒计数器(得到秒数,再通过使用time.h中的函数得到年月日)

  • 20位的可编程预分频器,可适配不同频率的输入时钟(秒计数器 显然需要1s自增一次,因此驱动计数器的时钟 需要是一个 1Hz的信号 ,但是实际提供给RTC的时钟,RTCCLK一般频率都比较高,添加分频器,给RTCCLK将频,保证输出给计时器的频率是1Hz,为了适配各种频率的RTCCLK,加了20位的分频器,可以选择对输入时钟进行1~~~~2^20,这么大范围的分频)

  • 可选择三种RTC时钟源:接入RTCCLK

HSE时钟除以128(通常为8MHz/128)LSE振荡器时钟(通常为32.768KHz)LSI振荡器时钟(固定40KHz)

对于这三个时钟源的理解可以看RCC时钟树 整个芯片可以有4个时钟源

开头是高速 L开头是低速 E结尾是外部 I结尾是内部

  • 高速时钟一般供内部程序运行和主要外设使用

  • 低速时钟一般供RTC,看门狗这些使用

RTCCLK有三个来源

OSC引脚接的HSE(主要作为系统时钟),外部高速晶振(这个晶振是主晶振,一般都是用8MHz) 8Mhz 通过128分频 可产生RTCCLK信号 (==先128分频原因:8Mhz的主晶振太快,如果不提前分频,直接给RTCCLK,后续即使再通过RTC的20位分频器,也分不到1Hz频率==)
LSE,外部低速晶振,OSC_32这两个引脚接上,这个晶振产生的时钟,可以直接提供给RTCCLK,**OSC32的晶振是内部RTC的专用时钟** ==该晶振的值 不能够随便选

通常跟RTC有关的晶振都是统一的数值,都是32.768KHz

选择32.768KHz的原因

  • 晶振工艺比较合适的频率

  • 32768 是2的次方数 2的15次方 (经过一个15位的分频器的自然溢出,就可以很方便的得到1Hz的频率)

自然溢出:设计一个15位的计数器,这个计数器不用设置计数目标,直接从0------32767,计满后自然溢出,这个溢出信号就是1Hz。自然溢出的好处·就是不用额外设计一个计数目标,也不用比较计数器是不是计到目标值了,可以简化电路设计

来自LSI 内部低速RC振荡器(主要作为看门狗时钟),LSI固定40Khz,后续经过40K的分频 就可以得到1k的计数时钟。
内部的RC振荡器没有外部精度高,内部RC振荡器 可当做一个备选方案
​
LSI可以提供给看门狗

最常用的还是LSE(RTC专用),只有这个时钟可以VBAT 备用电池供电,上下两路时钟在VDD主电源断电后,是停止运行的

要想实现RTC断电还能继续走时的功能 必须得选择中间这一路的RTC专用时钟


RTC框图
  • 左边这一块是核心的、分频和计数计时部分,右边是中断输出使能和NVIC部分,上面这部分是APB1总线读写的部分,下面这部分是和PWR关联的部分(RTC的闹钟可以唤醒设备,退出待机·模式)

  • 灰色填充的部分 都处于后备区域,这些电路在主电源断电后,可以使用备用电池维持工作

  • 读写RTC中的寄存器需要通过APB1总线(RTC是APB1总线上的设备)。

分频部分:

这一块的输入时钟:RTCCLK(RTCCLK的来源需要再RCC中进行配置)

RTCCLK进来首先需要经过RTC预分频器进行分频,

该分频器由两个寄存器组成(类似于时基单元中的计数器CNT和重装值ARR)

  • 重装载寄存器RTC_PRL 重装值写入几 就是 几+1 分频

  • 余数寄存器RTC_DIV(每来一个时钟计一个数,是个自减计数器)

RTC输入时钟是 32.758Khz 即326768Hz 为了分频以后得到1Hz
PRL == 326767 这个数值不变 DIV给数值为0
第一个输入时钟来到时,DIV立刻溢出 产生溢出信号给后续电路,同时DIV变为重装值 32767
第二个时钟 DIV自减 变为 32766
每来32768个输入时钟,计数器溢出一次 产生一个输出脉冲

计数计时部分

32位可编程计数器RTC_CNT核心部分;

将这个计数器看做是Unix时间戳的秒计数器

RTC设计一个闹钟寄存器RTC_ALR(也是一个32位的寄存器 和 RTC_CNT是等宽的,)

作用:设置闹钟;在ALR写一个秒数,设定闹钟,当CNT的值和ARR设定的闹钟值一样时,这时会产生RTC_Alarm闹钟信号,通往右边的中断系统。在中断函数中可以执行响应操作。(闹钟值 是一个定值 只能响一次,如果想要实现周期性闹钟,在闹钟每次响以后,都要重新设置一下闹钟时间

同时这个闹钟还兼具一个功能,闹钟信号可以让STM32退出待机模式

这个功能对应的用途
设计了一个数据采集设备,在环境恶劣的地方工作(海底,高原等),要求每天中午12点采集一次环境数据,其他时间为了节省电量,芯片必须处于待机模式
可以使用RTC自带的闹钟功能,定一个·12点的闹钟,闹钟一响,芯片唤醒,采集数据,完成以后继续待机

中断部分

如图,有三个信号可以触发中断,

  • RTC_Second 秒中断 (来源CNT的输入时钟,如果开启这个中断,程序每秒会进一次RTC中断)

  • RTC_Overflow 溢出中断 (来源CNT,当CNT的32位计数器计满溢出时,会触发一次中断,一般不会触发 STM32 这个CNT定义的是无符号数 )

  • RTC_Alarm 闹钟中断(当计数器值和闹钟值相等时 触发中断,同时闹钟信号可以把设备从待机模式唤醒)

F结尾的是中断标志位,IE结尾的是中断使能

下面部分

闹钟信号RTC_Alarm 和WKUP引脚 都可以唤醒设备

WKUP引脚在PA0的位置兼具唤醒功能


RTC基本结构

从左往右

RTCCLK时钟来源 需要在RCC中进行配置

RTCCKL先通过预分频器,对时钟进行分频,余数寄存器是一个自减寄存器,存储当前的计数值,重装寄存器是计数目标,决定分频值

通向32位计数器

需要中断的话 先使能中断 再通过NVIC


硬件电路

为了配合STM32的RTC,外部还需要一些电路

在最小系统电路上,外部电路还需要额外加两部分,

  • 备用电池

  • 外部低速晶振

备用电池供电电路

(1)简单连接

使用一个3V的电池,负极和系统共地,正极直接引到STM32的VBAT引脚上

(2)推荐连接

电池通过二极管D1,向VBAT供电;主电源的3.3V 也通过二极管D2 向VBAT进行供电


RTC操作注意事项
  • 执行以下操作将使能对BKP和RTC的访问:RTC和BKP开启比较复杂,必须要执行以下两步 (对于正常外设,第一步开启时钟就能用了)

(1)设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟 (开启APB1外设时钟,同时开启PWR和BKP时钟)

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);

(2)设置PWR_CR的DBP,使能对BKP和RTC的访问

    PWR_BackupAccessCmd(ENABLE);
  • 若在读取RTC寄存器时,RTC的APB1接口曾经处于禁止状态,则软件首先必须等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置1

RTC_WaitForSynchro();(RTC等待同步)

原因:

PCLK1和RTCCLK两个时钟,
​
PCLK1在主电源掉电时会停止,为了保证RTC主电源掉电正常工作,RTC里的寄存器都是在RTCCLK的同步下变更的,
​
当用PCLK1驱动的总线,去读取RTCCLK驱动的寄存器时,会有一个时钟不同步的问题
​
RTC寄存器只有在RTCCLK的上升沿更新,但是PCLK1的频率36MHz,远大于RTCCLK的频率32KHz,如果在APB1刚开始,就立刻读取RTC寄存器,有可能RTC寄存器还没有更新到APB1总线上

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

RTC_EnterConfigMode();

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

RTC_WaitForLastTask();


BKP备份寄存器库函数
用途:手动清空BKP所有的数据寄存器 如果有备用电池,BKP的数据VDD掉电不清零,上电复位也不清零,
void BKP_DeInit(void);
​
配置Tamper侵入检测功能
void BKP_TamperPinLevelConfig(uint16_t BKP_TamperPinLevel);配置Tamper引脚的有效电平,即高电平触发还是低电平触发
void BKP_TamperPinCmd(FunctionalState NewState);是否开启侵入检测功能
中断配置
void BKP_ITConfig(FunctionalState NewState);
时钟输出功能配置
可以选择在RTC引脚上输出时钟信号,输出RTC校准时钟,RTC闹钟脉冲或者秒脉冲
void BKP_RTCOutputConfig(uint16_t BKP_RTCOutputSource);
设置RTC校准值 即 写入RTC校准寄存器
void BKP_SetRTCCalibrationValue(uint8_t CalibrationValue);
​
写备份寄存器
void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);
读备份寄存器
uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);
​
​
FlagStatus BKP_GetFlagStatus(void);
void BKP_ClearFlag(void);
ITStatus BKP_GetITStatus(void);
void BKP_ClearITPendingBit(void);
​
备份寄存器访问使能-----设置PWR_CR寄存器里的DBP位
void PWR_BackupAccessCmd(FunctionalState NewState);

RTC实时时钟库函数
配置中断输出
void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState);
进入配置模式 置CRL的CNF为1  使RTC进入配置模式以后 才能够使用 RTC_PRL,RTC_CNT,RTC_ALR寄存器
void RTC_EnterConfigMode(void);
退出配置模式 即 将CNF为置0
void RTC_ExitConfigMode(void);
获得CNT计数器的值
uint32_t  RTC_GetCounter(void);
设置CNT计数器的值
void RTC_SetCounter(uint32_t CounterValue);
写入预分频器 会写入预分配的PRL重装寄存器中 配置预分配器的分频系数
void RTC_SetPrescaler(uint32_t PrescalerValue);、
写入闹钟值
void RTC_SetAlarm(uint32_t AlarmValue);
读取预分配器中的DIV余数寄存器
uint32_t  RTC_GetDivider(void);
​
等待上次操作完成  循环 直到RTOFF状态位 为 1
    //对于RTC任何寄存器的写操作,都必须在前一次写操作结束之后进行,可以通过查询RTC_CR寄存器中的RTOFF状态位 判断RTC寄存器是否处在更新中,只有RTOFF状态位置1,才可以写入RTC寄存器
void RTC_WaitForLastTask(void);
等待同步 清除RSF标志位 循环等待 直到RSF为1
    //若在读取RTC寄存器时,RTC的APB1接口曾处在禁止状态,则软件首先必须等待RTC_CRL寄存器中的RTF(寄存器同步标志)被硬件置1
void RTC_WaitForSynchro(void);
​
​
FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG);
void RTC_ClearFlag(uint16_t RTC_FLAG);
ITStatus RTC_GetITStatus(uint16_t RTC_IT);
void RTC_ClearITPendingBit(uint16_t RTC_IT);
RCC部分库函数
配置LSE外部低速时钟
void RCC_LSEConfig(uint8_t RCC_LSE);
配置LSI内部低速时钟
void RCC_LSICmd(FunctionalState NewState);
​
RTCCLK配置  选择RTCCLK的时钟源 
void RCC_RTCCLKConfig(uint32_t RCC_RTCCLKSource);
​
启动RTCCLK
void RCC_RTCCLKCmd(FunctionalState NewState);
​
还需要获取标志位 因为LSE时钟 不能够立即启动 需要调用标志位 等RCC标志位 LSERDY置1 之后 这个时钟才算启动完成 工作稳定

;