aboutsummaryrefslogtreecommitdiff
path: root/src/pico/adafruit_register/i2c_bcd_alarm.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/pico/adafruit_register/i2c_bcd_alarm.py')
-rwxr-xr-xsrc/pico/adafruit_register/i2c_bcd_alarm.py202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/pico/adafruit_register/i2c_bcd_alarm.py b/src/pico/adafruit_register/i2c_bcd_alarm.py
new file mode 100755
index 0000000..74c8a77
--- /dev/null
+++ b/src/pico/adafruit_register/i2c_bcd_alarm.py
@@ -0,0 +1,202 @@
+# SPDX-FileCopyrightText: 2016 Scott Shawcroft for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+# pylint: disable=too-few-public-methods
+# pylint: disable=too-many-branches
+
+"""
+`adafruit_register.i2c_bcd_alarm`
+====================================================
+
+Binary Coded Decimal alarm register
+
+* Author(s): Scott Shawcroft
+"""
+
+__version__ = "0.0.0+auto.0"
+__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Register.git"
+
+import time
+
+try:
+ from typing import Optional, Type, Tuple
+ from typing_extensions import Literal
+ from circuitpython_typing.device_drivers import I2CDeviceDriver
+
+ FREQUENCY_T = Literal[
+ "monthly", "weekly", "daily", "hourly", "minutely", "secondly"
+ ]
+except ImportError:
+ pass
+
+
+def _bcd2bin(value: int) -> int:
+ """Convert binary coded decimal to Binary
+
+ :param value: the BCD value to convert to binary (required, no default)
+ """
+ return value - 6 * (value >> 4)
+
+
+def _bin2bcd(value: int) -> int:
+ """Convert a binary value to binary coded decimal.
+
+ :param 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: int,
+ has_seconds: bool = True,
+ weekday_shared: bool = True,
+ weekday_start: Literal[0, 1] = 1,
+ ) -> None:
+ 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: Optional[I2CDeviceDriver],
+ objtype: Optional[Type[I2CDeviceDriver]] = None,
+ ) -> Tuple[time.struct_time, FREQUENCY_T]:
+ # Read the alarm register.
+ with obj.i2c_device as i2c:
+ i2c.write_then_readinto(self.buffer, self.buffer, out_end=1, in_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
+ else:
+ frequency = "minutely"
+ seconds = _bcd2bin(self.buffer[i] & 0x7F)
+ 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: I2CDeviceDriver, value: Tuple[time.struct_time, FREQUENCY_T]
+ ) -> None:
+ if len(value) != 2:
+ raise ValueError("Value must be sequence of length two")
+ # Turn all components off by default.
+ for i in range(len(self.buffer) - 1):
+ self.buffer[i + 1] = ALARM_COMPONENT_DISABLED
+ frequency_name = value[1]
+ error_message = "%s is not a supported frequency" % frequency_name
+ if frequency_name not in FREQUENCY:
+ raise ValueError(error_message)
+
+ frequency = FREQUENCY.index(frequency_name)
+ if frequency < 1 and not self.has_seconds:
+ raise ValueError(error_message)
+
+ # 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.write(self.buffer)