一、前言,为什么要对PCF8563时钟模块进行校时操作
根据PCF8563实时时钟模块的说明资料:由于模块电路外围器件的性能参数的偏差,该模块的计时误差平均为5分钟/年。在实际使用中,经测试发现,该模块的计时误差约为每天慢3.6秒,全年累计要慢21.9分钟,相比说明书介绍,偏差要大得多。
由于项目对于计时的精确度要求较高,加之,难以对电路中的器件进行更换或调整,所以,考虑用程序进行软件自动修正校时。
电路应用如下:
二、校正时间的思路
首先要找出PCF8563走时误差到底是多少,比如每天误差多少。做法如下:
1)为调试的电脑设置一个精确的时间。
首先,为电脑设置一个精确的时间。事先,要保证电脑连接internet。打开电脑“设置”的“日期和时间”的“自动设置时间”选项,如下图绿圈处。
2)为调试的ESP32模块RTC设置一个精确的时间
设置Thonny“选项”的“解释器”的"同步设备的实时时钟"为真,如下图划绿线部分。启用这个功能,在用Thonny调试程序,Thonny会用电脑的时间来设置ESP32模块上的RTC时间。
3)为PCF8563模块设定时间
编写一个功能函数,用ESP32上RTC来设定PCF8563模块的时间。找张纸,记下此刻PCF8563模块的日期和时间。由于PCF8563模块上有备用电池,即使外供电掉电,PCF8563模块还能够正常运行、走时。
设定或读取PCF8563时间,此过程可参考本人另一篇https://blog.csdn.net/weixin_42419608/article/details/134900922?spm=1001.2014.3001.5502
4)计算PCF8563模块的误差时间,确定走时误差修正值
经过几天,编写一个功能函数读取PCF8563模块的时间,对比电脑上的时间,可以看到两个时间已经出现偏差。再对比此前纸上记下的日期时间,可以计算出经过几天后,PCF8563模块每天快了(或慢了)多少秒的时间误差,从而可以计算出每天的修正值,或是其他比例修正值。
此时,编写一个记录校时的文件,保存进ESP32里,如:cal8563.txt,保存校时信息的文件,格式例如#B13.6#B21766410308#B32024,4,11,13,41,42,6,105
其中#B13.6#B2表示修正值为+3.6秒,#B21766410308#B3表示校时时的流逝累计秒,#B32024,4,11,13,41,42,6,105表示校时时的日期时间(另一种格式表达而已,方面阅读、理解),备用
5)以后,在程序中循环调用功能函数,每隔一段时间,条件达到,按误差修正值,对PCF8563进行校时修正。
三、实现过程(几个关键点)
1)读取或设置ESP32模块RTC时间
用下面语句就能读取ESP32模块RTC时间
import time
a= time.localtime()
print("RTC time: ",a)
#localtime格式(year, month, mday, hour, minute, second, weekday, yearday) ,运行后,结果如下:
RTC time: (2024, 6, 10, 17, 38, 30, 0, 162)
重点说明:time.localtime([secs]) 这是本文中校时的一个重要功能函数,方便进行时间格式变换
如果未提供secs或 None,则使用来自 RTC 的当前时间。
如果提供secs秒(以秒表示的时间),则转换为2000年1月1日以来的一个 8 元组(year, month, mday, hour, minute, second, weekday, yearday),包含:
• year年份包括世纪(例如 2014)。
• month月份是 1-12
• mday 是 1-31
• hour小时是 0-23
• minute分钟是 0-59
• second秒是 0-59
• weekday周一至周日的工作日是 0-6
• yearday是一年中第几天是 1-366
另一个重要功能函数 time.time(), 此函数的返回值方便了校时修正的计算过程
函数说明:获取当前cpu时间戳,2000年1月1日以来的流逝时间,单位:秒。
示例:
>>> time.time()
771357344
>>>
用这两个功能函数相互配合就可以很方便地进行校时操作,否则,仅仅是闰年闰月的判断,就会把人搞疯。
Thonny调试运行结果见下:
2)校时功能函数工作流程
3)代码*
#函数功能:对PCF8563校时--------------------------
#参数1:Cals修正值(秒),参数2:每隔CalPerTimes秒修正一次,如1天=86400秒
#返回:当前年份
#状态:可用,调试于2024-4-15
def Calib_PCF8563(Cals,CalPerTimes):
#cal8563.txt保存信息的文件,格式例如#B13.6#B21766410308#B32024,4,11,13,41,42,6,105
# #B13.6#B2修正值3.6秒,#B21766410308#B3流逝的累计秒,#B32024,4,11,13,41,42,6,105修正时的日期时间
WrRd_PCF8563(False) #自编函数,取PCF8563时间来设置RTC
CurrSeconds = int(time.time()) #获得当前时间(流逝秒)
#print("CurrTime: ",CurrSeconds)
#以下提取之前校时的时间记录
f = open ('cal8563.txt', 'r',encoding= 'UTF-8')
file_c = f.read()
f.close()
i = file_c.find('#B2') #提取特定标志信息
if i != -1:
j=file_c.find('#B3')
PastTimes = int(file_c[i+3:j:]) #获得上次修正时的流逝秒
#print("PastTimes:",PastTimes)
#如果当前时间比上次修正时间超过一次需要校正的时间CalPerTimes秒,如CalPerTimes=86400秒=1天,则进行校时修正操作
if CurrSeconds > (PastTimes+CalPerTimes):
#当前流逝秒 +((当前-过去)的流逝秒数除以每CalPerTimes秒后取整,乘以修正值)
#计算出经过了CalPerTimes后的修正值,即修正的流逝秒
CalsTime = CurrSeconds + int((CurrSeconds-PastTimes)//CalPerTimes * Cals)
#print ("CalSeconds: ",CalsTime)
#按修正的流逝秒来设置RTC
c = time.localtime(CalsTime) #用带参数的time.localtime()来变换时间
rtc = RTC() #准备来设置RTC时间值
rtc.datetime((c[0],c[1],c[2],c[6],c[3],c[4],c[5],0)) #用变换出来的数据设置RTC
#print("AfterCal:",time.time())
WrRd_PCF8563(True) #自编函数,取RTC时间来设置PCF8563,也就是对PCF8563校时
#把校正时间时的时间信息形成格式化信息保存下来,以备下一次使用
toFile = '#B1'+ str(Cals) + '#B2'+str(time.time())+'#B3'+str(time.localtime())
#print("tofile: ",toFile)
with open('cal8563.txt', "wt",encoding= 'UTF-8') as f:
f.write(toFile)
f.close
nowT=time.localtime()
return nowT[0] #返回年份值
4)实现软件校时
程序中循环调用以上功能函数,间隔时间条件满足时,就会进行校时操作,从而实现软件校时。
其他:关于设置和读取PCF8653时间,可参考本人另一篇https://blog.csdn.net/weixin_42419608/article/details/134900922?spm=1001.2014.3001.5502