单片机RTC及时钟芯片的时间到底从哪一年起始?

本文通过实测STM32F103、STM32F407、R5F100、DS1302和DS1307等多种设备的RTC功能,发现它们的初始时间通常设定为00年,且00~99年之间能被4整除的年份被视为闰年。这并非源于1970年1月1日的Unix时间戳,而是由于硬件限制和设计选择的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

实时时钟RTC在嵌入式系统中是常用的功能,有的单片机有RTC功能,也有专用的RTC芯片,如DS1302、DS1307等。但RTC的“年寄存器”长度有限,一般不会包含所有年份,都是从0开始,到某个数字结束。这个开始的年份“0”到底是哪一年呢?很多人说是1970年1月1日开始,真是这样吗?
没有调查就没有发言权,本文笔者将测试多种单片机,使用内部RTC功能,以及专用的RTC芯片,考证单片机RTC及常用时钟芯片的时间到底从哪一年起始。
要讨论RTC等等时间从哪一年开始,需要用到历法知识,我们需要在特殊的时间点检验RTC的时间是如何变化的,比如闰年的闰月2月28日23:59:59秒之后,时间变成了哪一天。

二、实验测试

1.STM32F103系列内部RTC功能

STM32F103单片机内部RTC实时时钟驱动程序中,我们讨论了STM32F103C8T6单片机内部RTC功能,该功能只有一个计数器,每1秒加1,没有年月日及时间寄存器,读取计数器的值后,需要使用软件计算出时间,才能实现日历、时钟功能,软件算法需要考虑历法中的闰年等特殊情况,因此,可得出结论:STM32F103系列内部RTC没有具体的起始时间,起始时间由软件算法确定;在前述的博文中,选择用UTC时间戳(以1970年1月1日00:00:00为开始时间),可以使程序通用性更强。

2.STM32F407系列内部RTC功能

该型号芯片是STM32具有完整RTC功能的代表,先看F407数据手册中关于RTC的描述:
在这里插入图片描述
这与F103不同,RTC具有完整的时间、日历寄存器,再看各寄存器的大小:
在这里插入图片描述
在这里插入图片描述
我们只需要关心年份寄存器的大小即可,由红框中的描述可知,年份只能表示2位十进制数,即0~99。日期寄存器上电复位值为0x0000 2101,对应00年1月1日星期1,还有时间寄存器(详见手册)复位值为0x0000 0000,对应时间为00:00:00。
接下来,我们使用STM32F407VGT6,首次上电后,不进行RTC的初始化配置,直接读取寄存器,通过串口输出,看看时间如何:
在这里插入图片描述

可看出时间确实是从00年1月1日00时00分00秒,星期一开始的,与手册一致。把时间设置为00-02-28 23:59:50,星期几忽略,看10秒后时间如何变化:
在这里插入图片描述
时间变成了2月29日,看来是把00年当作闰年处理;再把时间设置为01-02-28 23:59:50,星期几忽略,看10秒后时间如何变化:
在这里插入图片描述
时间变为3月1日,是平年。
测试发现,00~99之间可被4整除的年份都被当作闰年,其他年份为平年,不再贴图。

3.瑞萨R5F100内部RTC功能

R5F100FE是瑞萨RL78/G13系列较常用的芯片,对RTC功能的描述如下:
在这里插入图片描述
手册中直接说最长计数达到99年,再看年计数寄存器的描述:
在这里插入图片描述
这就比较明朗了,年是从00开始,最多到99,而且认为年份可整除4是闰年,与STM32F407相同。根据手册的描述,复位时,寄存器初始化为00年1月1日00:00:00,星期日(手册中说明,星期寄存器初始化为0,对应星期日):
在这里插入图片描述
同样,把时间设置为00-02-28 23:59:50,星期几忽略,看10秒后时间如何变化:
在这里插入图片描述
时间变为2月29日,即00年当做闰年处理;再把时间设置为01-02-28 午夜,时间如何变化:
在这里插入图片描述
时间变为3月1日,是平年。
测试发现,00~99之间可被4整除的年份都被当作闰年,其他年份为平年,不再贴图。

4.时钟芯片DS1302

DS1302手册中描述如下,带闰年补偿功能,可到2100年:
在这里插入图片描述
再看相关寄存器,年寄存器只能表示BCD码格式的2位数,00~99年,如下图:
在这里插入图片描述
笔者的这份手册中,没有说明各寄存器的初始值是多少,我们通过读取寄存器的值来查看,和前述方法一样,不做任何设置,直接读取寄存器,通过串口输出:
在这里插入图片描述
可知DS1302在首次上电复位时,寄存器被初始化为00年1月1日00:00:00,星期一,与STM32F407相同。接下来,把时间设置为00-02-28 23:59:50,星期几忽略,看时间如何变化:
在这里插入图片描述
时间变成了2月29日,与STM32F407、R5F100FE一样,是把00年当作闰年处理;再把时间设置为01-02-28 23:59:50,星期几忽略,看时间如何变化:
在这里插入图片描述
时间变为3月1日,是平年。
测试发现,和STM32F407、R5F100FE一样,00~99之间可被4整除的年份都被当作闰年,其他年份为平年,不再贴图。

5.时钟芯片DS1307

D31307是IIC接口的设备,除此之外,与DS1302有非常多的相似之处,都有闰年补偿功能,可计时到2100年,以下是DS1307的主要寄存器:
在这里插入图片描述可看出,寄存器除了地址不同外,其他基本功能类似;在手册中有明确的说明,首次上电,寄存器被初始化为00年1月1日00:00:00,星期一,如下:
在这里插入图片描述在这里插入图片描述
同样,把时间设置为00-02-28 23:59:50,星期几忽略,时间会切换为2月29日:
在这里插入图片描述
经测试,DS1307与DS1302、STM32F407以及R5F100FE一样,将00~99之间可被4整除的年份都被当作闰年,其他年份为平年,不再贴图。

三、总结

1.文中用到的设备,初始年份大多为00年,即四位年份yyyy格式的后两位,且当作闰年处理,如果是指1970年的话,1970年不是闰年;无论是单片机还是RTC专用芯片,没有一个手册中提到1970年1月1日这个时间,因此,说RTC功能的起始时间是1970-01-01 00:00:00的,不准确,也没有依据;认为单片机的RTC及时钟芯片时间从1970年1月1日00:00:00开始,是因为这个时间点是Unix时间戳的起始时间,在很多计算机系统和编程语言中被广泛使用;但RTC和时钟芯片的实际初始时间可能会因不同的硬件设备、制造商或系统设计而有所差异,因寄存器大小的限制,一般只计时0~99年的范围,而起始时间,实际上可由使用者自己规定;
2.对于类似STM32F103C8T6这样,RTC功能只有一个计数器,没有年月日时分秒这种时间寄存器的,需要由软件算法配合来计时,起始时间可以任意;
3.对于计时范围00~99年的设备,每4年为一个闰年,在前后100年范围内可用,但要注意的是,并不是每个整百年都是闰年,例如,2000年是闰年,但是1900年、2100年就不是闰年,因此这种情况不能单纯的将年份加1900或者2000处理,而是要经过特殊处理,可以看出,这些器件都是为本世纪设计的;如果时间来到下一世纪2100年,这些芯片没有升级换代,有程序员把00年直接当做2100年而且没经过特殊处理,是会出问题的;当然,有摩尔定律在,70多年后,这种情况是不会发生的,因为那时候这些芯片应该不存在了;
4.以上结论仅基于所述的种类有限的设备,经过测验得出,可能以偏概全了;如果有人做过更深入的研究,或者是做芯片相关功能设计的,欢迎批评指正。

### 51单片机实现万年历功能而不使用DS1302芯片 #### 设计思路 在不依赖外部实时时钟(RTC)芯片的情况下,可以通过软件算法来计算并维护时间和日期。这种方法主要依靠定时中断来模拟RTC的功能。通过设定一个初始时间点,并利用定时器定期增加这个时间变量,从而达到跟踪时间的目的。 对于51单片机而言,可以采用内部定时/计数器资源配合相应的延时子程序完成每秒钟一次的更新操作。每次进入定时中断服务程序(Timer Interrupt Service Routine, ISR),就对全局静态变量中的秒、分钟、小时等字段进行累加运算;当这些数值超出其合法范围时,则相应地进位到更高单位上去[^3]。 #### 时间管理逻辑 为了简化闰年的判断以及大小月天数差异带来的复杂度,通常会预先定义好每个月份对应的天数数组,并编写专门用于处理跨月甚至跨年的辅助函数。下面是一个简单的例子: ```c // 定义平年各个月份的最大天数 const uint8_t monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 判断是否为闰年 bool isLeapYear(uint16_t year){ return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } // 更新一天后的日期 void updateDate(struct Date *datePtr){ datePtr->day++; // 如果超过了本月最大天数则转下一个月 if(datePtr->day > getMonthMaxDay(*datePtr)){ datePtr->day = 1; datePtr->month++; // 超过十二月则转入新的一年 if(datePtr->month > 12){ datePtr->month = 1; datePtr->year++; } } } ``` #### 显示部分 考虑到硬件成本和电路板空间等因素,在某些应用场景中可以直接选用带有字符型LCD显示屏(如1602 LCD)的产品来进行数据显示。这类显示器支持ASCII编码的文字打印,因此只需要按照特定格式将当前的时间信息转换成字符串形式再发送给LCD控制器即可显示出来[^2]。 #### 初始化配置 启动阶段需要初始化所有必要的外设单元,包括但不限于串口通信端口、按键扫描矩阵等等。更重要的是要设置好系统的起始时刻——这通常是通过硬编码的方式指定一个固定的日期作为起点,或者是允许用户手动输入当前的确切时间以便同步校准。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值