micropython-lib/micropython/register/i2c_register.py

940 wiersze
30 KiB
Python
Czysty Zwykły widok Historia

"""
* Author(s): SquirtleSquadLeader
* License: MIT
* Purpose:
* The purpose of this module is to provide easy I2C Register
* access. It is inspired by the CircuitPython Register
* module maintained by Adafruit.
* RORegBit - Single bit Read Only
* RWRegBit - Single bit Read/Write
* RORegBits - Multi-bit Read Only
* RWRegBits - Multi-bit Read/Write
* ROReg - Single/Multi Read Only
* RWReg - Single/Multi Read/Write
* Notes:
1) Reference format strings below:
Format C Type Standard size
c char 1
b signed char 1
B unsigned char 1
h short 2
H unsigned short 2
i integer 4
I unsigned int 4
l long 4
L unsigned long 4
q long long 8
Q unsigned long long 8
f float 4
d double 8
2024-05-23 01:50:01 +00:00
"""
from machine import I2C
from struct import pack, unpack
2024-05-23 01:50:01 +00:00
class RORegBit:
2024-05-23 15:45:44 +00:00
def __init__(self, i2c, dev_addr, reg_addr, num_bytes, bit_location, endian="", fmt="B"):
"""
2024-05-23 01:50:01 +00:00
Creates an :class:`RORegBit` object which allows read only access to a single bit within a register.
:param i2c: I2C bus which connects the host system to the peripheral device
:type kind: machine.I2C()
2024-05-23 01:50:01 +00:00
:param dev_addr: I2C address of the device which
:type dev_addr: int()
:param reg_addr: Physical register address which contains the bit of interest
:type reg_addr: int()
:param num_bytes: Number of bytes to read
:type num_bytes: int()
:param bit_location: Location of bit within bitfield
:type bit_locatin: int()
:param endian: Endian-ness of system for which the code is intended to run on. str('') uses native Endian-ness.
:type endian: str()
2024-05-23 01:50:01 +00:00
:param fmt: Format code which is used to unpack data from bytes(). Defaults to 'B' which is good for a single 8-bit register
:type fmt: int()
:return: An initialized RORegBit object
:rtype: RORegBit()
2024-05-23 01:50:01 +00:00
**Quickstart: Importing and using the device**
2024-05-23 01:50:01 +00:00
Here is an example of using the :class:`RORegBit` class.
First you will need to import the following libraries:
.. code-block:: python
2024-05-23 01:50:01 +00:00
from machine import I2C
from register.register import RORegBit
Once this is done you must define a :class:`machine.I2C` object and then pass that
to the :class:`RORegBit` object to instantiate it.
2024-05-23 01:50:01 +00:00
.. code-block:: python
i2c = I2C(0) # I2C details are project specific
2024-05-23 01:50:01 +00:00
my_reg = RORegBit(i2c, 68, 5, 1, 5)
2024-05-23 01:50:01 +00:00
'my_reg' can now provide access to the :method:`__get__(). Using this method
2024-05-23 01:50:01 +00:00
will return the value of the bit found at :param bit_location:.
.. code-block:: python
2024-05-23 01:50:01 +00:00
value = my_reg.__get__() # 0 or 1
2024-05-23 01:50:01 +00:00
Alternatively, a :class:`RORegBit` object(s) can be placed within another class.
.. code-block:: python
2024-05-23 01:50:01 +00:00
# import
from machine import I2C
2024-05-23 01:50:01 +00:00
from register.register import RORegBit
# define I2C
i2c = I2C(0)
# create class with desired functionality
class FooDevice:
2024-05-23 01:50:01 +00:00
def __init__(self, i2c_bus):
self._my_reg_1 = RORegBit(i2c_bus, 68, 5, 1, 5)
self._my_reg_2 = RORegBit(i2c_bus, 68, 6, 1, 5)
2024-05-23 01:50:01 +00:00
def get_my_reg1(self):
return self._my_reg_1.__get__()
2024-05-23 01:50:01 +00:00
def get_my_reg2(self):
2024-05-23 01:50:01 +00:00
return self._my_reg_1.__get__()
# invoke class object
device = FooDevice(i2c)
"""
self._i2c = i2c
self._dev_addr = dev_addr
self._reg_addr = reg_addr
self._num_bytes = num_bytes
self._bit_location = bit_location
self._endian = endian
self._fmt = fmt
2024-05-23 01:50:01 +00:00
__check_reg(self)
2024-05-23 01:50:01 +00:00
del (i2c, dev_addr, reg_addr, num_bytes, bit_location, fmt)
def __get__(self):
"""
:return: Returns the value of the bit located at :param bit_location:
:rtype: int()
"""
return __getbit(self)
2024-05-23 01:50:01 +00:00
class RWRegBit:
2024-05-23 15:45:44 +00:00
def __init__(self, i2c, dev_addr, reg_addr, num_bytes, bit_location, endian="", fmt="B"):
"""
2024-05-23 01:50:01 +00:00
Creates an :class:`RORegBit` object which allows read and write access to a single bit within a register.
:param i2c: I2C bus which connects the host system to the peripheral device
:type kind: machine.I2C()
2024-05-23 01:50:01 +00:00
:param dev_addr: I2C address of the device which
:type dev_addr: int()
:param reg_addr: Physical register address which contains the bit of interest
:type reg_addr: int()
:param num_bytes: Number of bytes to read
:type num_bytes: int()
:param bit_location: Location of bit within bitfield
:type bit_locatin: int()
:param endian: Endian-ness of system for which the code is intended to run on. str('') uses native Endian-ness.
:type endian: str()
2024-05-23 01:50:01 +00:00
:param fmt: Format code which is used to unpack data from bytes(). Defaults to 'B' which is good for a single 8-bit register
:type fmt: int()
:return: An initialized RWRegBit object
:rtype: RWRegBit()
2024-05-23 01:50:01 +00:00
**Quickstart: Importing and using the device**
2024-05-23 01:50:01 +00:00
Here is an example of using the :class:`RWRegBit` class.
First you will need to import the following libraries:
.. code-block:: python
2024-05-23 01:50:01 +00:00
from machine import I2C
from register.register import RWRegBit
Once this is done you must define a :class:`machine.I2C` object and then pass that
to the :class:`RWRegBit` object to instantiate it.
2024-05-23 01:50:01 +00:00
.. code-block:: python
i2c = I2C(0) # I2C details are project specific
2024-05-23 01:50:01 +00:00
my_reg = RWRegBit(i2c, 68, 5, 1, 5)
2024-05-23 01:50:01 +00:00
'my_reg' can now provide access to the :method:`__get__() and :method:`__set__().
2024-05-23 01:50:01 +00:00
Using these methods will get/set the value of the bit found at :param bit_location:.
.. code-block:: python
2024-05-23 01:50:01 +00:00
my_reg.__set__(1)
print(my_reg.__get__()) # prints 1
2024-05-23 01:50:01 +00:00
Alternatively, a :class:`RWRegBit` object(s) can be placed within another class.
.. code-block:: python
2024-05-23 01:50:01 +00:00
# import
from machine import I2C
2024-05-23 01:50:01 +00:00
from register.register import RWRegBit
# define I2C
i2c = I2C(0)
# create class with desired functionality
class FooDevice:
2024-05-23 01:50:01 +00:00
def __init__(self, i2c_bus):
self._my_reg = RORegBit(i2c_bus, 68, 5, 1, 5)
def get_my_reg(self):
return self._my_reg.__get__()
2024-05-23 01:50:01 +00:00
def get_my_reg2(self, n):
return self._my_reg.__set__(n)
2024-05-23 01:50:01 +00:00
# invoke class object
device = FooDevice(i2c)
"""
self._i2c = i2c
self._dev_addr = dev_addr
self._reg_addr = reg_addr
self._num_bytes = num_bytes
self._bit_location = bit_location
self._endian = endian
self._fmt = fmt
2024-05-23 01:50:01 +00:00
__check_reg(self)
2024-05-23 01:50:01 +00:00
2024-05-23 15:45:44 +00:00
self._premask, self._postmask = __calc_mask(bit_location, bit_location, num_bytes)
2024-05-23 01:50:01 +00:00
del (i2c, dev_addr, reg_addr, num_bytes, bit_location, endian, fmt)
def __get__(self):
"""
:return: Returns the value of the bit located at :param bit_location:
:rtype: int()
"""
return __getbit(self)
2024-05-23 01:50:01 +00:00
def __set__(self, setting):
"""
2024-05-23 01:50:01 +00:00
:return: Returns 'True' if operation successful
:rtype: bool()
"""
return __setbit(self, setting)
2024-05-23 01:50:01 +00:00
class RORegBits:
2024-05-23 15:45:44 +00:00
def __init__(self, i2c, dev_addr, reg_addr, num_bytes, lsb, msb, endian="", fmt="B"):
"""
2024-05-23 01:50:01 +00:00
Creates an :class:`RORegBits` object which allows read only access to a sequential set of bits within a bitfield.
:param i2c: I2C bus which connects the host system to the peripheral device
:type kind: machine.I2C()
2024-05-23 01:50:01 +00:00
:param dev_addr: I2C address of the device which
:type dev_addr: int()
:param reg_addr: Physical register address which contains the bit of interest
:type reg_addr: int()
:param num_bytes: Number of bytes to read
:type num_bytes: int()
:param lsb: Location of least significant bit within bitfield
:type lsb: int()
2024-05-23 01:50:01 +00:00
:param msb: Location of most significant bit within bitfield
:type msb: int()
2024-05-23 01:50:01 +00:00
:param endian: Endian-ness of system for which the code is intended to run on. str('') uses native Endian-ness.
:type endian: str()
2024-05-23 01:50:01 +00:00
:param fmt: Format code which is used to unpack data from bytes(). Defaults to 'B' which is good for a single 8-bit register
:type fmt: int()
:return: An initialized RORegBits object
:rtype: RORegBits()
2024-05-23 01:50:01 +00:00
**Quickstart: Importing and using the device**
2024-05-23 01:50:01 +00:00
Here is an example of using the :class:`RORegBits` class.
First you will need to import the following libraries:
.. code-block:: python
2024-05-23 01:50:01 +00:00
from machine import I2C
from register.register import RORegBits
Once this is done you must define a :class:`machine.I2C` object and then pass that
to the :class:`RORegBits` object to instantiate it.
2024-05-23 01:50:01 +00:00
.. code-block:: python
i2c = I2C(0) # I2C details are project specific
2024-05-23 01:50:01 +00:00
my_reg = RORegBits(i2c_bus, 68, 5, 1, 0, 2)
2024-05-23 01:50:01 +00:00
'my_reg' can now provide access to the :method:`__get__(). Using this method
2024-05-23 01:50:01 +00:00
will return the value of the bit found at :param bit_location:.
.. code-block:: python
2024-05-23 01:50:01 +00:00
value = my_reg.__get__() # Returns some value from 0b000 to 0b111
2024-05-23 01:50:01 +00:00
Alternatively, a :class:`RORegBits` object(s) can be placed within another class.
.. code-block:: python
2024-05-23 01:50:01 +00:00
# import
from machine import I2C
2024-05-23 01:50:01 +00:00
from register.register import RORegBits
# define I2C
i2c = I2C(0)
# create class with desired functionality
class FooDevice:
2024-05-23 01:50:01 +00:00
def __init__(self, i2c_bus):
self._my_reg_1 = RORegBits(i2c_bus, 68, 5, 1, 0, 2)
self._my_reg_2 = RORegBits(i2c_bus, 68, 6, 1, 3, 6)
2024-05-23 01:50:01 +00:00
def get_my_reg1(self):
return self._my_reg_1.__get__()
2024-05-23 01:50:01 +00:00
@property
def my_reg2(self):
return self._my_reg_2.__get__()
2024-05-23 01:50:01 +00:00
# invoke class object
device = FooDevice(i2c)
2024-05-23 01:50:01 +00:00
n1 = device.get_my_reg()
n2 = device.my_reg2
"""
self._i2c = i2c
self._dev_addr = dev_addr
self._reg_addr = reg_addr
self._num_bytes = num_bytes
self._endian = endian
self._fmt = fmt
2024-05-23 01:50:01 +00:00
__check_reg(self)
2024-05-23 01:50:01 +00:00
self._premask, self._mask, self._postmask = __calc_mask(lsb, msb, num_bytes)
del (i2c, dev_addr, reg_addr, num_bytes, lsb, msb, endian, fmt)
def __get__(self):
"""
:return: Returns the value of the bitfield located between :param lsb: and :param msb:
:rtype: int()
"""
return __getbits(self)
2024-05-23 01:50:01 +00:00
class RWRegBits:
2024-05-23 15:45:44 +00:00
def __init__(self, i2c, dev_addr, reg_addr, num_bytes, lsb, msb, endian="", fmt="B"):
"""
2024-05-23 01:50:01 +00:00
Creates an :class:`RWRegBits` object which allows read and write access to a sequential set of bits within a bitfield.
:param i2c: I2C bus which connects the host system to the peripheral device
:type kind: machine.I2C()
2024-05-23 01:50:01 +00:00
:param dev_addr: I2C address of the device which
:type dev_addr: int()
:param reg_addr: Physical register address which contains the bit of interest
:type reg_addr: int()
:param num_bytes: Number of bytes to read
:type num_bytes: int()
:param lsb: Location of least significant bit within bitfield
:type lsb: int()
2024-05-23 01:50:01 +00:00
:param msb: Location of most significant bit within bitfield
:type msb: int()
2024-05-23 01:50:01 +00:00
:param endian: Endian-ness of system for which the code is intended to run on. str('') uses native Endian-ness.
:type endian: str()
2024-05-23 01:50:01 +00:00
:param fmt: Format code which is used to unpack data from bytes(). Defaults to 'B' which is good for a single 8-bit register
:type fmt: int()
:return: An initialized RWRegBits object
:rtype: RWRegBits()
2024-05-23 01:50:01 +00:00
**Quickstart: Importing and using the device**
2024-05-23 01:50:01 +00:00
Here is an example of using the :class:`RWRegBits` class.
First you will need to import the following libraries:
.. code-block:: python
2024-05-23 01:50:01 +00:00
from machine import I2C
from register.register import RWRegBits
Once this is done you must define a :class:`machine.I2C` object and then pass that
to the :class:`RWRegBits` object to instantiate it.
2024-05-23 01:50:01 +00:00
.. code-block:: python
i2c = I2C(0) # I2C details are project specific
2024-05-23 01:50:01 +00:00
my_reg = RWRegBits(i2c_bus, 68, 5, 1, 0, 2)
2024-05-23 01:50:01 +00:00
'my_reg' can now provide access to :method:`__get__() and :method:`__set__().
.. code-block:: python
2024-05-23 01:50:01 +00:00
my_reg.__set__(0b110) # Returns some value from 0b000 to 0b111
value = my_reg.__get__() # Returns 0b110, assuming nothing changes
2024-05-23 01:50:01 +00:00
Alternatively, a :class:`RWRegBits` object(s) can be placed within another class.
.. code-block:: python
2024-05-23 01:50:01 +00:00
# import
from machine import I2C
2024-05-23 01:50:01 +00:00
from register.register import RWRegBits
# define I2C
i2c = I2C(0)
# create class with desired functionality
class FooDevice:
2024-05-23 01:50:01 +00:00
def __init__(self, i2c_bus):
self._my_reg_1 = RWRegBits(i2c_bus, 68, 5, 1, 0, 2)
self._my_reg_2 = RWRegBits(i2c_bus, 68, 6, 1, 3, 6)
2024-05-23 01:50:01 +00:00
def get_my_reg1(self):
return self._my_reg_1.__get__()
2024-05-23 01:50:01 +00:00
def set_my_reg1(self, n):
return self._my_reg_1.__set__(n)
2024-05-23 01:50:01 +00:00
@property
def my_reg2(self):
return self._my_reg_2.__get__()
2024-05-23 01:50:01 +00:00
@my_reg2.setter
def my_reg2(self, n):
return self._my_reg_2.__set__(n)
2024-05-23 01:50:01 +00:00
# invoke class object
device = FooDevice(i2c)
2024-05-23 01:50:01 +00:00
device.set_my_reg(0b110)
print(device.get_my_reg()) # prints 6
2024-05-23 01:50:01 +00:00
device.my_reg2 = 0b110
print(device.my_reg2) # prints 6
"""
self._i2c = i2c
self._dev_addr = dev_addr
self._reg_addr = reg_addr
self._num_bytes = num_bytes
self._endian = endian
self._fmt = fmt
2024-05-23 01:50:01 +00:00
__check_reg(self)
2024-05-23 01:50:01 +00:00
self._premask, self._mask, self._postmask = __calc_mask(lsb, msb, num_bytes)
del (i2c, dev_addr, reg_addr, num_bytes, lsb, msb, fmt, endian)
def __get__(self):
"""
:return: Returns the value of the bitfield located between :param lsb: and :param msb:
:rtype: int()
"""
return __getbits(self)
2024-05-23 01:50:01 +00:00
def __set__(self, setting):
"""
:return: True if successful
:rtype: bool()
"""
2024-05-23 01:50:01 +00:00
return __setbits(self, setting)
class ROReg:
2024-05-23 01:50:01 +00:00
def __init__(self, i2c, dev_addr, reg_addr, num_bytes=1, endian="", fmt="B"):
"""
Creates a :class:`ROReg` object which allows read only access to n number of sequential registers,
2024-05-23 01:50:01 +00:00
where n is specified by :param num_bytes:.
:param i2c: I2C bus which connects the host system to the peripheral device
:type kind: machine.I2C()
2024-05-23 01:50:01 +00:00
:param dev_addr: I2C address of the device which
:type dev_addr: int()
:param reg_addr: Physical register address which contains the bit of interest
:type reg_addr: int()
:param num_bytes: Number of bytes to read. Defaults to 1.
2024-05-23 01:50:01 +00:00
:type num_bytes: int()
:param fmt: Format code which is used to unpack data from bytes(). Defaults to 'B'.
:type fmt: int()
:return: An initialized ROReg object
:rtype: ROReg()
2024-05-23 01:50:01 +00:00
**Quickstart: Importing and using the device**
2024-05-23 01:50:01 +00:00
Here is an example of using the :class:`ROReg` class.
First you will need to import the following libraries:
.. code-block:: python
2024-05-23 01:50:01 +00:00
from machine import I2C
from register.register import ROReg
Once this is done you must define a :class:`machine.I2C` object and then pass that
to the :class:`ROReg` object to instantiate it.
2024-05-23 01:50:01 +00:00
.. code-block:: python
i2c = I2C(0) # I2C details are project specific
2024-05-23 01:50:01 +00:00
my_reg = ROReg(i2c, 68, 5)
2024-05-23 01:50:01 +00:00
'my_reg' can now provide access to the :method:`__get__(). Using this method
2024-05-23 01:50:01 +00:00
will return the value of the bit found at :param bit_location:.
.. code-block:: python
2024-05-23 01:50:01 +00:00
value = my_reg.__get__() # some value between 0b0 and 0b1111_1111
2024-05-23 01:50:01 +00:00
Alternatively, a :class:`ROReg` object(s) can be placed within another class.
.. code-block:: python
2024-05-23 01:50:01 +00:00
# import
from machine import I2C
2024-05-23 01:50:01 +00:00
from register.register import ROReg
# define I2C
i2c = I2C(0)
# create class with desired functionality
class FooDevice:
2024-05-23 01:50:01 +00:00
def __init__(self, i2c_bus):
self._my_reg_1 = ROReg(i2c_bus, 68, 5)
self._my_reg_2 = ROReg(i2c_bus, 68, 6)
2024-05-23 01:50:01 +00:00
def get_my_reg1(self):
return self._my_reg_1.__get__()
2024-05-23 01:50:01 +00:00
@property
def my_reg2(self):
2024-05-23 01:50:01 +00:00
return self._my_reg_1.__get__()
# invoke class object
device = FooDevice(i2c)
2024-05-23 01:50:01 +00:00
print(device.get_my_reg1())
print(device.my_reg2)
"""
self._i2c = i2c
self._dev_addr = dev_addr
self._reg_addr = reg_addr
self._num_bytes = num_bytes
self._fmt = fmt
self._endian = endian
2024-05-23 01:50:01 +00:00
__check_reg(self)
2024-05-23 01:50:01 +00:00
del (i2c, dev_addr, reg_addr, num_bytes, fmt, endian)
def __get__(self):
"""
:return: Returns tuple containing n number of elements, where n is the number of characters in :param fmt:
:rtype: tuple()
"""
return __getreg(self)
2024-05-23 01:50:01 +00:00
class RWReg:
2024-05-23 01:50:01 +00:00
def __init__(self, i2c, dev_addr, reg_addr, num_bytes, endian="", fmt="B"):
"""
Creates a :class:`RWReg` object which allows read and write access to n number of sequential registers,
2024-05-23 01:50:01 +00:00
where n is specified by :param num_bytes:.
:param i2c: I2C bus which connects the host system to the peripheral device
:type kind: machine.I2C()
2024-05-23 01:50:01 +00:00
:param dev_addr: I2C address of the device which
:type dev_addr: int()
:param reg_addr: Physical register address which contains the bit of interest
:type reg_addr: int()
:param num_bytes: Number of bytes to read. Defaults to 1.
2024-05-23 01:50:01 +00:00
:type num_bytes: int()
:param fmt: Format code which is used to unpack data from bytes(). Defaults to 'B'.
:type fmt: int()
:return: An initialized RWReg object
:rtype: RWReg()
2024-05-23 01:50:01 +00:00
**Quickstart: Importing and using the device**
2024-05-23 01:50:01 +00:00
Here is an example of using the :class:`RWReg` class.
First you will need to import the following libraries:
.. code-block:: python
2024-05-23 01:50:01 +00:00
from machine import I2C
from register.register import RWReg
Once this is done you must define a :class:`machine.I2C` object and then pass that
to the :class:`RWReg` object to instantiate it.
2024-05-23 01:50:01 +00:00
.. code-block:: python
i2c = I2C(0) # I2C details are project specific
2024-05-23 01:50:01 +00:00
my_reg = RWReg(i2c, 68, 5)
2024-05-23 01:50:01 +00:00
'my_reg' can now provide access to the :method:`__get__() and __set__().
.. code-block:: python
my_reg.__set__(0b0)
value = my_reg.__get__() # 0b0 if nothing changed
2024-05-23 01:50:01 +00:00
Alternatively, a :class:`RWReg` object(s) can be placed within another class.
.. code-block:: python
2024-05-23 01:50:01 +00:00
# import
from machine import I2C
2024-05-23 01:50:01 +00:00
from register.register import RWReg
# define I2C
i2c = I2C(0)
# create class with desired functionality
class FooDevice:
2024-05-23 01:50:01 +00:00
def __init__(self, i2c_bus):
self._my_reg_1 = RWReg(i2c_bus, 68, 5)
self._my_reg_2 = RWReg(i2c_bus, 68, 6)
2024-05-23 01:50:01 +00:00
def get_my_reg1(self):
return self._my_reg_1.__get__()
2024-05-23 01:50:01 +00:00
def set_my_reg1(self, n):
return self._my_reg_1.__set__(n)
2024-05-23 01:50:01 +00:00
@property
def my_reg2(self):
return self._my_reg_1.__get__()
2024-05-23 01:50:01 +00:00
@my_reg2.setter
def my_reg2(self, n):
return self._my_reg_1.__set__(n)
2024-05-23 01:50:01 +00:00
# invoke class object
device = FooDevice(i2c)
2024-05-23 01:50:01 +00:00
device.set_my_reg1(0b110)
print(device.get_my_reg1()) # prints 6, assuming nothing changed
2024-05-23 01:50:01 +00:00
device.my_reg2 = 0b1111_0000
print(device.my_reg2) # prints 240
"""
self._i2c = i2c
self._dev_addr = dev_addr
self._reg_addr = reg_addr
self._num_bytes = num_bytes
self._fmt = fmt
self._endian = endian
2024-05-23 01:50:01 +00:00
__check_reg(self)
2024-05-23 01:50:01 +00:00
del (i2c, dev_addr, reg_addr, num_bytes, fmt, endian)
def __get__(self):
"""
:return: Returns tuple containing n number of elements, where n is the number of characters in :param fmt:
:rtype: tuple()
"""
return __getreg(self)
2024-05-23 01:50:01 +00:00
def __set__(self, setting):
"""
:param setting: Value(s) to be written to register(s). Order must match :param fmt:.
2024-05-23 01:50:01 +00:00
:type setting: int(), bytes(), bytearray(), or list/tuple containing those values in order
:return: Returns True if operation successful
:rtype: tuple()
"""
return __setreg(self, setting)
2024-05-23 01:50:01 +00:00
"""
*
* GLOBAL HELPER FUNCTIONS
*
*
"""
2024-05-23 01:50:01 +00:00
def __getbit(reg_object):
if isinstance(reg_object, (RORegBit, RWRegBit)):
# Retrieve register value and unpack to int
2024-05-23 01:50:01 +00:00
value = reg_object._i2c.readfrom_mem(
reg_object._dev_addr, reg_object._reg_addr, reg_object._num_bytes
)
# Unpack byte
2024-05-23 01:50:01 +00:00
value = unpack(reg_object._endian + reg_object._fmt, value)[0]
# Perform shift followed by _AND_ operation to determine bit state
return (value >> reg_object._bit_location) & 0b1
else:
2024-05-23 01:50:01 +00:00
raise TypeError("incorrect object type - must be RORegBit, RWRegBit")
def __setbit(reg_object, setting):
if isinstance(reg_object, RWRegBit):
2024-05-23 01:50:01 +00:00
if setting in (0, 1):
# Retrieve register value and unpack to int
2024-05-23 01:50:01 +00:00
value = reg_object._i2c.readfrom_mem(
reg_object._dev_addr, reg_object._reg_addr, reg_object._num_bytes
)
# Unpack byte
2024-05-23 01:50:01 +00:00
value = unpack(reg_object._endian + reg_object._fmt, value)[0]
# Assemble byte
2024-05-23 01:50:01 +00:00
value = (
(value & reg_object._postmask)
+ (setting << reg_object._bit_location)
+ (value & reg_object._premask)
)
# Pack to bytes
2024-05-23 01:50:01 +00:00
value = pack(reg_object._endian + reg_object._fmt, value)
# Write to I2C
2024-05-23 15:45:44 +00:00
reg_object._i2c.writeto_mem(reg_object._dev_addr, reg_object._reg_addr, value)
2024-05-23 01:50:01 +00:00
# Return True for success
return True
2024-05-23 01:50:01 +00:00
else:
2024-05-23 01:50:01 +00:00
raise ValueError("setting must be int(0) or int(1)")
else:
raise TypeError("incorrect object type - must be RWRegBit")
2024-05-23 01:50:01 +00:00
def __getbits(reg_object):
2024-05-23 01:50:01 +00:00
if isinstance(reg_object, (RORegBits, RWRegBits)):
# Retrieve register value and unpack to int
2024-05-23 01:50:01 +00:00
value = reg_object._i2c.readfrom_mem(
reg_object._dev_addr, reg_object._reg_addr, reg_object._num_bytes
)
# Unpack bytes
2024-05-23 01:50:01 +00:00
value = unpack(reg_object._endian + reg_object._fmt, value)[0]
# Return value of bit field
2024-05-23 01:50:01 +00:00
return (value & reg_object._mask) >> reg_object._lsb
else:
2024-05-23 01:50:01 +00:00
raise TypeError("incorrect object type - must be RORegBits, RWRegBits")
def __setbits(reg_object, setting):
2024-05-23 01:50:01 +00:00
if isinstance(reg_object, RWRegBits):
if isinstance(setting, int) and setting <= reg_object._mask:
# Retrieve register value and unpack to int
2024-05-23 01:50:01 +00:00
value = reg_object._i2c.readfrom_mem(
reg_object._dev_addr, reg_object._reg_addr, reg_object._num_bytes
)
# Unpack bytes
2024-05-23 01:50:01 +00:00
value = unpack(reg_object._endian + reg_object._fmt, value)[0]
# Assemble
value = (
(value & reg_object._postmask)
+ (setting << reg_object._lsb)
+ (value & reg_object._premask)
)
# Pack to bytes object
2024-05-23 01:50:01 +00:00
value = struct.pack(reg_object._endian + reg_object._fmt, value)
# Write to device
2024-05-23 15:45:44 +00:00
reg_object._i2c.writeto_mem(reg_object._dev_addr, reg_object._reg_addr, value)
2024-05-23 01:50:01 +00:00
return True
2024-05-23 01:50:01 +00:00
else:
2024-05-23 15:45:44 +00:00
raise ValueError(f"value of setting exceeds max value of bitfield: {reg_object._mask}")
else:
raise TypeError("incorrect object type - must be RWRegBits")
2024-05-23 01:50:01 +00:00
def __getreg(reg_object):
if isinstance(reg_object, (ROReg, RWReg)):
# Retrieve register value and unpack to int
2024-05-23 01:50:01 +00:00
values = reg_object._i2c.readfrom_mem(
reg_object._dev_addr, reg_object._reg_addr, reg_object._num_bytes
)
# Return Tuple of values
2024-05-23 01:50:01 +00:00
return unpack(reg_object._endian + reg_object._fmt, values)
else:
2024-05-23 01:50:01 +00:00
raise TypeError("incorrect object type - must be ROReg, RWReg")
def __setreg(reg_object, settings):
if isinstance(reg_object, RWReg):
if isinstance(settings, (bytes, bytearray)):
2024-05-23 01:50:01 +00:00
# Write to device
2024-05-23 15:45:44 +00:00
reg_object._i2c.writeto_mem(reg_object._dev_addr, reg_object._reg_addr, settings)
2024-05-23 01:50:01 +00:00
elif isinstance(settings, (tuple, list)):
# Where our data will go
d = bytearray()
2024-05-23 01:50:01 +00:00
# Pack and append to d
for n in range(0, len(settings)):
2024-05-23 01:50:01 +00:00
d.extend(pack(reg_object._endian + reg_object._fmt[n], settings[n]))
# Write to device
2024-05-23 01:50:01 +00:00
reg_object._i2c.writeto_mem(reg_object._dev_addr, reg_object._reg_addr, d)
# Assumed single int() for single reg-op
elif isinstance(settings, int):
2024-05-23 01:50:01 +00:00
d = pack(reg_object._endian + reg_object._fmt, settings)
reg_object._i2c.writeto_mem(reg_object._dev_addr, reg_object._reg_addr, d)
else:
2024-05-23 01:50:01 +00:00
raise TypeError(
"unsupported object type, settings must be int(), bytes(), bytearray(), tuple(), or list()"
)
else:
2024-05-23 01:50:01 +00:00
raise TypeError("incorrect object type - must be ROReg, RWReg")
def __calc_mask(lsb, msb, numbytes):
"""
Takes in full description of bitfield that needs masking
2024-05-23 01:50:01 +00:00
returns ints() pre, mask, post
2024-05-23 01:50:01 +00:00
"""
# Check input types
if lsb.__class__() == int() and lsb >= 0:
if msb.__class__() == int() and msb >= 0:
2024-05-23 01:50:01 +00:00
if numbytes.__class__() == int() and numbytes >= 0:
# Check for detectable errors
2024-05-23 01:50:01 +00:00
if msb >= lsb:
# Single bit mask
if msb == lsb:
pre, post = 0b0, 0b0
2024-05-23 01:50:01 +00:00
# Calc post masking
2024-05-23 01:50:01 +00:00
for bit in range(msb + 1, numbytes * 8):
post = (post << 1) + 0b1
# Calc pre masking
for bit in range(0, lsb):
2024-05-23 01:50:01 +00:00
pre = (pre << 1) + 0b1
return pre, post
2024-05-23 01:50:01 +00:00
# Multibit mask
else:
# Values to return
pre, mask, post = 0b0, 0b0, 0b0
2024-05-23 01:50:01 +00:00
# Calc post masking
2024-05-23 01:50:01 +00:00
for bit in range(msb + 1, numbytes * 8):
post = (post << 1) + 0b1
# Calc bitfield masking
2024-05-23 01:50:01 +00:00
for bit in range(lsb, msb + 1):
mask = (mask << 1) + 0b1
# No bits lower than 0
if lsb == 0:
return 0b0, mask, post
2024-05-23 01:50:01 +00:00
else:
for bit in range(0, lsb):
2024-05-23 01:50:01 +00:00
pre = (pre << 1) + 0b1
return pre, mask, post
else:
raise ValueError("msb must be greater than or equal to lsb")
else:
raise ValueError("numbytes must be of type int() and 0 or greater")
else:
raise ValueError("msb must be of type int() and 0 or greater")
else:
raise ValueError("lsb must be of type int() and 0 or greater")
2024-05-23 01:50:01 +00:00
def __check_reg(reg_object):
# Alowable struct.pack/unpack formats to check for
2024-05-23 01:50:01 +00:00
fmts = {
"b": 1,
"B": 1,
"h": 2,
"H": 2,
"f": 4,
"i": 4,
"I": 4,
"l": 4,
"L": 4,
"q": 8,
"Q": 8,
}
endians = "@><"
byte_count = 0
2024-05-23 01:50:01 +00:00
# Take in only register objects
if isinstance(reg_object, (RORegBit, RWRegBit, RORegBits, RWRegBits, ROReg, RWReg)):
# Make sure they are strings
if type(reg_object._fmt) == str and type(reg_object._endian) == str:
# Check each letter in format string, To see if allowable
for n in range(0, len(reg_object._fmt)):
2024-05-23 01:50:01 +00:00
if reg_object._fmt[n] in fmts:
# Add corresonding byte length to verify _num_bytes and format string agree
2024-05-23 01:50:01 +00:00
byte_count = byte_count + fmts[reg_object._fmt[n]]
else:
2024-05-23 15:45:44 +00:00
raise ValueError(f"unsupported format code of '{reg_object._fmt[n]}'")
2024-05-23 01:50:01 +00:00
if byte_count != reg_object._num_bytes:
2024-05-23 01:50:01 +00:00
raise ValueError(
f"format string accounts for {byte_count} bytes, _num_bytes value of {reg_object._num_bytes} does not match"
)
else:
raise TypeError("format and endian must be of type str()")
else:
2024-05-23 01:50:01 +00:00
raise TypeError(
"incorrect object type - must be ROReg, RWReg, ROBits, RWBits, ROReg, RWReg"
2024-05-23 15:45:44 +00:00
)