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 之后 这个时钟才算启动完成 工作稳定