MicroPython Driver for DS1302
Contents
Introduction
This a MicroPython driver for the DS1302 real time clock breakout board written specifically for the BBC micro:bit.
The DS1302 is discussed in some detail here. This reference also contains link details to the original driver file.
The driver can be:
- Copied from this webpage onto the clipboard then paste into the MicroPython editor e.g. the Mu Editor. It should be saved as fc_ds1302.py - OR -
- Download as a zip file using the link. Unzip the file and save it as fc_ds1302.py into the default directory where the MicroPython editor e.g. Mu Editor saves python code files.
After saving the fc_ds1302.py file to the computer it should be copied to the small filesystem on the micro:bit. The examples on this page will not work if this step is omitted. Any MicroPython editor that recognises the micro:bit will have an option to do this.
The Mu Editor is recommended for its simplicity. It provides a Files button on the toolbar for just this purpose.
Connecting the DS1302 module
The DS1302 communicates via a three wire SPI-like interface. The micro:bit and the DS1302 module is best connected as follows:
micro:bit | DS1302 Module |
---|---|
GND | GND |
3.3V | VCC |
Pin 13 | CLK |
Pin 14 | DAT |
Pin 15 | CS[1] |
Driver Overview
The driver is implemented as a class. The first thing to do is call the constructor. This provides an instance of the class.
Syntax:
DS1302(CLK=pin13, DAT=pin14, CS=pin15)
Notes:
The default arguments for CLK, DAT and CS
are the default micro:bit SPI pins. Any
three GPIO digital pins available on the
micro:bit can be used.
Example 1:
from fc_ds1302 import *
# Use default microbit pins
# CLK=pin13, DAT=pin14, CS=pin15
my_clock = DS1302()
Example 2:
from fc_ds1302 import *
# Use pin0, pin1, pin3
ds1302 = DS1302(CLK=pin0, DAT=pin1, CS=pin2)
The driver provides methods that:
- Set and read the time (seconds, minutes, hour)
- Set and read the date (weekday, calendar day, month, year)
- Read and write to the 31 bytes of user RAM
While the DS1302 RTC supports both 12hr clock time and 24hr clock time this driver only supports 24hr time.
Following is a full list of methods that are user callable. Each method is described and example use given in the sections below.
Sec() | Mins() | Hr() | Wday() |
Day() | Mon() | Yr() | DateTime() |
Time() | Date() | Ram() |
Set and Read the Time
The hour (in 24Hr format), minutes and seconds can be set (and read) with the following methods:
- Sec(sec = None) ; valid values 0 to 59
- Mins(mins = None) ; valid values 0 to 59
- Hr(hr = None) ; valid values 0 to 23
If the method is called with no argument the requested time component (hours, minutes or seconds) is read and returned.
from fc_ds1302 import *
my_clock = DS1302()
# Set hours
my_clock.Hr(10)
# Read hours
print('Hour is:', my_clock.Hr())
⇒ Hour is: 10
The following method sets (and reads) hours, minutes and seconds in a single call:
- Time(h=None, m=None, s=None)
This method is very flexible. Any combination of hours, minutes and seconds can be set on the DS1302.
If the method is called with an empty argument list then the time is returned as a string in the format "HH:MM:SS". This string format is understood by spreadsheet programs such as Microsoft Excel and is useful for exporting logged data for further analysis.
# Test Time() method.
from fc_ds1302 import *
my_clock = DS1302()
# Set hours, minutes and seconds
my_clock.Time(h=11, m=9, s=0)
# Read time and return formatted string
print('Time is:', my_clock.Time())
# Set minutes only
my_clock.Time(m=31)
# Read time and return formatted string
print('New time is:', my_clock.Time())
Time is: 11:09:00
New time is: 11:31:00
Set and Read the Date
The year, month, day and weekday can be set (and read) with the following methods:
- Yr(yr = None) ; valid values 0 to 99
- Mon(mon = None) ; valid values 1 to 12
- Day(day = None) ; valid values 1 to 31
- Wday(wday = None) ; valid values 1 to 7
If the method is called with no argument the requested date component (year, month, day or weekday) is read and returned.
from fc_ds1302 import *
my_clock = DS1302()
# Set month
my_clock.Mon(9)
# Read month
print('Month is:', my_clock.Mon())
⇒ Month is: 9
The following method sets (and reads) year, month and day in a single call:
- Date(y=None, m=None, d=None, fmt=1)
This method is very flexible. Any combination of year, month and day can be set on the DS1302.
If the method is called without providing year, month and day then the date is returned as a string in the format defined by the argument fmt.
- fmt = 1 ; "YYYY-MM-DD"
- fmt = 2 ; "DD-MM-YYYY"
- fmt = 3 ; "MM-DD-YYYY"
These string formats are understood by spreadsheet programs such as Microsoft Excel and is useful for exporting logged data for further analysis.
# Test Date() method.
from fc_ds1302 import *
my_clock = DS1302()
# Set year, month and day
my_clock.Date(y=24, m=9, d=23)
# Read date and return formatted string
print('Date is:', my_clock.Date(fmt=1))
# Set day only
my_clock.Date(d=20)
# Read date and return formatted string
print('New date is:', my_clock.Date(fmt=2))
Date is: 2024-09-23
New date is: 20-09-2024
In the above example only month and day are being set. So, when the full date is requested and printed in the last line of the program it will return whatever value is currently in the year register.
The Universal DateTime() Method
The DateTime() method allows all seven date and time parameters to be set (and read) in a single call.
Syntax: DateTime(dat = None) Where: dat : A list containing (in the order given): [Year, Month, Day, Weekday, Hour, Minutes, Seconds] Example: from fc_ds1302 import * my_clock = DS1302() # Set date/time 2024-09-23 15:26:00 dt = [24, 9, 23, 1, 15, 30, 0] my_clock.DateTime(dt) print(my_clock.DateTime()) ⇒ [2024, 9, 23, 1, 15, 30, 0]
Reading and Writing the User RAM
The DS1302 chip provides 31 bytes of non-volatile RAM for general purpose use. The user might take advantage of this, for example, to store startup parameters. Being non-volatile, the RAM contents are retained after power is lost to the DS1302.
The driver provides the method Ram() to read and write to the RAM. This is a very simple method with no error checking performed.
It reads or writes a single byte on each call. The 8-bit byte value is expected to be an integer in the range 0 to 255.
Syntax:
Ram(reg, dat=None)
Parameters:
reg : The RAM register to be read or written to.
It is a value in the range 1 to 31.
dat : The value that is written to the
given RAM register. It must be an
integer in the range 0 to 255.
Note:
If dat is not provided then the method will read
and return the value in the RAM register reg.
Example
from fc_ds1302 import *
my_clock = DS1302()
# Write the value 0x20 to RAM register 5
my_clock.Ram(5, 0x20)
# Read value at RAM register 5
print(hex(my_clock.Ram(5)))
⇒ 0x20
Examples
The following code can be used to set the time and date on the DS1302. Make the obvious edits to add your current date and time. Flash the program to the micro:bit.
Example 1
# Set time and date on DS1302
from fc_ds1302 import *
# Instantiate a DS1302 RTC object.
ds1302 = DS1302()
# Set the clock's date
ds1302.Date(y=24, m=9, d=23)
# Set the day of week
ds1302.Wday(1)
# Set the clock's (in 24 Hr mode) time.
ds1302.Time(h=9, m=42, s=5)
# Returns a list of all 7 time/date components.
print('Time & Date values:', ds1302.DateTime())
Output: Time & Date values: [2024, 9, 23, 1, 9, 42, 5]
The meaning of values in the returned list after calling the method DateTime() are fairly obvious: year=2024, month=9, day=23, weekday=1 (Monday), hour=9, minutes=42, seconds=5.
The next example demonstrates some of the methods that might be used after the date and time has been set on the DS1302.
Example 2
# Test DS1302 MicroPython driver for micro:bit
from fc_ds1302 import *
# Instantiate a DS1302 RTC object.
ds1302 = DS1302()
# Test retrieval of clock seconds
print('Seconds are:', ds1302.Sec())
# test retrieval of clock minutes
print('Minutes are:', ds1302.Mins())
# test retrieval of clock hours
print('Hours are:', ds1302.Hr())
# Test formatted time method Time()
print('Time is:', ds1302.Time())
# Test formatted date method Date()
print('Date (yyyy-mm-dd) is:', ds1302.Date(fmt=1))
print('Date (dd-mm-yyyy) is:', ds1302.Date(fmt=2))
print('Date (mm-dd-yyyy) is:', ds1302.Date(fmt=3))
# Test reading & writing to the RAM
# Write value 2 to all RAM registers
for reg in range(1, 32):
ds1302.Ram(reg, 2)
# Read back all RAM values.
# Sum the values.
sum = 0
for reg in range(1, 32):
sum += ds1302.Ram(reg)
print('Sum =', sum)
Output: Seconds are: 50 Minutes are: 42 Hours are: 9 Time is: 09:42:50 Date (yyyy-mm-dd) is: 2024-09-23 Date (dd-mm-yyyy) is: 23-09-2024 Date (mm-dd-yyyy) is: 09-23-2024 Sum = 62
The final part of the above example program writes a value of 2 to each of the 31 RAM registers. It then reads back the value of each RAM register, summing the values. Since there are 31 registers each containing a value of 2, the sum will be 31 x 2 = 62.
Enjoy!
DS1302 Driver Code for micro:bit
Download as zip file
'''
DS1302 Real Time Clock Driver
for the BBC micro:bit
Original author: shaoziyang
Date: 2018.2
http://www.micropython.org.cn
Extended by fredscave.com
DATE: 2024.9
'''
from microbit import *
from micropython import const
REG_SEC = const(0x80)
REG_MIN = const(0x82)
REG_HR = const(0x84)
REG_DAY = const(0x86)
REG_MON = const(0x88)
REG_WDAY = const(0x8A)
REG_YR = const(0x8C)
REG_WP = const(0x8E)
# REG_CTRL = const(0x90)
REG_RAM = const(0xC0)
class DS1302:
def __init__(self, CLK=pin13, DAT=pin14, CS=pin15):
self.CLK = CLK
self.DAT = DAT
self.CS = CS
def DecToHex(self, dat):
return (dat//10) * 16 + (dat%10)
def HexToDec(self, dat):
return (dat//16) * 10 + (dat%16)
def write_byte(self, dat):
for i in range(8):
self.DAT.write_digital((dat >> i) & 1)
self.CLK.write_digital(1)
self.CLK.write_digital(0)
def read_byte(self):
d = 0
for i in range(8):
d = d | (self.DAT.read_digital() << i)
self.CLK.write_digital(1)
self.CLK.write_digital(0)
return d
def getReg(self, reg):
self.CS.write_digital(1)
self.write_byte(reg)
t = self.read_byte()
self.CS.write_digital(0)
return t
def setReg(self, reg, dat):
self.CS.write_digital(1)
self.write_byte(reg)
self.write_byte(dat)
self.CS.write_digital(0)
def wr(self, reg, dat):
self.setReg(REG_WP, 0)
self.setReg(reg, dat)
self.setReg(REG_WP, 0x80)
def start(self):
t = self.getReg(REG_SEC + 1)
self.wr(REG_SEC, t & 0x7f)
def stop(self):
t = self.getReg(REG_SEC + 1)
self.wr(REG_SEC, t | 0x80)
def Sec(self, sec = None):
if sec == None:
return self.HexToDec(self.getReg(REG_SEC+1))%60
else:
self.wr(REG_SEC, self.DecToHex(sec%60))
def Mins(self, mins = None):
if mins == None:
return self.HexToDec(self.getReg(REG_MIN+1))
else:
self.wr(REG_MIN, self.DecToHex(mins%60))
def Hr(self, hr = None):
if hr == None:
return self.HexToDec(self.getReg(REG_HR+1))
else:
self.wr(REG_HR, self.DecToHex(hr%24))
def Wday(self, wday = None):
if wday == None:
return self.HexToDec(self.getReg(REG_WDAY+1))
else:
self.wr(REG_WDAY, self.DecToHex(wday%8))
def Day(self, day = None):
if day == None:
return self.HexToDec(self.getReg(REG_DAY+1))
else:
self.wr(REG_DAY, self.DecToHex(day%32))
def Mon(self, mon = None):
if mon == None:
return self.HexToDec(self.getReg(REG_MON+1))
else:
self.wr(REG_MON, self.DecToHex(mon%13))
def Yr(self, yr = None):
if yr == None:
return self.HexToDec(self.getReg(REG_YR+1)) + 2000
else:
self.wr(REG_YR, self.DecToHex(yr%100))
def DateTime(self, dat = None):
if dat == None:
return [self.Yr(),
self.Mon(),
self.Day(),
self.Wday(),
self.Hr(),
self.Mins(),
self.Sec()]
else:
self.Yr(dat[0])
self.Mon(dat[1])
self.Day(dat[2])
self.Wday(dat[3])
self.Hr(dat[4])
self.Mins(dat[5])
self.Sec(dat[6])
def Time(self, h=None, m=None, s=None):
if (h==None) and (m==None) and (s==None):
str_sec = "%02d" % self.Sec()
str_min = "%02d" % self.Mins()
str_hr = "%02d" % self.Hr()
return str_hr + ':' + str_min + ':' + str_sec
if (s != None):
self.Sec(s)
if (m != None):
self.Mins(m)
if (h != None):
self.Hr(h)
def Date(self, y=None, m=None, d=None, fmt=1):
# fmt = 1; YMD
# fmt = 2; DMY
# fmt = 3; MDY
if (y==None) and (m==None) and (d==None):
str_day = "%02d" % self.Day()
str_mon = "%02d" % self.Mon()
str_yr = "%04d" % self.Yr()
if fmt == 1:
return str_yr + '-' + str_mon + '-' + str_day
if fmt == 2:
return str_day + '-' + str_mon + '-' + str_yr
if fmt == 3:
return str_mon + '-' + str_day + '-' + str_yr
return None
if (d != None):
self.Day(d)
if (m != None):
self.Mon(m)
if (y != None):
self.Yr(y)
def Ram(self, reg, dat = None):
if dat == None:
return self.getReg(REG_RAM + 1 + (reg%31)*2)
else:
self.wr(REG_RAM + (reg%31)*2, dat)