一、RTC实时时钟
在使用RTC实时时钟时,我们需要理解一下Unix时间戳相关的内容。Unix定义为从UTC/GMT的1970年1月1日的0时0分0秒开始所经过的秒数,不考虑润秒。
时间戳存储在一个秒计数器中,秒计数器为32位/64位的整型变量,世界上所有时区的秒计数器相同,不同时区通过添加偏移来得到当地的时间。
GMT(Greenwich Mean Time)格林尼治标准时间是一种以地球自转为基础的时间计量系统。它将地球自转一周的时间间隔等分为24小时,以此确定计时标准。
UTC(Universal Time Coordinated)协调世界时是一种以原子钟为基础的时间计量系统。它规定铯133原子基态的两个超精细能级间在零磁场下跃迁辐射9,192,631,770周所持续的时间为1秒。当原子钟计时一天的时间与地球自转一周的时间相差超过0.9秒时,UTC会执行闰秒来保证其计时与地球自转的协调一致。
这里我们用代码和框图看看time.h库的相关内容。
#include<time.h>
#include<stdio.h>// 如果你转到定义你就可以看到 , typedef __time64_t time_t;
time_t time_now;// 如果你转到定义你就可以看到 tm 这个结构体的详细形式
// struct tm {
// int tm_sec;
// int tm_min;
// int tm_hour;
// int tm_mday;
// int tm_mon;
// int tm_year;
// int tm_wday;
// int tm_yday;
// int tm_isdst;
// };
struct tm time_struct;char string1[50];int main()
{time_now = time(&time_now); //int64printf("time_now = %lld \n" , time_now);// printf("time = %s" , ctime(&time_now));time_struct = *gmtime(&time_now); //转化为格林尼治时间,比中国的的时间早了8小时,你可以全部打印出来看看printf("time_struct.tm_hour = %d \n" , time_struct.tm_hour);time_struct = *localtime(&time_now); //转化为当地时间printf("time_struct.tm_hour = %d \n" , time_struct.tm_hour);printf("mktime = %lld \n" , mktime(&time_struct)); //用当地时间转化为1900 1 1 0 0 0 到现在的秒数printf("ctime = %s \n" , ctime(&time_now)); //秒计数器转化为字符串printf("asctime = %s \n" , asctime(&time_struct)); // 日期时间转化为字符串strftime(string1 , 50 , "%Y - %m - %d \n" , &time_struct); //将日期时间转化为自定义格式,下面有自定义格式printf(string1);return 0;
}// %Y:四位年份(例如:2023)
// %m:月份(01 到 12)
// %d:日期(01 到 31)
// %H:小时(00 到 23)
// %M:分钟(00 到 59)
// %S:秒(00 到 59)
// %A:星期几的完整名称(例如:Monday)
// %a:星期几的缩写(例如:Mon)
// %B:月份的完整名称(例如:January)
// %b:月份的缩写(例如:Jan)
二、BKP——Backup Registers备份寄存器
BKP可用于存储数据,但数据的容量不大,STM32F103C8T6仅有20字节(中容量和小容量) / 84字节(大容量和互联型)。BKP在VCC被切断的时候仍然可以有VBAT(1.8~3.6V)维持供电。即使系统复位或电源复位时他们也不会复位。
TAMPER引脚产生侵入事件将所有备份寄存器内容清楚。
同时他还肩具两个RTC的功能①输出RTC校准时钟 ②RTC闹钟脉冲或者秒脉冲。
下图是BKP的基本框图,图中橙色部分是后背区域,掉电后可由VBAT后备电池供电。
三、RTC——Real Time Clock实时时钟
a、RTC简介
RTC是一个独立的定时器,可以为系统提供时钟和日历功能,RTC和时钟配置系统位于后背区域,系统复位时数据不清零,即使主电源掉电了也可以使用VBAT(1.8~3.6V)引脚供电。
由第一节中 time_t 的数据类型来看,这里存储的是一个由1970年1月1日0时0分0秒到现在时间的一个秒数,所以应该是一个很大的数。RTC有一个32位的可编程计数器,可以对应Unix时间戳的秒计数器。
同时RTC还有着20位的可编程预分频器,可适配不同评率的输入时钟。
RTC时钟源的选择有三个:①HSE时钟 / 128 (通常为8MHZ / 128)
②LSE振荡器时钟(通常为32.768KHZ) ③LSI振荡器时钟(40KHZ)
b、RTC的硬件电路图和程序框图
RTC的硬件电路图先观察灰色部分的最左边有着RTCCLK选择RTC时钟,经过下面这个20位的可编程预分频器(RTC_DIV是自减计数器)后进入32位的可编程计数器,①RTC_Second是RTC每增加一秒的线路他们可以去到NVIC发起中断。②RTC_Overflow是溢出标志位,即32位数溢出,但这应该是最早2038年的事情,但STM32是无符号数所以应该是2106年。③RTC_Alarm是闹钟功能,但这个Alarm是一个定值只能响一次,如果要周期性响就需要再每次Alarm信号产生时改变RTC_ALR的值。 注意RTC_CR中F结尾的位是标志位,IE结尾的是使能。
最上面RTC是APB1总线上的设备。
最下面还有一个WKUP引脚用于唤醒设备。
c、RTC操作注意事项
在访问BKP和RTC之前需要以下两步:
①设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟。
②设置PWR_CR的DBP,使能对BKP和RTC的访问。
若在读取RTC寄存器时,RTC的APB1接口曾经处于禁止状态,则软件首先必须等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置1。(这是因为RTC的时钟是RTCCLK低速时钟,而APB1总线是PCLK1时钟。当我们用PCLK1驱动的设备读取RTCCLK驱动的设备时会有时钟不同步的问题,RTCCLK只有在上升沿才会更新数据。若我第一个时钟还没来,RTC寄存器的数据还没更新到APB1总线上,则读取会失败。)
必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、RTC_ALR寄存器。
对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是1时,才可以写入RTC寄存器。(这个也是因为两个时钟频率不对等的原因)
参考
[12-1] Unix时间戳_哔哩哔哩_bilibili
[12-2] BKP备份寄存器&RTC实时时钟_哔哩哔哩_bilibili