kopia lustrzana https://github.com/peterhinch/micropython-samples
DS3231 Support Pyboard D. Various updates and fixes.
rodzic
c259792a7c
commit
f62a6d8e1c
|
@ -1,13 +1,14 @@
|
|||
# The DS3231 real time clock chip
|
||||
|
||||
This is a remarkably inexpensive and easily interfaced battery-backed RTC. It
|
||||
is an ideal way to rapidly calibrate the Pyboard's RTC which can then achieve
|
||||
is an ideal way rapidly to calibrate the Pyboard's RTC which can then achieve
|
||||
similar levels of accuracy (+- ~2 mins/year). The chip can also provide
|
||||
accurate time to platforms lacking a good RTC (notably the ESP8266).
|
||||
|
||||
Two drivers are provided:
|
||||
1. `ds3231_port.py` A multi-platform driver.
|
||||
2. `ds3231_pb.py` A Pyboard-specific driver with RTC calibration facility.
|
||||
2. `ds3231_pb.py` A Pyboard-specific driver with RTC calibration facility. For
|
||||
Pyboard 1.x and Pyboard D.
|
||||
|
||||
Breakout boards are widely available. The interface is I2C. Pullups to 3.3V
|
||||
(typically 10KΩ) should be provided on the `SCL` and `SDA` lines if these are
|
||||
|
@ -15,7 +16,8 @@ not supplied on the breakout board.
|
|||
|
||||
Both divers use edge detection to achieve millisecond-level precision from the
|
||||
DS3231. This enables relatively rapid accuracy testing of the platform's RTC,
|
||||
and fast calibration of the Pyboard's RTC.
|
||||
and fast calibration of the Pyboard's RTC. To quantify this, a sufficiently
|
||||
precise value of calibration may be acquired in 5-10 minutes.
|
||||
|
||||
###### [Main README](../README.md)
|
||||
|
||||
|
@ -23,10 +25,28 @@ and fast calibration of the Pyboard's RTC.
|
|||
|
||||
This can use soft I2C so any pins may be used.
|
||||
|
||||
It is based on the assumption that, where a hardware RTC exists, MicroPython's
|
||||
local time (`utime.localtime()`) is based on the RTC time. Changes to local
|
||||
time don't propagate to the RTC which must explicitly be set. This holds for
|
||||
the Pyboard, ESP8266 and ESP32.
|
||||
It uses the currently undocumented `RTC.datetime()` method to set and to query
|
||||
the platform RTC. This appears to be the only cross-platform way to do this.
|
||||
The meaning of the subseconds field is hardware dependent so this is ignored.
|
||||
The RTC is checked against the DS3231 by timing the transition of the seconds
|
||||
field of each clock (using system time to measure the relative timing of the
|
||||
edges).
|
||||
|
||||
This example ran on a WeMos D1 Mini ESP8266 board, also a generic ESP32.
|
||||
```python
|
||||
from ds3231_port import DS3231
|
||||
from machine import Pin, I2C
|
||||
# Pins with pullups on ESP8266: clk=WeMos D3(P0) data=WeMos D4(P2)
|
||||
i2c = I2C(-1, Pin(0, Pin.OPEN_DRAIN), Pin(2, Pin.OPEN_DRAIN))
|
||||
ds3231 = DS3231(i2c)
|
||||
ds3231.get_time()
|
||||
```
|
||||
Testing the onboard RTC:
|
||||
```
|
||||
ds3231.rtc_test() # Takes 10 minutes
|
||||
```
|
||||
In my testing the ESP8266 RTC was out by 5%. The ESP32 was out by 6.7ppm or
|
||||
about 12 minutes/yr. Hardware samples will vary.
|
||||
|
||||
## 1.1 The DS3231 class
|
||||
|
||||
|
@ -34,35 +54,42 @@ Constructor:
|
|||
This takes one mandatory argument, an initialised I2C bus.
|
||||
|
||||
Public methods:
|
||||
1. `get_time` Optional boolean arg `set_rtc=False`. If `set_rtc` is `True` it
|
||||
sets the platform's RTC from the DS3231. It returns the DS3231 time as a tuple
|
||||
in the same format as `utime.localtime()` except that yday (day of year) is 0.
|
||||
So the format is (year, month, day, hour, minute, second, wday, 0).
|
||||
1. `get_time(set_rtc=False)`. If `set_rtc` is `True` it sets the platform's
|
||||
RTC from the DS3231. It returns the DS3231 time as a tuple in the same format
|
||||
as `utime.localtime()` except that yday (day of year) is 0. So the format is
|
||||
(year, month, day, hour, minute, second, wday, 0).
|
||||
Note that on ports/platforms which don't support an RTC, if `set_rtc` is
|
||||
`True`, the local time will be set from the DS3231.
|
||||
2. `save_time` No args. Sets the DS3231 time from the platform's local time.
|
||||
3. `rtc_test` Optional args: `runtime=600`, `ppm=False`. This tests the
|
||||
platform's local time against the DS3231 returning the error in parts per
|
||||
million (if `ppm` is `True`) or seconds per year. A positive value indicates
|
||||
that the DS3231 clock leads the platform local time.
|
||||
2. `save_time()` No args. Sets the DS3231 time from the platform's local time.
|
||||
3. `rtc_test(runtime=600, ppm=False, verbose=True)`. This tests the platform's
|
||||
RTC time against the DS3231 returning the error in parts per million (if `ppm`
|
||||
is `True`) or seconds per year. A positive value indicates that the DS3231
|
||||
clock leads the platform RTC.
|
||||
The `runtime` value in seconds defines the duration of the test. The default
|
||||
of 10 minutes provides high accuracy but shorter durations will suffice on
|
||||
devices with poor RTC's (e.g. ESP8266).
|
||||
If `machine.RTC` is unsupported a `RuntimeError` will be thrown.
|
||||
|
||||
# 2. The Pyboard driver
|
||||
|
||||
The principal reason to use this driver is to calibrate the Pyboard's RTC. This
|
||||
does not yet support the Pyboard D.
|
||||
supports the Pyboard 1.x and Pyboard D. Note that the RTC on the Pyboard D is
|
||||
much more accurate than that on the Pyboard 1.x but can still be in error by up
|
||||
to 20ppm. It can benefit from calibration. For this to work reliably on the D a
|
||||
firmware build later than V1.12 is required: use a daily build if a later
|
||||
release is not yet available.
|
||||
|
||||
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
|
||||
and date.
|
||||
The sample below assumes that the DS3231 is connected to the hardware I2C port
|
||||
via I2C(2) but any I2C may be used including soft I2C. Ensure that the Pyboard
|
||||
RTC is set to the correct time and date.
|
||||
|
||||
Usage to calibrate the Pyboard's RTC. Takes 5 minutes.
|
||||
|
||||
```python
|
||||
from ds3231_pb import DS3231
|
||||
ds3231 = DS3231('X')
|
||||
import machine
|
||||
i2c = machine.I2C(2) # Connected on 'Y' side Y9 clk Y10 data
|
||||
ds3231 = DS3231(i2c)
|
||||
ds3231.save_time() # Set DS3231 to match Pyboard RTC
|
||||
ds3231.calibrate()
|
||||
```
|
||||
|
@ -73,19 +100,19 @@ used the RTC will run accurately in the event of a power outage.
|
|||
## 2.1 The DS3231 class
|
||||
|
||||
Constructor:
|
||||
This takes one mandatory argument, a string identifying the Pyboard side in use
|
||||
('X' or 'Y').
|
||||
This takes one mandatory argument, an I2C bus instantiated using the `machine`
|
||||
library.
|
||||
|
||||
Public methods:
|
||||
1. `get_time` Optional boolean arg `set_rtc=False`. If `set_rtc` is True it
|
||||
sets the Pyboard's RTC from the DS3231. It returns the DS3231 time as a tuple
|
||||
in the same format as `utime.localtime()` except that yday (day of year) is 0.
|
||||
namely (year, month, day, hour, minute, second, wday, 0).
|
||||
2. `save_time` No args. Sets the DS3231 time from the Pyboard's RTC.
|
||||
3. `calibrate` Optional arg `minutes=5`. The time to run. This calculates the
|
||||
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
|
||||
outage of all power sources.
|
||||
1. `get_time(set_rtc=False)`. If `set_rtc` is `True` it sets the Pyboard's RTC
|
||||
from the DS3231. It returns the DS3231 time as a tuple in the same format as
|
||||
`utime.localtime()` except that yday (day of year) is 0.
|
||||
Namely (year, month, day, hour, minute, second, wday, 0).
|
||||
2. `save_time()` No args. Sets the DS3231 time from the Pyboard's RTC.
|
||||
3. `calibrate(minutes=5)`. The time to run. This calculates the 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 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
|
||||
|
|
|
@ -1,47 +1,36 @@
|
|||
# Pyboard driver for DS3231 precison real time clock.
|
||||
# Adapted from WiPy driver at https://github.com/scudderfish/uDS3231
|
||||
# Includes routine to calibrate the Pyboard's RTC from the DS3231
|
||||
# delta method now operates to 1mS precision
|
||||
# precison of calibration further improved by timing Pyboard RTC transition
|
||||
# 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
|
||||
# Note docs for machine.RTC are wrong for Pyboard. The undocumented datetime
|
||||
# method seems to be the only way to set the RTC and it follows the same
|
||||
# convention as the pyb RTC's datetime method.
|
||||
|
||||
import utime
|
||||
import pyb
|
||||
import machine
|
||||
import os
|
||||
|
||||
d_series = os.uname().machine.split(' ')[0][:4] == 'PYBD'
|
||||
if d_series:
|
||||
pyb.Pin.board.EN_3V3.value(1)
|
||||
machine.Pin.board.EN_3V3.value(1)
|
||||
|
||||
DS3231_I2C_ADDR = 104
|
||||
|
||||
class DS3231Exception(OSError):
|
||||
pass
|
||||
|
||||
rtc = pyb.RTC()
|
||||
rtc = machine.RTC()
|
||||
|
||||
def now(): # Return the current time from the RTC in millisecs from year 2000
|
||||
secs = utime.time()
|
||||
if d_series:
|
||||
ms = rtc.datetime()[7] // 1000
|
||||
else:
|
||||
ms = 1000 * (255 -rtc.datetime()[7]) >> 8
|
||||
if ms < 50: # Might have just rolled over
|
||||
secs = utime.time()
|
||||
return 1000 * secs + ms
|
||||
|
||||
def nownr(): # Return the current time from the RTC: caller ensures transition has occurred
|
||||
if d_series:
|
||||
return 1000 * utime.time() + rtc.datetime()[7] // 1000
|
||||
return 1000 * utime.time() + (1000 * (255 -rtc.datetime()[7]) >> 8)
|
||||
def get_ms(s): # Pyboard 1.x datetime to ms. Caller handles rollover.
|
||||
return (1000 * (255 - s[7]) >> 8) + s[6] * 1000 + s[5] * 60_000 + s[4] * 3_600_000
|
||||
|
||||
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
|
||||
# source https://github.com/scudderfish/uDS3231
|
||||
return s[7] + s[6] * 1_000_000 + s[5] * 60_000_000 + s[4] * 3_600_000_000
|
||||
|
||||
def bcd2dec(bcd):
|
||||
return (((bcd & 0xf0) >> 4) * 10 + (bcd & 0x0f))
|
||||
|
||||
|
@ -49,25 +38,25 @@ def dec2bcd(dec):
|
|||
tens, units = divmod(dec, 10)
|
||||
return (tens << 4) + units
|
||||
|
||||
def tobytes(num):
|
||||
return num.to_bytes(1, 'little')
|
||||
|
||||
class DS3231:
|
||||
def __init__(self, side = 'X'):
|
||||
side = side.lower()
|
||||
if side == 'x':
|
||||
bus = 1
|
||||
elif side == 'y':
|
||||
bus = 2
|
||||
else:
|
||||
raise ValueError('Side must be "X" or "Y"')
|
||||
self.ds3231 = pyb.I2C(bus, mode=pyb.I2C.MASTER, baudrate=400000)
|
||||
def __init__(self, i2c):
|
||||
self.ds3231 = i2c
|
||||
self.timebuf = bytearray(7)
|
||||
if DS3231_I2C_ADDR not in self.ds3231.scan():
|
||||
raise DS3231Exception("DS3231 not found on I2C bus at %d" % DS3231_I2C_ADDR)
|
||||
|
||||
def get_time(self, set_rtc=False):
|
||||
if set_rtc:
|
||||
data = self.await_transition() # For accuracy set RTC immediately after a seconds transition
|
||||
self.await_transition() # For accuracy set RTC immediately after a seconds transition
|
||||
else:
|
||||
data = self.ds3231.mem_read(self.timebuf, DS3231_I2C_ADDR, 0) # don't wait
|
||||
self.ds3231.readfrom_mem_into(DS3231_I2C_ADDR, 0, self.timebuf) # don't wait
|
||||
return self.convert(set_rtc)
|
||||
|
||||
def convert(self, set_rtc=False):
|
||||
data = self.timebuf
|
||||
ss = bcd2dec(data[0])
|
||||
mm = bcd2dec(data[1])
|
||||
if data[2] & 0x40:
|
||||
|
@ -89,31 +78,25 @@ class DS3231:
|
|||
return (YY, MM, DD, hh, mm, ss, wday -1, 0) # Time from DS3231 in time.time() format (less yday)
|
||||
|
||||
def save_time(self):
|
||||
(YY, MM, DD, wday, hh, mm, ss, subsecs) = rtc.datetime()
|
||||
self.ds3231.mem_write(dec2bcd(ss), DS3231_I2C_ADDR, 0)
|
||||
self.ds3231.mem_write(dec2bcd(mm), DS3231_I2C_ADDR, 1)
|
||||
self.ds3231.mem_write(dec2bcd(hh), DS3231_I2C_ADDR, 2) # Sets to 24hr mode
|
||||
self.ds3231.mem_write(dec2bcd(wday), DS3231_I2C_ADDR, 3) # 1 == Monday, 7 == Sunday
|
||||
self.ds3231.mem_write(dec2bcd(DD), DS3231_I2C_ADDR, 4)
|
||||
(YY, MM, mday, hh, mm, ss, wday, yday) = utime.localtime() # Based on RTC
|
||||
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 0, tobytes(dec2bcd(ss)))
|
||||
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 1, tobytes(dec2bcd(mm)))
|
||||
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 2, tobytes(dec2bcd(hh))) # Sets to 24hr mode
|
||||
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 3, tobytes(dec2bcd(wday + 1))) # 1 == Monday, 7 == Sunday
|
||||
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 4, tobytes(dec2bcd(mday))) # Day of month
|
||||
if YY >= 2000:
|
||||
self.ds3231.mem_write(dec2bcd(MM) | 0b10000000, DS3231_I2C_ADDR, 5)
|
||||
self.ds3231.mem_write(dec2bcd(YY-2000), DS3231_I2C_ADDR, 6)
|
||||
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 5, tobytes(dec2bcd(MM) | 0b10000000)) # Century bit
|
||||
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 6, tobytes(dec2bcd(YY-2000)))
|
||||
else:
|
||||
self.ds3231.mem_write(dec2bcd(MM), DS3231_I2C_ADDR, 5)
|
||||
self.ds3231.mem_write(dec2bcd(YY-1900), DS3231_I2C_ADDR, 6)
|
||||
|
||||
def delta(self): # Return no. of mS RTC leads DS3231
|
||||
self.await_transition()
|
||||
rtc_ms = now()
|
||||
t_ds3231 = utime.mktime(self.get_time()) # To second precision, still in same sec as transition
|
||||
return rtc_ms - 1000 * t_ds3231
|
||||
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 5, tobytes(dec2bcd(MM)))
|
||||
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 6, tobytes(dec2bcd(YY-1900)))
|
||||
|
||||
def await_transition(self): # Wait until DS3231 seconds value changes
|
||||
data = self.ds3231.mem_read(self.timebuf, DS3231_I2C_ADDR, 0)
|
||||
ss = data[0]
|
||||
while ss == data[0]:
|
||||
data = self.ds3231.mem_read(self.timebuf, DS3231_I2C_ADDR, 0)
|
||||
return data
|
||||
self.ds3231.readfrom_mem_into(DS3231_I2C_ADDR, 0, self.timebuf)
|
||||
ss = self.timebuf[0]
|
||||
while ss == self.timebuf[0]:
|
||||
self.ds3231.readfrom_mem_into(DS3231_I2C_ADDR, 0, self.timebuf)
|
||||
return self.timebuf
|
||||
|
||||
# Get calibration factor for Pyboard RTC. Note that the DS3231 doesn't have millisecond resolution so we
|
||||
# wait for a seconds transition to emulate it.
|
||||
|
@ -134,23 +117,27 @@ class DS3231:
|
|||
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 = utime.ticks_us()
|
||||
st = rtc.datetime()[7]
|
||||
while rtc.datetime()[7] == st: # Wait for RTC to change
|
||||
pass
|
||||
t1 = pyb.elapsed_micros(tus) # t1 is duration (μs) between DS and RTC change (start)
|
||||
rtcstart = nownr() # RTC start time in mS
|
||||
dsstart = utime.mktime(self.get_time()) # DS start time in secs
|
||||
pyb.delay(minutes * 60000)
|
||||
t1 = utime.ticks_diff(utime.ticks_us(), tus) # t1 is duration (μs) between DS and RTC change (start)
|
||||
rtcstart = get_ms(rtc.datetime()) # RTC start time in mS
|
||||
dsstart = utime.mktime(self.convert()) # DS start time in secs as recorded by await_transition
|
||||
|
||||
utime.sleep(minutes * 60)
|
||||
|
||||
self.await_transition() # DS second boundary
|
||||
tus = pyb.micros()
|
||||
tus = utime.ticks_us()
|
||||
st = rtc.datetime()[7]
|
||||
while rtc.datetime()[7] == st:
|
||||
pass
|
||||
t2 = pyb.elapsed_micros(tus) # t2 is duration (μs) between DS and RTC change (end)
|
||||
rtcend = nownr()
|
||||
dsend = utime.mktime(self.get_time())
|
||||
t2 = utime.ticks_diff(utime.ticks_us(), tus) # t2 is duration (μs) between DS and RTC change (end)
|
||||
rtcend = get_ms(rtc.datetime())
|
||||
dsend = utime.mktime(self.convert())
|
||||
dsdelta = (dsend - dsstart) * 1000000 # Duration (μs) between DS edges as measured by DS3231
|
||||
if rtcend < rtcstart: # It's run past midnight. Assumption: run time < 1 day!
|
||||
rtcend += 24 * 3_600_000
|
||||
rtcdelta = (rtcend - rtcstart) * 1000 + t1 - t2 # Duration (μs) between DS edges as measured by RTC and corrected
|
||||
ppm = (1000000* (rtcdelta - dsdelta))/dsdelta
|
||||
if cal:
|
||||
|
@ -169,13 +156,15 @@ class DS3231:
|
|||
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)
|
||||
dsstart = utime.mktime(self.convert()) # DS start time in secs
|
||||
|
||||
utime.sleep(minutes * 60)
|
||||
|
||||
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
|
||||
dsend = utime.mktime(self.convert()) # DS end 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
|
||||
|
||||
|
|
|
@ -96,13 +96,37 @@ class DS3231:
|
|||
# one-seond boundaries and using ticks_ms() to time the RTC.
|
||||
# For a 10 minute measurement +-1ms corresponds to 1.7ppm or 53s/yr. Longer
|
||||
# runtimes improve this, but the DS3231 is "only" good for +-2ppm over 0-40C.
|
||||
def rtc_test(self, runtime=600, ppm=False):
|
||||
factor = 1000000 if ppm else 31557600 # seconds per year
|
||||
self.await_transition() # Start on transition
|
||||
rtc_start = utime.ticks_ms() # and get RTC time NOW
|
||||
ds3231_start = utime.mktime(self.convert())
|
||||
def rtc_test(self, runtime=600, ppm=False, verbose=True):
|
||||
if rtc is None:
|
||||
raise RuntimeError('machine.RTC does not exist')
|
||||
verbose and print('Waiting {} minutes for result'.format(runtime//60))
|
||||
factor = 1_000_000 if ppm else 114_155_200 # seconds per year
|
||||
|
||||
self.await_transition() # Start on transition of DS3231. Record time in .timebuf
|
||||
t = utime.ticks_ms() # Get system time now
|
||||
ss = rtc.datetime()[6] # Seconds from system RTC
|
||||
while ss == rtc.datetime()[6]:
|
||||
pass
|
||||
ds = utime.ticks_diff(utime.ticks_ms(), t) # ms to transition of RTC
|
||||
ds3231_start = utime.mktime(self.convert()) # Time when transition occurred
|
||||
t = rtc.datetime()
|
||||
rtc_start = utime.mktime((t[0], t[1], t[2], t[4], t[5], t[6], t[3] - 1, 0)) # y m d h m s wday 0
|
||||
|
||||
utime.sleep(runtime) # Wait a while (precision doesn't matter)
|
||||
self.await_transition()
|
||||
d_rtc = utime.ticks_diff(utime.ticks_ms(), rtc_start)
|
||||
d_ds3231 = 1000 * (utime.mktime(self.convert()) - ds3231_start)
|
||||
return (d_ds3231 - d_rtc) * factor / d_ds3231
|
||||
|
||||
self.await_transition() # of DS3231 and record the time
|
||||
t = utime.ticks_ms() # and get system time now
|
||||
ss = rtc.datetime()[6] # Seconds from system RTC
|
||||
while ss == rtc.datetime()[6]:
|
||||
pass
|
||||
de = utime.ticks_diff(utime.ticks_ms(), t) # ms to transition of RTC
|
||||
ds3231_end = utime.mktime(self.convert()) # Time when transition occurred
|
||||
t = rtc.datetime()
|
||||
rtc_end = utime.mktime((t[0], t[1], t[2], t[4], t[5], t[6], t[3] - 1, 0)) # y m d h m s wday 0
|
||||
|
||||
d_rtc = 1000 * (rtc_end - rtc_start) + de - ds # ms recorded by RTC
|
||||
d_ds3231 = 1000 * (ds3231_end - ds3231_start) # ms recorded by DS3231
|
||||
ratio = (d_ds3231 - d_rtc) / d_ds3231
|
||||
ppm = ratio * 1_000_000
|
||||
verbose and print('DS3231 leads RTC by {:4.1f}ppm {:4.1f}mins/yr'.format(ppm, ppm*1.903))
|
||||
return ratio * factor
|
||||
|
|
Ładowanie…
Reference in New Issue