常用的计时时钟芯片有 DS1302、DS1307、DS3231,各型号还有衍生型号。都是 Dallas 生产的芯片。其中 DS1302 和 DS1307 可以看成是同一芯片的不同协议版本(I2C 和 SPI)。他们在树莓派 Pico 上的用法几乎一致,下面将简单介绍 DS1302 的用法。
我们使用的模块长这样。

硬件连接
时钟模块的 VCC 引脚接 Pico 的 VSYS。
时钟模块的 GND 引脚接 Pico 的 GND。
时钟模块的 CLK 引脚接 Pico 的 GP21。
时钟模块的 DAT 引脚接 Pico 的 GP20。
时钟模块的 RST 引脚接 Pico 的 GP19。
编程
使用开源的 MicroPython DS1302 库,可以在这里下载源码。也可以将下面的代码保存在 Pico 上,命名为 DS1302.py。
from machine import Pin
W_SECOND = const(0x80)
W_MINUTE = const(0x82)
W_HOUR = const(0x84)
W_DATE = const(0x86)
W_MONTH = const(0x88)
W_WEEK = const(0x8A)
W_YEAR = const(0x8C)
W_WP = const(0x8E)
R_SECOND = const(0x81)
R_MINUTE = const(0x83)
R_HOUR = const(0x85)
R_DATE = const(0x87)
R_MONTH = const(0x89)
R_WEEK = const(0x8B)
R_YEAR = const(0x8D)
class DS1302():
def __init__(self,SCLK,DAT,RST):
self.resetPin = DAT
self.SCLK = Pin(SCLK,Pin.OUT)
self.DAT = Pin(DAT,Pin.OUT)
self.RST = Pin(RST,Pin.OUT)
self.SCLK.off()
self.RST.off()
def SetTime(self,YEAR,MONTH,DAY,HOUR,MINUTE,SECOND,WEEK):
self.WriteByte(W_WP,0x00);
YEAR = int(YEAR/10)*16+YEAR%10
self.WriteByte(W_YEAR,YEAR);
MONTH = int(MONTH/10)*16+MONTH%10
self.WriteByte(W_MONTH,MONTH);
DAY = int(DAY/10)*16+DAY%10
self.WriteByte(W_DATE,DAY);
self.WriteByte(W_HOUR,HOUR);
MINUTE = int(MINUTE/10)*16+MINUTE%10
self.WriteByte(W_MINUTE,MINUTE);
SECOND = int(SECOND/10)*16+SECOND%10
self.WriteByte(W_SECOND,SECOND);
self.WriteByte(W_WEEK,WEEK);
self.WriteByte(W_WP,0x80);
def GetTime(self):
date = []
Temp = self.ReadByte(R_YEAR)
Temp = ((Temp&0xF0)>>4)*10+(Temp&0x0F)
date.append(Temp)
Temp = self.ReadByte(R_MONTH)
Temp = ((Temp&0x10)>>4)*10+(Temp&0x0F)
date.append(Temp)
Temp = self.ReadByte(R_DATE)
Temp = ((Temp&0x20)>>4)*10+(Temp&0x0F)
date.append(Temp)
Temp = self.ReadByte(R_HOUR)
Temp = (Temp&0x1F)
date.append(Temp)
Temp = self.ReadByte(R_MINUTE)
Temp = ((Temp&0x70)>>4)*10+(Temp&0x0F)
date.append(Temp)
Temp = self.ReadByte(R_SECOND)
Temp = ((Temp&0x70)>>4)*10+(Temp&0x0F)
date.append(Temp)
Temp = self.ReadByte(R_WEEK)
date.append(Temp)
return date
def Now(self):
date = self.GetTime()
Now = []
Year = "20{0}".format(date[0])
Now.append(Year)
Month = "0{0}".format(date[1]) if (date[1]/10)<1 else "{0}".format(date[1])
Now.append(Month)
Day = "0{0}".format(date[2]) if (date[2]/10)<1 else "{0}".format(date[2])
Now.append(Day)
Hour = "0{0}".format(date[3]) if (date[3]/10)<1 else "{0}".format(date[3])
Now.append(Hour)
Minute = "0{0}".format(date[4]) if (date[4]/10)<1 else "{0}".format(date[4])
Now.append(Minute)
Second = "0{0}".format(date[5]) if (date[5]/10)<1 else "{0}".format(date[5])
Now.append(Second)
Now.append(date[6])
return Now
def WriteByte(self,Command,Data):
self.RST.on()
Pin(self.resetPin,Pin.OUT)
for i in range(0,8):
if Command&(0x01<<i):
self.DAT.on()
else:
self.DAT.off()
self.SCLK.on()
self.SCLK.off()
for i in range(0,8):
if Data&(0x01<<i):
self.DAT.on()
else:
self.DAT.off()
self.SCLK.on()
self.SCLK.off()
self.RST.off()
def ReadByte(self,Command):
Data = 0x00
Pin(self.resetPin,Pin.OUT)
self.RST.on()
for i in range(0,8):
if Command&(0x01<<i):
self.DAT.on()
else:
self.DAT.off()
self.SCLK.on()
self.SCLK.off()
Pin(self.resetPin,Pin.IN)
for i in range(0,8):
bit = self.DAT.value()
Data |= (bit<<i)
self.SCLK.on()
self.SCLK.off()
self.RST.off()
return Data
将下面的代码命名为 main.py 保存在 Pico 上。首次使用,或者需要修改时钟模块上的时间时,可以将代码中的 ds1302.SetTime(23,11,22,18,00,00,3) 这行代码前的注释去除,并将参数指定为你想要的时间。SetTime 方法有 7 个参数,分别是年(两位数,23 表示 2023 年)、月、日、小时(24 小时制)、分、秒、星期(1-7)。
from machine import Pin,I2C
from DS1302 import DS1302
import time
ds1302 = DS1302(21,20,19)
# 设置时钟模块的时间 2023-11-22 18:00:00 星期三
# ds1302.SetTime(23,11,22,18,00,00,3)
while True:
date = ds1302.Now()
print("{0}-{1}-{2} {3}:{4}:{5}".format(date[0],date[1],date[2],date[3],date[4],date[5]))
time.sleep(1)
运行上面的代码之后,Shell 窗口将会每一秒钟打印一行从时钟模块获取的当前时间。


这个库有问题,会出现时间跳跃现象,会从9点跳到16点,而不是10点