Skip to content

Commit

Permalink
Switch to using time.struct_time and split out the alarm register bec…
Browse files Browse the repository at this point in the history
…ause its complicated.
  • Loading branch information
tannewt committed Jan 9, 2017
1 parent a9226a3 commit 87c98f6
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 89 deletions.
165 changes: 165 additions & 0 deletions adafruit_register/i2c_bcd_alarm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# The MIT License (MIT)
#
# Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import time

def _bcd2bin(value):
"""Convert binary coded decimal to Binary
Arguments:
value - the BCD value to convert to binary (required, no default)
"""
return value - 6 * (value >> 4)


def _bin2bcd(value):
"""Convert a binary value to binary coded decimal.
Arguments:
value - the binary value to convert to BCD. (required, no default)
"""
return value + 6 * (value // 10)

ALARM_COMPONENT_DISABLED = 0x80
FREQUENCY = ["secondly", "minutely", "hourly", "daily", "weekly", "monthly"]

class BCDAlarmTimeRegister:
"""
Alarm date and time register using binary coded decimal structure.
The byte order of the registers must* be: [second], minute, hour, day,
weekday. Each byte must also have a high enable bit where 1 is disabled and
0 is enabled.
* If weekday_shared is True, then weekday and day share a register.
* If has_seconds is True, then there is a seconds register.
Values are a tuple of (`time.struct_time`, `str`) where the struct represents
a date and time that would alarm. The string is the frequency:
* "secondly", once a second (only if alarm has_seconds)
* "minutely", once a minute when seconds match (if alarm doesn't seconds then when seconds = 0)
* "hourly", once an hour when `tm_min` and `tm_sec` match
* "daily", once a day when `tm_hour`, `tm_min` and `tm_sec` match
* "weekly", once a week when `tm_wday`, `tm_hour`, `tm_min`, `tm_sec` match
* "monthly", once a month when `tm_mday`, `tm_hour`, `tm_min`, `tm_sec` match
:param int register_address: The register address to start the read
:param bool has_seconds: True if the alarm can happen minutely.
:param bool weekday_shared: True if weekday and day share the same register
:param int weekday_start: 0 or 1 depending on the RTC's representation of the first day of the week (Monday)
"""
# Defaults are based on alarm1 of the DS3231.
def __init__(self, register_address, has_seconds=True, weekday_shared=True, weekday_start=1):
buffer_size = 5
if weekday_shared:
buffer_size -= 1
if has_seconds:
buffer_size += 1
self.has_seconds = has_seconds
self.buffer = bytearray(buffer_size)
self.buffer[0] = register_address
self.weekday_shared = weekday_shared
self.weekday_start = weekday_start

def __get__(self, obj, objtype=None):
# Read the alarm register.
with obj.i2c_device:
obj.i2c_device.writeto(self.buffer, end=1, stop=False)
obj.i2c_device.readfrom_into(self.buffer, start=1)

frequency = None
i = 1
seconds = 0
if self.has_seconds:
if (self.buffer[1] & 0x80) != 0:
frequency = "secondly"
else:
frequency = "minutely"
seconds = _bcd2bin(self.buffer[1] & 0x7f)
i = 2
minute = 0
if (self.buffer[i] & 0x80) == 0:
frequency = "hourly"
minute = _bcd2bin(self.buffer[i] & 0x7f)

hour = 0
if (self.buffer[i + 1] & 0x80) == 0:
frequency = "daily"
hour = _bcd2bin(self.buffer[i + 1] & 0x7f)

mday = None
wday = None
if (self.buffer[i + 2] & 0x80) == 0:
# day of the month
if not self.weekday_shared or (self.buffer[i + 2] & 0x40) == 0:
frequency = "monthly"
mday = _bcd2bin(self.buffer[i + 2] & 0x3f)
else: # weekday
frequency = "weekly"
wday = _bcd2bin(self.buffer[i + 2] & 0x3f) - self.weekday_start

# weekday
if not self.weekday_shared and (self.buffer[i + 3] & 0x80) == 0:
frequency = "monthly"
mday = _bcd2bin(self.buffer[i + 3] & 0x7f)

if mday is not None:
wday = (mday - 2) % 7
elif wday is not None:
mday = wday + 2
else:
# Jan 1, 2017 was a Sunday (6)
wday = 6
mday = 1

return (time.struct_time((2017, 1, mday, hour, minute, seconds, wday, mday, -1)), frequency)

def __set__(self, obj, value):
# Turn all components off by default.
for i in range(len(self.buffer) - 1):
self.buffer[i + 1] = ALARM_COMPONENT_DISABLED

frequency = FREQUENCY.index(value[1])

# i is the index of the minute byte
i = 2 if self.has_seconds else 1

if frequency > 0 and self.has_seconds: # minutely at least
self.buffer[1] = _bin2bcd(value[0].tm_sec)

if frequency > 1: # hourly at least
self.buffer[i] = _bin2bcd(value[0].tm_min)

if frequency > 2: # daily at least
self.buffer[i + 1] = _bin2bcd(value[0].tm_hour)

if value[1] == "weekly":
if self.weekday_shared:
self.buffer[i + 2] = _bin2bcd(value[0].tm_wday + self.weekday_start) | 0x40
else:
self.buffer[i + 3] = _bin2bcd(value[0].tm_wday + self.weekday_start)
elif value[1] == "monthly":
self.buffer[i + 2] = _bin2bcd(value[0].tm_mday)

with obj.i2c_device:
obj.i2c_device.writeto(self.buffer)
97 changes: 9 additions & 88 deletions adafruit_register/i2c_bcd_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,15 @@ def __get__(self, obj, objtype=None):
with obj.i2c_device:
obj.i2c_device.writeto(self.buffer, end=1, stop=False)
obj.i2c_device.readfrom_into(self.buffer, start=1)
return datetime_tuple(
year=_bcd2bin(self.buffer[7]) + 2000,
month=_bcd2bin(self.buffer[6]),
day=_bcd2bin(self.buffer[5 - self.weekday_offset]),
weekday=_bcd2bin(self.buffer[4 + self.weekday_offset] - self.weekday_start),
hour=_bcd2bin(self.buffer[3]),
minute=_bcd2bin(self.buffer[2]),
second=_bcd2bin(self.buffer[1] & 0x7F),
)
return time.struct_time((_bcd2bin(self.buffer[7]) + 2000,
_bcd2bin(self.buffer[6]),
_bcd2bin(self.buffer[5 - self.weekday_offset]),
_bcd2bin(self.buffer[3]),
_bcd2bin(self.buffer[2]),
_bcd2bin(self.buffer[1] & 0x7F),
_bcd2bin(self.buffer[4 + self.weekday_offset] - self.weekday_start),
-1,
-1))

def __set__(self, obj, value):
self.buffer[1] = _bin2bcd(value.second) & 0x7F # format conversions
Expand All @@ -87,82 +87,3 @@ def __set__(self, obj, value):
self.buffer[7] = _bin2bcd(value.year - 2000)
with obj.i2c_device:
obj.i2c_device.writeto(self.buffer)

ALARM_COMPONENT_DISABLED = 0x80

FREQUENCY = ["secondly", "minutely", "hourly", "daily", "weekly", "monthly"]

class BCDAlarmTimeRegister:
"""
Alarm date and time register using binary coded decimal structure.
The byte order of the registers must* be: [second], minute, hour, day,
weekday. Each byte must also have a high enable bit where 1 is disabled and
0 is enabled.
* If weekday_shared is True, then weekday and day share a register.
* If has_seconds is True, then there is a seconds register.
Values are a tuple of (`time.struct_time`, `str`) where the struct represents
a date and time that would alarm. The string is the frequency:
* "secondly", once a second (only if alarm has_seconds)
* "minutely", once a minute when seconds match (if alarm doesn't seconds then when seconds = 0)
* "hourly", once an hour when `tm_min` and `tm_sec` match
* "daily", once a day when `tm_hour`, `tm_min` and `tm_sec` match
* "weekly", once a week when `tm_wday`, `tm_hour`, `tm_min`, `tm_sec` match
* "monthly", once a month when `tm_mday`, `tm_hour`, `tm_min`, `tm_sec` match
:param int register_address: The register address to start the read
:param bool has_seconds: True if the alarm can happen minutely.
:param bool weekday_shared: True if weekday and day share the same register
:param int weekday_start: 0 or 1 depending on the RTC's representation of the first day of the week (Monday)
"""
# Defaults are based on alarm1 of the DS3231.
def __init__(self, register_address, has_seconds=True, weekday_shared=True, weekday_start=1):
buffer_size = 5
if weekday_shared:
buffer_size -= 1
if has_seconds:
buffer_size += 1
self.has_seconds = has_seconds
self.buffer = bytearray(buffer_size)
self.buffer[0] = register_address
self.weekday_shared = weekday_shared
self.weekday_start = weekday_start

def __get__(self, obj, objtype=None):
# Read the alarm register.
with obj.i2c_device:
obj.i2c_device.writeto(self.buffer, end=1, stop=False)
obj.i2c_device.readfrom_into(self.buffer, start=1)
frequency = None

while (self.buffer[i] & 0x80) != 0:
return None
return datetime_tuple(
weekday=_bcd2bin(self.buffer[3] & 0x7f) - self.weekday_start,
day=_bcd2bin(self.buffer[4] & 0x7f),
hour=_bcd2bin(self.buffer[2] & 0x7f),
minute=_bcd2bin(self.buffer[1] & 0x7f),
)

def __set__(self, obj, value):
minute = ALARM_COMPONENT_DISABLED
if value.minute is not None:
minute = _bin2bcd(value.minute)
self.buffer[1] = minute

hour = ALARM_COMPONENT_DISABLED
if value.hour is not None:
hour = _bin2bcd(value.hour)
self.buffer[2] = hour

if not self.weekday_shared:
day = ALARM_COMPONENT_DISABLED
self.buffer[4] = (_bin2bcd(value.day) if value.day is not None else 0x80)

self.buffer[3 + self.weekday_offset] = (_bin2bcd(value.weekday + self.weekday_start) | 0b01000000 if value.weekday is not None else 0x80)

with obj.i2c_device:
obj.i2c_device.writeto(self.buffer)
8 changes: 7 additions & 1 deletion adafruit_register/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,18 @@ I2C
.. automodule:: adafruit_register.i2c_struct
:members:

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

.. automodule:: adafruit_register.i2c_bcd_datetime
:members:

`i2c_bcd_alarm` - Binary Coded Decimal alarm register
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

.. automodule:: adafruit_register.i2c_bcd_alarm
:members:

SPI
---
Coming soon!

0 comments on commit 87c98f6

Please sign in to comment.