diff options
Diffstat (limited to 'src/pico/adafruit_register/i2c_bits.py')
| -rwxr-xr-x | src/pico/adafruit_register/i2c_bits.py | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/src/pico/adafruit_register/i2c_bits.py b/src/pico/adafruit_register/i2c_bits.py new file mode 100755 index 0000000..9a9f1d2 --- /dev/null +++ b/src/pico/adafruit_register/i2c_bits.py @@ -0,0 +1,114 @@ +# SPDX-FileCopyrightText: 2016 Scott Shawcroft for Adafruit Industries +# +# SPDX-License-Identifier: MIT +# pylint: disable=too-few-public-methods + +""" +`adafruit_register.i2c_bits` +==================================================== + +Multi bit registers + +* Author(s): Scott Shawcroft +""" + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Register.git" + +try: + from typing import Optional, Type, NoReturn + from circuitpython_typing.device_drivers import I2CDeviceDriver +except ImportError: + pass + + +class RWBits: + """ + Multibit register (less than a full byte) that is readable and writeable. + This must be within a byte register. + + Values are `int` between 0 and 2 ** ``num_bits`` - 1. + + :param int num_bits: The number of bits in the field. + :param int register_address: The register address to read the bit from + :param int lowest_bit: The lowest bits index within the byte at ``register_address`` + :param int register_width: The number of bytes in the register. Defaults to 1. + :param bool lsb_first: Is the first byte we read from I2C the LSB? Defaults to true + :param bool signed: If True, the value is a "two's complement" signed value. + If False, it is unsigned. + """ + + def __init__( # pylint: disable=too-many-arguments + self, + num_bits: int, + register_address: int, + lowest_bit: int, + register_width: int = 1, + lsb_first: bool = True, + signed: bool = False, + ) -> None: + self.bit_mask = ((1 << num_bits) - 1) << lowest_bit + # print("bitmask: ",hex(self.bit_mask)) + if self.bit_mask >= 1 << (register_width * 8): + raise ValueError("Cannot have more bits than register size") + self.lowest_bit = lowest_bit + self.buffer = bytearray(1 + register_width) + self.buffer[0] = register_address + self.lsb_first = lsb_first + self.sign_bit = (1 << (num_bits - 1)) if signed else 0 + + def __get__( + self, + obj: Optional[I2CDeviceDriver], + objtype: Optional[Type[I2CDeviceDriver]] = None, + ) -> int: + with obj.i2c_device as i2c: + i2c.write_then_readinto(self.buffer, self.buffer, out_end=1, in_start=1) + # read the number of bytes into a single variable + reg = 0 + order = range(len(self.buffer) - 1, 0, -1) + if not self.lsb_first: + order = reversed(order) + for i in order: + reg = (reg << 8) | self.buffer[i] + reg = (reg & self.bit_mask) >> self.lowest_bit + # If the value is signed and negative, convert it + if reg & self.sign_bit: + reg -= 2 * self.sign_bit + return reg + + def __set__(self, obj: I2CDeviceDriver, value: int) -> None: + value <<= self.lowest_bit # shift the value over to the right spot + with obj.i2c_device as i2c: + i2c.write_then_readinto(self.buffer, self.buffer, out_end=1, in_start=1) + reg = 0 + order = range(len(self.buffer) - 1, 0, -1) + if not self.lsb_first: + order = range(1, len(self.buffer)) + for i in order: + reg = (reg << 8) | self.buffer[i] + # print("old reg: ", hex(reg)) + reg &= ~self.bit_mask # mask off the bits we're about to change + reg |= value # then or in our new value + # print("new reg: ", hex(reg)) + for i in reversed(order): + self.buffer[i] = reg & 0xFF + reg >>= 8 + i2c.write(self.buffer) + + +class ROBits(RWBits): + """ + Multibit register (less than a full byte) that is read-only. This must be + within a byte register. + + Values are `int` between 0 and 2 ** ``num_bits`` - 1. + + :param int num_bits: The number of bits in the field. + :param int register_address: The register address to read the bit from + :param type lowest_bit: The lowest bits index within the byte at ``register_address`` + :param int register_width: The number of bytes in the register. Defaults to 1. + """ + + def __set__(self, obj: I2CDeviceDriver, value: int) -> NoReturn: + raise AttributeError() |