Skip to content

Commit 87c98f6

Browse files
committed
Switch to using time.struct_time and split out the alarm register because its complicated.
1 parent a9226a3 commit 87c98f6

File tree

3 files changed

+181
-89
lines changed

3 files changed

+181
-89
lines changed

adafruit_register/i2c_bcd_alarm.py

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
23+
import time
24+
25+
def _bcd2bin(value):
26+
"""Convert binary coded decimal to Binary
27+
28+
Arguments:
29+
value - the BCD value to convert to binary (required, no default)
30+
"""
31+
return value - 6 * (value >> 4)
32+
33+
34+
def _bin2bcd(value):
35+
"""Convert a binary value to binary coded decimal.
36+
37+
Arguments:
38+
value - the binary value to convert to BCD. (required, no default)
39+
"""
40+
return value + 6 * (value // 10)
41+
42+
ALARM_COMPONENT_DISABLED = 0x80
43+
FREQUENCY = ["secondly", "minutely", "hourly", "daily", "weekly", "monthly"]
44+
45+
class BCDAlarmTimeRegister:
46+
"""
47+
Alarm date and time register using binary coded decimal structure.
48+
49+
The byte order of the registers must* be: [second], minute, hour, day,
50+
weekday. Each byte must also have a high enable bit where 1 is disabled and
51+
0 is enabled.
52+
53+
* If weekday_shared is True, then weekday and day share a register.
54+
* If has_seconds is True, then there is a seconds register.
55+
56+
Values are a tuple of (`time.struct_time`, `str`) where the struct represents
57+
a date and time that would alarm. The string is the frequency:
58+
59+
* "secondly", once a second (only if alarm has_seconds)
60+
* "minutely", once a minute when seconds match (if alarm doesn't seconds then when seconds = 0)
61+
* "hourly", once an hour when `tm_min` and `tm_sec` match
62+
* "daily", once a day when `tm_hour`, `tm_min` and `tm_sec` match
63+
* "weekly", once a week when `tm_wday`, `tm_hour`, `tm_min`, `tm_sec` match
64+
* "monthly", once a month when `tm_mday`, `tm_hour`, `tm_min`, `tm_sec` match
65+
66+
:param int register_address: The register address to start the read
67+
:param bool has_seconds: True if the alarm can happen minutely.
68+
:param bool weekday_shared: True if weekday and day share the same register
69+
:param int weekday_start: 0 or 1 depending on the RTC's representation of the first day of the week (Monday)
70+
"""
71+
# Defaults are based on alarm1 of the DS3231.
72+
def __init__(self, register_address, has_seconds=True, weekday_shared=True, weekday_start=1):
73+
buffer_size = 5
74+
if weekday_shared:
75+
buffer_size -= 1
76+
if has_seconds:
77+
buffer_size += 1
78+
self.has_seconds = has_seconds
79+
self.buffer = bytearray(buffer_size)
80+
self.buffer[0] = register_address
81+
self.weekday_shared = weekday_shared
82+
self.weekday_start = weekday_start
83+
84+
def __get__(self, obj, objtype=None):
85+
# Read the alarm register.
86+
with obj.i2c_device:
87+
obj.i2c_device.writeto(self.buffer, end=1, stop=False)
88+
obj.i2c_device.readfrom_into(self.buffer, start=1)
89+
90+
frequency = None
91+
i = 1
92+
seconds = 0
93+
if self.has_seconds:
94+
if (self.buffer[1] & 0x80) != 0:
95+
frequency = "secondly"
96+
else:
97+
frequency = "minutely"
98+
seconds = _bcd2bin(self.buffer[1] & 0x7f)
99+
i = 2
100+
minute = 0
101+
if (self.buffer[i] & 0x80) == 0:
102+
frequency = "hourly"
103+
minute = _bcd2bin(self.buffer[i] & 0x7f)
104+
105+
hour = 0
106+
if (self.buffer[i + 1] & 0x80) == 0:
107+
frequency = "daily"
108+
hour = _bcd2bin(self.buffer[i + 1] & 0x7f)
109+
110+
mday = None
111+
wday = None
112+
if (self.buffer[i + 2] & 0x80) == 0:
113+
# day of the month
114+
if not self.weekday_shared or (self.buffer[i + 2] & 0x40) == 0:
115+
frequency = "monthly"
116+
mday = _bcd2bin(self.buffer[i + 2] & 0x3f)
117+
else: # weekday
118+
frequency = "weekly"
119+
wday = _bcd2bin(self.buffer[i + 2] & 0x3f) - self.weekday_start
120+
121+
# weekday
122+
if not self.weekday_shared and (self.buffer[i + 3] & 0x80) == 0:
123+
frequency = "monthly"
124+
mday = _bcd2bin(self.buffer[i + 3] & 0x7f)
125+
126+
if mday is not None:
127+
wday = (mday - 2) % 7
128+
elif wday is not None:
129+
mday = wday + 2
130+
else:
131+
# Jan 1, 2017 was a Sunday (6)
132+
wday = 6
133+
mday = 1
134+
135+
return (time.struct_time((2017, 1, mday, hour, minute, seconds, wday, mday, -1)), frequency)
136+
137+
def __set__(self, obj, value):
138+
# Turn all components off by default.
139+
for i in range(len(self.buffer) - 1):
140+
self.buffer[i + 1] = ALARM_COMPONENT_DISABLED
141+
142+
frequency = FREQUENCY.index(value[1])
143+
144+
# i is the index of the minute byte
145+
i = 2 if self.has_seconds else 1
146+
147+
if frequency > 0 and self.has_seconds: # minutely at least
148+
self.buffer[1] = _bin2bcd(value[0].tm_sec)
149+
150+
if frequency > 1: # hourly at least
151+
self.buffer[i] = _bin2bcd(value[0].tm_min)
152+
153+
if frequency > 2: # daily at least
154+
self.buffer[i + 1] = _bin2bcd(value[0].tm_hour)
155+
156+
if value[1] == "weekly":
157+
if self.weekday_shared:
158+
self.buffer[i + 2] = _bin2bcd(value[0].tm_wday + self.weekday_start) | 0x40
159+
else:
160+
self.buffer[i + 3] = _bin2bcd(value[0].tm_wday + self.weekday_start)
161+
elif value[1] == "monthly":
162+
self.buffer[i + 2] = _bin2bcd(value[0].tm_mday)
163+
164+
with obj.i2c_device:
165+
obj.i2c_device.writeto(self.buffer)

adafruit_register/i2c_bcd_datetime.py

Lines changed: 9 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,15 @@ def __get__(self, obj, objtype=None):
6767
with obj.i2c_device:
6868
obj.i2c_device.writeto(self.buffer, end=1, stop=False)
6969
obj.i2c_device.readfrom_into(self.buffer, start=1)
70-
return datetime_tuple(
71-
year=_bcd2bin(self.buffer[7]) + 2000,
72-
month=_bcd2bin(self.buffer[6]),
73-
day=_bcd2bin(self.buffer[5 - self.weekday_offset]),
74-
weekday=_bcd2bin(self.buffer[4 + self.weekday_offset] - self.weekday_start),
75-
hour=_bcd2bin(self.buffer[3]),
76-
minute=_bcd2bin(self.buffer[2]),
77-
second=_bcd2bin(self.buffer[1] & 0x7F),
78-
)
70+
return time.struct_time((_bcd2bin(self.buffer[7]) + 2000,
71+
_bcd2bin(self.buffer[6]),
72+
_bcd2bin(self.buffer[5 - self.weekday_offset]),
73+
_bcd2bin(self.buffer[3]),
74+
_bcd2bin(self.buffer[2]),
75+
_bcd2bin(self.buffer[1] & 0x7F),
76+
_bcd2bin(self.buffer[4 + self.weekday_offset] - self.weekday_start),
77+
-1,
78+
-1))
7979

8080
def __set__(self, obj, value):
8181
self.buffer[1] = _bin2bcd(value.second) & 0x7F # format conversions
@@ -87,82 +87,3 @@ def __set__(self, obj, value):
8787
self.buffer[7] = _bin2bcd(value.year - 2000)
8888
with obj.i2c_device:
8989
obj.i2c_device.writeto(self.buffer)
90-
91-
ALARM_COMPONENT_DISABLED = 0x80
92-
93-
FREQUENCY = ["secondly", "minutely", "hourly", "daily", "weekly", "monthly"]
94-
95-
class BCDAlarmTimeRegister:
96-
"""
97-
Alarm date and time register using binary coded decimal structure.
98-
99-
The byte order of the registers must* be: [second], minute, hour, day,
100-
weekday. Each byte must also have a high enable bit where 1 is disabled and
101-
0 is enabled.
102-
103-
* If weekday_shared is True, then weekday and day share a register.
104-
* If has_seconds is True, then there is a seconds register.
105-
106-
Values are a tuple of (`time.struct_time`, `str`) where the struct represents
107-
a date and time that would alarm. The string is the frequency:
108-
109-
* "secondly", once a second (only if alarm has_seconds)
110-
* "minutely", once a minute when seconds match (if alarm doesn't seconds then when seconds = 0)
111-
* "hourly", once an hour when `tm_min` and `tm_sec` match
112-
* "daily", once a day when `tm_hour`, `tm_min` and `tm_sec` match
113-
* "weekly", once a week when `tm_wday`, `tm_hour`, `tm_min`, `tm_sec` match
114-
* "monthly", once a month when `tm_mday`, `tm_hour`, `tm_min`, `tm_sec` match
115-
116-
:param int register_address: The register address to start the read
117-
:param bool has_seconds: True if the alarm can happen minutely.
118-
:param bool weekday_shared: True if weekday and day share the same register
119-
:param int weekday_start: 0 or 1 depending on the RTC's representation of the first day of the week (Monday)
120-
"""
121-
# Defaults are based on alarm1 of the DS3231.
122-
def __init__(self, register_address, has_seconds=True, weekday_shared=True, weekday_start=1):
123-
buffer_size = 5
124-
if weekday_shared:
125-
buffer_size -= 1
126-
if has_seconds:
127-
buffer_size += 1
128-
self.has_seconds = has_seconds
129-
self.buffer = bytearray(buffer_size)
130-
self.buffer[0] = register_address
131-
self.weekday_shared = weekday_shared
132-
self.weekday_start = weekday_start
133-
134-
def __get__(self, obj, objtype=None):
135-
# Read the alarm register.
136-
with obj.i2c_device:
137-
obj.i2c_device.writeto(self.buffer, end=1, stop=False)
138-
obj.i2c_device.readfrom_into(self.buffer, start=1)
139-
frequency = None
140-
141-
while (self.buffer[i] & 0x80) != 0:
142-
return None
143-
return datetime_tuple(
144-
weekday=_bcd2bin(self.buffer[3] & 0x7f) - self.weekday_start,
145-
day=_bcd2bin(self.buffer[4] & 0x7f),
146-
hour=_bcd2bin(self.buffer[2] & 0x7f),
147-
minute=_bcd2bin(self.buffer[1] & 0x7f),
148-
)
149-
150-
def __set__(self, obj, value):
151-
minute = ALARM_COMPONENT_DISABLED
152-
if value.minute is not None:
153-
minute = _bin2bcd(value.minute)
154-
self.buffer[1] = minute
155-
156-
hour = ALARM_COMPONENT_DISABLED
157-
if value.hour is not None:
158-
hour = _bin2bcd(value.hour)
159-
self.buffer[2] = hour
160-
161-
if not self.weekday_shared:
162-
day = ALARM_COMPONENT_DISABLED
163-
self.buffer[4] = (_bin2bcd(value.day) if value.day is not None else 0x80)
164-
165-
self.buffer[3 + self.weekday_offset] = (_bin2bcd(value.weekday + self.weekday_start) | 0b01000000 if value.weekday is not None else 0x80)
166-
167-
with obj.i2c_device:
168-
obj.i2c_device.writeto(self.buffer)

adafruit_register/index.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,18 @@ I2C
2424
.. automodule:: adafruit_register.i2c_struct
2525
:members:
2626

27-
`i2c_bcd_datetime` - Binary Coded Decimal date and time registers
27+
`i2c_bcd_datetime` - Binary Coded Decimal date and time register
2828
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2929

3030
.. automodule:: adafruit_register.i2c_bcd_datetime
3131
:members:
3232

33+
`i2c_bcd_alarm` - Binary Coded Decimal alarm register
34+
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
35+
36+
.. automodule:: adafruit_register.i2c_bcd_alarm
37+
:members:
38+
3339
SPI
3440
---
3541
Coming soon!

0 commit comments

Comments
 (0)