kopia lustrzana https://github.com/peterhinch/micropython-samples
Prior to DS3231 update.
rodzic
d8a1810a90
commit
c259792a7c
|
@ -51,7 +51,8 @@ Public methods:
|
||||||
|
|
||||||
# 2. The Pyboard driver
|
# 2. The Pyboard driver
|
||||||
|
|
||||||
The principal reason to use this driver is to calibrate the Pyboard's RTC.
|
The principal reason to use this driver is to calibrate the Pyboard's RTC. This
|
||||||
|
does not yet support the Pyboard D.
|
||||||
|
|
||||||
This assumes that the DS3231 is connected to the hardware I2C port on the `X`
|
This assumes that the DS3231 is connected to the hardware I2C port on the `X`
|
||||||
or `Y` side of the board, and that the Pyboard's RTC is set to the correct time
|
or `Y` side of the board, and that the Pyboard's RTC is set to the correct time
|
||||||
|
@ -85,3 +86,7 @@ Public methods:
|
||||||
calibration factor and applies it to the Pyboard. It returns the calibration
|
calibration factor and applies it to the Pyboard. It returns the calibration
|
||||||
factor which may be stored in a file if the calibration needs to survive an
|
factor which may be stored in a file if the calibration needs to survive an
|
||||||
outage of all power sources.
|
outage of all power sources.
|
||||||
|
4. `getcal(minutes=5, cal=0, verbose=True)` Measures the performance of the
|
||||||
|
Pyboard RTC against the DS3231. If `cal` is specified, the calibration factor
|
||||||
|
is applied before the test is run. The default is to zero the calibration and
|
||||||
|
return the required factor.
|
||||||
|
|
|
@ -3,9 +3,19 @@
|
||||||
# Includes routine to calibrate the Pyboard's RTC from the DS3231
|
# Includes routine to calibrate the Pyboard's RTC from the DS3231
|
||||||
# delta method now operates to 1mS precision
|
# delta method now operates to 1mS precision
|
||||||
# precison of calibration further improved by timing Pyboard RTC transition
|
# precison of calibration further improved by timing Pyboard RTC transition
|
||||||
# Adapted by Peter Hinch, Jan 2016
|
# Adapted by Peter Hinch, Jan 2016, Jan 2020 for Pyboard D
|
||||||
|
|
||||||
|
# Pyboard D rtc.datetime()[7] counts microseconds. See end of page on
|
||||||
|
# https://pybd.io/hw/pybd_sfxw.htm
|
||||||
|
|
||||||
|
import utime
|
||||||
|
import pyb
|
||||||
|
import os
|
||||||
|
|
||||||
|
d_series = os.uname().machine.split(' ')[0][:4] == 'PYBD'
|
||||||
|
if d_series:
|
||||||
|
pyb.Pin.board.EN_3V3.value(1)
|
||||||
|
|
||||||
import utime, pyb
|
|
||||||
DS3231_I2C_ADDR = 104
|
DS3231_I2C_ADDR = 104
|
||||||
|
|
||||||
class DS3231Exception(OSError):
|
class DS3231Exception(OSError):
|
||||||
|
@ -15,14 +25,21 @@ rtc = pyb.RTC()
|
||||||
|
|
||||||
def now(): # Return the current time from the RTC in millisecs from year 2000
|
def now(): # Return the current time from the RTC in millisecs from year 2000
|
||||||
secs = utime.time()
|
secs = utime.time()
|
||||||
ms = 1000 * (255 -rtc.datetime()[7]) >> 8
|
if d_series:
|
||||||
if ms < 50: # Might have just rolled over
|
ms = rtc.datetime()[7] // 1000
|
||||||
|
else:
|
||||||
|
ms = 1000 * (255 -rtc.datetime()[7]) >> 8
|
||||||
|
if ms < 50: # Might have just rolled over
|
||||||
secs = utime.time()
|
secs = utime.time()
|
||||||
return 1000 * secs + ms
|
return 1000 * secs + ms
|
||||||
|
|
||||||
def nownr(): # Return the current time from the RTC: caller ensures transition has occurred
|
def nownr(): # Return the current time from the RTC: caller ensures transition has occurred
|
||||||
return 1000 * utime.time() + (1000 * (255 -rtc.datetime()[7]) >> 8)
|
if d_series:
|
||||||
|
return 1000 * utime.time() + rtc.datetime()[7] // 1000
|
||||||
|
return 1000 * utime.time() + (1000 * (255 -rtc.datetime()[7]) >> 8)
|
||||||
|
|
||||||
|
def get_us(s): # For Pyboard D: convert datetime to μs. Caller handles rollover
|
||||||
|
return (s[7] + s[6] * 1_000_000 + s[5] * 60_000_000 + s[4] * 3_600_000_000 )
|
||||||
# Driver for DS3231 accurate RTC module (+- 1 min/yr) needs adapting for Pyboard
|
# Driver for DS3231 accurate RTC module (+- 1 min/yr) needs adapting for Pyboard
|
||||||
# source https://github.com/scudderfish/uDS3231
|
# source https://github.com/scudderfish/uDS3231
|
||||||
def bcd2dec(bcd):
|
def bcd2dec(bcd):
|
||||||
|
@ -85,13 +102,13 @@ class DS3231:
|
||||||
self.ds3231.mem_write(dec2bcd(MM), DS3231_I2C_ADDR, 5)
|
self.ds3231.mem_write(dec2bcd(MM), DS3231_I2C_ADDR, 5)
|
||||||
self.ds3231.mem_write(dec2bcd(YY-1900), DS3231_I2C_ADDR, 6)
|
self.ds3231.mem_write(dec2bcd(YY-1900), DS3231_I2C_ADDR, 6)
|
||||||
|
|
||||||
def delta(self): # Return no. of mS RTC leads DS3231
|
def delta(self): # Return no. of mS RTC leads DS3231
|
||||||
self.await_transition()
|
self.await_transition()
|
||||||
rtc_ms = now()
|
rtc_ms = now()
|
||||||
t_ds3231 = utime.mktime(self.get_time()) # To second precision, still in same sec as transition
|
t_ds3231 = utime.mktime(self.get_time()) # To second precision, still in same sec as transition
|
||||||
return rtc_ms - 1000 * t_ds3231
|
return rtc_ms - 1000 * t_ds3231
|
||||||
|
|
||||||
def await_transition(self): # Wait until DS3231 seconds value changes
|
def await_transition(self): # Wait until DS3231 seconds value changes
|
||||||
data = self.ds3231.mem_read(self.timebuf, DS3231_I2C_ADDR, 0)
|
data = self.ds3231.mem_read(self.timebuf, DS3231_I2C_ADDR, 0)
|
||||||
ss = data[0]
|
ss = data[0]
|
||||||
while ss == data[0]:
|
while ss == data[0]:
|
||||||
|
@ -110,33 +127,69 @@ class DS3231:
|
||||||
# Note calibration factor is not saved on power down unless an RTC backup battery is used. An option is
|
# Note calibration factor is not saved on power down unless an RTC backup battery is used. An option is
|
||||||
# to store the calibration factor on disk and issue rtc.calibration(factor) on boot.
|
# to store the calibration factor on disk and issue rtc.calibration(factor) on boot.
|
||||||
|
|
||||||
def getcal(self, minutes=5):
|
def getcal(self, minutes=5, cal=0, verbose=True):
|
||||||
rtc.calibration(0) # Clear existing cal
|
if d_series:
|
||||||
self.save_time() # Set DS3231 from RTC
|
return self._getcal_d(minutes, cal, verbose)
|
||||||
self.await_transition() # Wait for DS3231 to change: on a 1 second boundary
|
verbose and print('Pyboard 1.x. Waiting {} minutes for calibration factor.'.format(minutes))
|
||||||
|
rtc.calibration(cal) # Clear existing cal
|
||||||
|
self.save_time() # Set DS3231 from RTC
|
||||||
|
self.await_transition() # Wait for DS3231 to change: on a 1 second boundary
|
||||||
tus = pyb.micros()
|
tus = pyb.micros()
|
||||||
st = rtc.datetime()[7]
|
st = rtc.datetime()[7]
|
||||||
while rtc.datetime()[7] == st: # Wait for RTC to change
|
while rtc.datetime()[7] == st: # Wait for RTC to change
|
||||||
pass
|
pass
|
||||||
t1 = pyb.elapsed_micros(tus) # t1 is duration (uS) between DS and RTC change (start)
|
t1 = pyb.elapsed_micros(tus) # t1 is duration (μs) between DS and RTC change (start)
|
||||||
rtcstart = nownr() # RTC start time in mS
|
rtcstart = nownr() # RTC start time in mS
|
||||||
dsstart = utime.mktime(self.get_time()) # DS start time in secs
|
dsstart = utime.mktime(self.get_time()) # DS start time in secs
|
||||||
pyb.delay(minutes * 60000)
|
pyb.delay(minutes * 60000)
|
||||||
self.await_transition() # DS second boundary
|
self.await_transition() # DS second boundary
|
||||||
tus = pyb.micros()
|
tus = pyb.micros()
|
||||||
st = rtc.datetime()[7]
|
st = rtc.datetime()[7]
|
||||||
while rtc.datetime()[7] == st:
|
while rtc.datetime()[7] == st:
|
||||||
pass
|
pass
|
||||||
t2 = pyb.elapsed_micros(tus) # t2 is duration (uS) between DS and RTC change (end)
|
t2 = pyb.elapsed_micros(tus) # t2 is duration (μs) between DS and RTC change (end)
|
||||||
rtcend = nownr()
|
rtcend = nownr()
|
||||||
dsend = utime.mktime(self.get_time())
|
dsend = utime.mktime(self.get_time())
|
||||||
dsdelta = (dsend - dsstart) * 1000000 # Duration (uS) between DS edges as measured by DS3231
|
dsdelta = (dsend - dsstart) * 1000000 # Duration (μs) between DS edges as measured by DS3231
|
||||||
rtcdelta = (rtcend - rtcstart) * 1000 + t1 -t2 # Duration (uS) between DS edges as measured by RTC and corrected
|
rtcdelta = (rtcend - rtcstart) * 1000 + t1 -t2 # Duration (μs) between DS edges as measured by RTC and corrected
|
||||||
ppm = (1000000* (rtcdelta - dsdelta))/dsdelta
|
ppm = (1000000* (rtcdelta - dsdelta))/dsdelta
|
||||||
return int(-ppm/0.954)
|
if cal:
|
||||||
|
verbose and print('Error {:4.1f}ppm {:4.1f}mins/year.'.format(ppm, ppm * 1.903))
|
||||||
|
return 0
|
||||||
|
cal = int(-ppm / 0.954)
|
||||||
|
verbose and print('Error {:4.1f}ppm {:4.1f}mins/year. Cal factor {}'.format(ppm, ppm * 1.903, cal))
|
||||||
|
return cal
|
||||||
|
|
||||||
|
# Version for Pyboard D. This has μs resolution.
|
||||||
|
def _getcal_d(self, minutes, cal, verbose):
|
||||||
|
verbose and print('Pyboard D. Waiting {} minutes for calibration factor.'.format(minutes))
|
||||||
|
rtc.calibration(cal) # Clear existing cal
|
||||||
|
self.save_time() # Set DS3231 from RTC
|
||||||
|
self.await_transition() # Wait for DS3231 to change: on a 1 second boundary
|
||||||
|
t = rtc.datetime() # Get RTC time
|
||||||
|
# Time of DS3231 transition measured by RTC in μs since start of day
|
||||||
|
rtc_start_us = get_us(t)
|
||||||
|
dsstart = utime.mktime(self.get_time()) # DS start time in secs
|
||||||
|
pyb.delay(minutes * 60_000)
|
||||||
|
self.await_transition() # Wait for DS second boundary
|
||||||
|
t = rtc.datetime()
|
||||||
|
# Time of DS3231 transition measured by RTC in μs since start of day
|
||||||
|
rtc_end_us = get_us(t)
|
||||||
|
dsend = utime.mktime(self.get_time()) # DS start time in secs
|
||||||
|
if rtc_end_us < rtc_start_us: # It's run past midnight. Assumption: run time < 1 day!
|
||||||
|
rtc_end_us += 24 * 3_600_000_000
|
||||||
|
|
||||||
|
dsdelta = (dsend - dsstart) * 1_000_000 # Duration (μs) between DS3231 edges as measured by DS3231
|
||||||
|
rtcdelta = rtc_end_us - rtc_start_us # Duration (μs) between DS edges as measured by RTC
|
||||||
|
ppm = (1_000_000 * (rtcdelta - dsdelta)) / dsdelta
|
||||||
|
if cal: # We've already calibrated. Just report results.
|
||||||
|
verbose and print('Error {:4.1f}ppm {:4.1f}mins/year.'.format(ppm, ppm * 1.903))
|
||||||
|
return 0
|
||||||
|
cal = int(-ppm / 0.954)
|
||||||
|
verbose and print('Error {:4.1f}ppm {:4.1f}mins/year. Cal factor {}'.format(ppm, ppm * 1.903, cal))
|
||||||
|
return cal
|
||||||
|
|
||||||
def calibrate(self, minutes=5):
|
def calibrate(self, minutes=5):
|
||||||
print('Waiting {} minutes to acquire calibration factor...'.format(minutes))
|
|
||||||
cal = self.getcal(minutes)
|
cal = self.getcal(minutes)
|
||||||
rtc.calibration(cal)
|
rtc.calibration(cal)
|
||||||
print('Pyboard RTC is calibrated. Factor is {}.'.format(cal))
|
print('Pyboard RTC is calibrated. Factor is {}.'.format(cal))
|
||||||
|
|
Ładowanie…
Reference in New Issue