kopia lustrzana https://github.com/peterhinch/micropython-samples
ds3231_gen: move setting RTC from code to docs.
rodzic
14ebf88797
commit
6c478601a6
|
@ -21,9 +21,27 @@ the DS3231. This enables relatively rapid accuracy testing of the platform's
|
||||||
RTC, and fast calibration of the Pyboard's RTC. To quantify this, a
|
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.
|
sufficiently precise value of calibration may be acquired in 5-10 minutes.
|
||||||
|
|
||||||
|
# Datetime tuples
|
||||||
|
|
||||||
|
MicroPython currently enjoys three formats. The first is that used by the
|
||||||
|
`time` module in `localtime` and `gmtime`. This is
|
||||||
|
`(year, month, mday, hour, minute, second, weekday, yearday)`
|
||||||
|
with the meaning of each field as described in the
|
||||||
|
[MP official docs](http://docs.micropython.org/en/latest/library/time.html#functions) and
|
||||||
|
[CPython docs](https://docs.python.org/3/library/time.html#time.struct_time).
|
||||||
|
**These drivers use this format as it is the CPython standard.**
|
||||||
|
|
||||||
|
The micropython RTC class uses [this format](http://docs.micropython.org/en/latest/library/machine.RTC.html#machine.RTC.init)
|
||||||
|
in its `__init__` method:
|
||||||
|
`(year, month, day[, hour[, minute[, second[, microsecond[, tzinfo]]]]])`
|
||||||
|
and this format in its `datetime` method:
|
||||||
|
`(year, month, mday, weekday, hours, minutes, seconds, subseconds)`
|
||||||
|
It is to be hoped that some standardisation will occur. Please check official
|
||||||
|
docs for any changes.
|
||||||
|
|
||||||
###### [Main README](../README.md)
|
###### [Main README](../README.md)
|
||||||
|
|
||||||
# 1. General purpose driver ds3231_gen.py
|
# 1. General purpose driver ds3231_gen
|
||||||
|
|
||||||
This uses datetime tuples to set and read time values. These are of form
|
This uses datetime tuples to set and read time values. These are of form
|
||||||
(year, month, day, hour, minute, second, weekday, yearday)
|
(year, month, day, hour, minute, second, weekday, yearday)
|
||||||
|
@ -35,12 +53,8 @@ as used by [time.localtime](http://docs.micropython.org/en/latest/library/time.h
|
||||||
This takes one mandatory argument, an initialised I2C bus.
|
This takes one mandatory argument, an initialised I2C bus.
|
||||||
|
|
||||||
#### Public methods:
|
#### Public methods:
|
||||||
1. `get_time(set_rtc=False)`. If `set_rtc` is `True` it sets the platform's
|
1. `get_time()`. Returns the DS3231 time as a datetime tuple with
|
||||||
RTC from the DS3231. Returns the DS3231 time as a datetime tuple with
|
|
||||||
`yearday=0`.
|
`yearday=0`.
|
||||||
On ports/platforms which don't support an RTC, if `set_rtc` is `True`, the
|
|
||||||
system time will be set from the DS3231. If setting the RTC, for accuracy the
|
|
||||||
method will pause until a seconds transition occurs on the DS3231.
|
|
||||||
2. `set_time(tt=None)`. Sets the DS3231 time. By default it uses the
|
2. `set_time(tt=None)`. Sets the DS3231 time. By default it uses the
|
||||||
platform's syatem time, otherwise the passed `datetime` tuple. If passing a
|
platform's syatem time, otherwise the passed `datetime` tuple. If passing a
|
||||||
tuple, see the note below.
|
tuple, see the note below.
|
||||||
|
@ -72,7 +86,7 @@ This takes one mandatory argument, an initialised I2C bus.
|
||||||
#### Module constants
|
#### Module constants
|
||||||
|
|
||||||
These are the allowable options for the alarm's `when` arg, along with the
|
These are the allowable options for the alarm's `when` arg, along with the
|
||||||
relevant `Alarm.set()` args:
|
relevant `Alarm.set()` args:
|
||||||
`EVERY_SECOND` Only supported by alarm1.
|
`EVERY_SECOND` Only supported by alarm1.
|
||||||
`EVERY_MINUTE` `sec`
|
`EVERY_MINUTE` `sec`
|
||||||
`EVERY_HOUR` `min`, `sec`
|
`EVERY_HOUR` `min`, `sec`
|
||||||
|
@ -97,7 +111,8 @@ def dt_tuple(dt):
|
||||||
|
|
||||||
#### Alarms
|
#### Alarms
|
||||||
|
|
||||||
Comments assume that a backup battery is in use.
|
Comments assume that a backup battery is in use, in which case alarms continue
|
||||||
|
to operate.
|
||||||
|
|
||||||
The battery ensures that alarm settings are stored through a power outage. If
|
The battery ensures that alarm settings are stored through a power outage. If
|
||||||
an alarm occurs during an outage the pin will be driven low and will stay low
|
an alarm occurs during an outage the pin will be driven low and will stay low
|
||||||
|
@ -110,10 +125,10 @@ from machine import SoftI2C, Pin
|
||||||
from ds3231_gen import *
|
from ds3231_gen import *
|
||||||
i2c = SoftI2C(scl=Pin(16, Pin.OPEN_DRAIN, value=1), sda=Pin(17, Pin.OPEN_DRAIN, value=1))
|
i2c = SoftI2C(scl=Pin(16, Pin.OPEN_DRAIN, value=1), sda=Pin(17, Pin.OPEN_DRAIN, value=1))
|
||||||
d = DS3231(i2c)
|
d = DS3231(i2c)
|
||||||
dt.alarm1.set(EVERY_MINUTE, sec=30)
|
dt.alarm1.set(EVERY_MINUTE, sec=30) # Alarm on the half minute
|
||||||
```
|
```
|
||||||
If a power outage occurs here the following code will ensure alarms continue to
|
If a power outage occurs here the following code demonstrates that alarms
|
||||||
occur at one minute intervals:
|
continue to occur at one minute intervals:
|
||||||
```python
|
```python
|
||||||
from machine import SoftI2C, Pin
|
from machine import SoftI2C, Pin
|
||||||
from ds3231_gen import *
|
from ds3231_gen import *
|
||||||
|
@ -132,6 +147,47 @@ Re the `INT\` (alarm) pin the datasheet (P9) states "The pullup voltage can be
|
||||||
up to 5.5V, regardless of the voltage on Vcc". Note that some breakout boards
|
up to 5.5V, regardless of the voltage on Vcc". Note that some breakout boards
|
||||||
have a pullup resistor between this pin and Vcc.
|
have a pullup resistor between this pin and Vcc.
|
||||||
|
|
||||||
|
#### Setting system RTC
|
||||||
|
|
||||||
|
Note that the DS3231 driver uses the CPython standard datetime tuple:
|
||||||
|
`(year, month, mday, hour, minute, second, weekday, yearday)`
|
||||||
|
Currently the RTC `datetime` method uses a different format.
|
||||||
|
|
||||||
|
The system RTC may be set from the DS3231 as follows:
|
||||||
|
```python
|
||||||
|
from machine import SoftI2C, Pin, RTC
|
||||||
|
from ds3231_gen import *
|
||||||
|
i2c = SoftI2C(scl=Pin(16, Pin.OPEN_DRAIN, value=1), sda=Pin(17, Pin.OPEN_DRAIN, value=1))
|
||||||
|
d = DS3231(i2c)
|
||||||
|
rtc = RTC()
|
||||||
|
YY, MM, DD, hh, mm, ss, wday, _ = d.get_time()
|
||||||
|
rtc.datetime((YY, MM, DD, wday, hh, mm, ss, 0))
|
||||||
|
```
|
||||||
|
The following can be used to set the RTC to better than +-1s accuracy. Though
|
||||||
|
the DS3231 has only 1s resolution, setting the RTC on a transition of the
|
||||||
|
seconds value minimises error.
|
||||||
|
```python
|
||||||
|
t = d.get_time() # As per above, d is the DS3231 instance
|
||||||
|
while t == d.get_time()[5]: # Wait for change in seconds
|
||||||
|
pass
|
||||||
|
YY, MM, DD, hh, mm, ss, wday, _ = t # Set time now
|
||||||
|
rtc.datetime((YY, MM, DD, wday, hh, mm, ss, 0))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Application: micropower systems
|
||||||
|
|
||||||
|
The DS3231 alarm pin may be used to control the application of power to the
|
||||||
|
MicroPython host and peripheral devices. This may be done with a PNP transistor
|
||||||
|
driven (via suitable resistors) from the alarm output. When in an alarm state,
|
||||||
|
power is applied to the system. The DS3231 is set to alarm at the required
|
||||||
|
interval - say at 00:03:00 every Sunday. On startup the system takes readings
|
||||||
|
and logs them or reports them via MQTT. Its final act is to issue
|
||||||
|
```python
|
||||||
|
ds3231.alarm1.clear()
|
||||||
|
```
|
||||||
|
which turns off the MOSFET and the transistor and powers down the system.
|
||||||
|
Current in the OFF state will be very much less than 1μA.
|
||||||
|
|
||||||
# 2. Portable driver ds3231_port
|
# 2. Portable driver ds3231_port
|
||||||
|
|
||||||
This can use soft I2C so any pins may be used.
|
This can use soft I2C so any pins may be used.
|
||||||
|
|
|
@ -71,26 +71,16 @@ class DS3231:
|
||||||
if _ADDR not in self.ds3231.scan():
|
if _ADDR not in self.ds3231.scan():
|
||||||
raise RuntimeError(f"DS3231 not found on I2C bus at {_ADDR}")
|
raise RuntimeError(f"DS3231 not found on I2C bus at {_ADDR}")
|
||||||
|
|
||||||
def get_time(self, set_rtc=False, data=bytearray(7)):
|
def get_time(self, data=bytearray(7)):
|
||||||
def bcd2dec(bcd): # Strip MSB
|
def bcd2dec(bcd): # Strip MSB
|
||||||
return ((bcd & 0x70) >> 4) * 10 + (bcd & 0x0F)
|
return ((bcd & 0x70) >> 4) * 10 + (bcd & 0x0F)
|
||||||
|
|
||||||
self.ds3231.readfrom_mem_into(_ADDR, 0, data)
|
self.ds3231.readfrom_mem_into(_ADDR, 0, data)
|
||||||
if set_rtc: # For accuracy set RTC immediately after a seconds transition
|
|
||||||
ss = data[0]
|
|
||||||
while ss == data[0]:
|
|
||||||
self.ds3231.readfrom_mem_into(_ADDR, 0, data)
|
|
||||||
ss, mm, hh, wday, DD, MM, YY = [bcd2dec(x) for x in data]
|
ss, mm, hh, wday, DD, MM, YY = [bcd2dec(x) for x in data]
|
||||||
MM &= 0x1F # Strip century
|
MM &= 0x1F # Strip century
|
||||||
YY += 2000
|
YY += 2000
|
||||||
# Time from DS3231 in time.localtime() format (less yday)
|
# Time from DS3231 in time.localtime() format (less yday)
|
||||||
result = YY, MM, DD, hh, mm, ss, wday - 1, 0
|
result = YY, MM, DD, hh, mm, ss, wday - 1, 0
|
||||||
if set_rtc:
|
|
||||||
if rtc is None: # Best we can do is to set local time
|
|
||||||
secs = time.mktime(result)
|
|
||||||
time.localtime(secs)
|
|
||||||
else:
|
|
||||||
rtc.datetime((YY, MM, DD, wday, hh, mm, ss, 0))
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# Output time or alarm data to device
|
# Output time or alarm data to device
|
||||||
|
|
Ładowanie…
Reference in New Issue