kopia lustrzana https://github.com/micropython/micropython-lib
Add files via upload
Folder containing register.py for consideration into micropython-libpull/860/head
rodzic
00fc3fd37b
commit
24c6ce5914
|
@ -0,0 +1,999 @@
|
|||
"""
|
||||
* 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
|
||||
|
||||
|
||||
"""
|
||||
|
||||
from machine import I2C
|
||||
from struct import pack, unpack
|
||||
|
||||
class RORegBit:
|
||||
def __init__(self, i2c, dev_addr, reg_addr, num_bytes, bit_location, endian='', fmt='B'):
|
||||
"""
|
||||
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()
|
||||
: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()
|
||||
: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()
|
||||
|
||||
|
||||
**Quickstart: Importing and using the device**
|
||||
|
||||
Here is an example of using the :class:`RORegBit` class.
|
||||
First you will need to import the following libraries:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
i2c = I2C(0) # I2C details are project specific
|
||||
|
||||
my_reg = RORegBit(i2c, 68, 5, 1, 5)
|
||||
|
||||
'my_reg' can now provide access to the :method:`__get__(). Using this method
|
||||
will return the value of the bit found at :param bit_location:.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
value = my_reg.__get__() # 0 or 1
|
||||
|
||||
Alternatively, a :class:`RORegBit` object(s) can be placed within another class.
|
||||
|
||||
.. code-block:: python
|
||||
# import
|
||||
from machine import I2C
|
||||
from register.register import RORegBit
|
||||
|
||||
# define I2C
|
||||
i2c = I2C(0)
|
||||
|
||||
# create class with desired functionality
|
||||
class FooDevice:
|
||||
|
||||
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)
|
||||
|
||||
def get_my_reg1(self):
|
||||
return self._my_reg_1.__get__()
|
||||
|
||||
def get_my_reg2(self):
|
||||
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
|
||||
|
||||
__check_reg(self)
|
||||
|
||||
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)
|
||||
|
||||
class RWRegBit:
|
||||
def __init__(self, i2c, dev_addr, reg_addr, num_bytes, bit_location, endian='', fmt='B'):
|
||||
"""
|
||||
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()
|
||||
: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()
|
||||
: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()
|
||||
|
||||
|
||||
**Quickstart: Importing and using the device**
|
||||
|
||||
Here is an example of using the :class:`RWRegBit` class.
|
||||
First you will need to import the following libraries:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
i2c = I2C(0) # I2C details are project specific
|
||||
|
||||
my_reg = RWRegBit(i2c, 68, 5, 1, 5)
|
||||
|
||||
'my_reg' can now provide access to the :method:`__get__() and :method:`__set__().
|
||||
Using these methods will get/set the value of the bit found at :param bit_location:.
|
||||
|
||||
.. code-block:: python
|
||||
my_reg.__set__(1)
|
||||
|
||||
print(my_reg.__get__()) # prints 1
|
||||
|
||||
Alternatively, a :class:`RWRegBit` object(s) can be placed within another class.
|
||||
|
||||
.. code-block:: python
|
||||
# import
|
||||
from machine import I2C
|
||||
from register.register import RWRegBit
|
||||
|
||||
# define I2C
|
||||
i2c = I2C(0)
|
||||
|
||||
# create class with desired functionality
|
||||
class FooDevice:
|
||||
|
||||
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__()
|
||||
|
||||
def get_my_reg2(self, n):
|
||||
return self._my_reg.__set__(n)
|
||||
|
||||
# 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
|
||||
|
||||
__check_reg(self)
|
||||
|
||||
self._premask, self._postmask = __calc_mask(bit_location, bit_location, num_bytes)
|
||||
|
||||
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)
|
||||
|
||||
def __set__(self, setting):
|
||||
"""
|
||||
:return: Returns 'True' if operation successful
|
||||
:rtype: bool()
|
||||
"""
|
||||
return __setbit(self, setting)
|
||||
|
||||
class RORegBits:
|
||||
def __init__(self, i2c, dev_addr, reg_addr, num_bytes, lsb, msb, endian='', fmt='B'):
|
||||
"""
|
||||
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()
|
||||
: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()
|
||||
:param msb: Location of most significant bit within bitfield
|
||||
:type msb: int()
|
||||
:param endian: Endian-ness of system for which the code is intended to run on. str('') uses native Endian-ness.
|
||||
:type endian: str()
|
||||
: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()
|
||||
|
||||
|
||||
**Quickstart: Importing and using the device**
|
||||
|
||||
Here is an example of using the :class:`RORegBits` class.
|
||||
First you will need to import the following libraries:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
i2c = I2C(0) # I2C details are project specific
|
||||
|
||||
my_reg = RORegBits(i2c_bus, 68, 5, 1, 0, 2)
|
||||
|
||||
'my_reg' can now provide access to the :method:`__get__(). Using this method
|
||||
will return the value of the bit found at :param bit_location:.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
value = my_reg.__get__() # Returns some value from 0b000 to 0b111
|
||||
|
||||
Alternatively, a :class:`RORegBits` object(s) can be placed within another class.
|
||||
|
||||
.. code-block:: python
|
||||
# import
|
||||
from machine import I2C
|
||||
from register.register import RORegBits
|
||||
|
||||
# define I2C
|
||||
i2c = I2C(0)
|
||||
|
||||
# create class with desired functionality
|
||||
class FooDevice:
|
||||
|
||||
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)
|
||||
|
||||
def get_my_reg1(self):
|
||||
return self._my_reg_1.__get__()
|
||||
|
||||
@property
|
||||
def my_reg2(self):
|
||||
return self._my_reg_2.__get__()
|
||||
|
||||
# invoke class object
|
||||
device = FooDevice(i2c)
|
||||
|
||||
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
|
||||
|
||||
__check_reg(self)
|
||||
|
||||
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)
|
||||
|
||||
class RWRegBits:
|
||||
def __init__(self, i2c, dev_addr, reg_addr, num_bytes, lsb, msb, endian='', fmt='B'):
|
||||
"""
|
||||
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()
|
||||
: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()
|
||||
:param msb: Location of most significant bit within bitfield
|
||||
:type msb: int()
|
||||
:param endian: Endian-ness of system for which the code is intended to run on. str('') uses native Endian-ness.
|
||||
:type endian: str()
|
||||
: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()
|
||||
|
||||
|
||||
**Quickstart: Importing and using the device**
|
||||
|
||||
Here is an example of using the :class:`RWRegBits` class.
|
||||
First you will need to import the following libraries:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
i2c = I2C(0) # I2C details are project specific
|
||||
|
||||
my_reg = RWRegBits(i2c_bus, 68, 5, 1, 0, 2)
|
||||
|
||||
'my_reg' can now provide access to :method:`__get__() and :method:`__set__().
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
my_reg.__set__(0b110) # Returns some value from 0b000 to 0b111
|
||||
value = my_reg.__get__() # Returns 0b110, assuming nothing changes
|
||||
|
||||
Alternatively, a :class:`RWRegBits` object(s) can be placed within another class.
|
||||
|
||||
.. code-block:: python
|
||||
# import
|
||||
from machine import I2C
|
||||
from register.register import RWRegBits
|
||||
|
||||
# define I2C
|
||||
i2c = I2C(0)
|
||||
|
||||
# create class with desired functionality
|
||||
class FooDevice:
|
||||
|
||||
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)
|
||||
|
||||
def get_my_reg1(self):
|
||||
return self._my_reg_1.__get__()
|
||||
|
||||
def set_my_reg1(self, n):
|
||||
return self._my_reg_1.__set__(n)
|
||||
|
||||
@property
|
||||
def my_reg2(self):
|
||||
return self._my_reg_2.__get__()
|
||||
|
||||
@my_reg2.setter
|
||||
def my_reg2(self, n):
|
||||
return self._my_reg_2.__set__(n)
|
||||
|
||||
# invoke class object
|
||||
device = FooDevice(i2c)
|
||||
|
||||
device.set_my_reg(0b110)
|
||||
print(device.get_my_reg()) # prints 6
|
||||
|
||||
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
|
||||
|
||||
__check_reg(self)
|
||||
|
||||
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)
|
||||
|
||||
def __set__(self, setting):
|
||||
"""
|
||||
:return: True if successful
|
||||
:rtype: bool()
|
||||
"""
|
||||
return __setbits(self, setting)
|
||||
|
||||
class ROReg:
|
||||
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,
|
||||
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()
|
||||
: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.
|
||||
: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()
|
||||
|
||||
|
||||
**Quickstart: Importing and using the device**
|
||||
|
||||
Here is an example of using the :class:`ROReg` class.
|
||||
First you will need to import the following libraries:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
i2c = I2C(0) # I2C details are project specific
|
||||
|
||||
my_reg = ROReg(i2c, 68, 5)
|
||||
|
||||
'my_reg' can now provide access to the :method:`__get__(). Using this method
|
||||
will return the value of the bit found at :param bit_location:.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
value = my_reg.__get__() # some value between 0b0 and 0b1111_1111
|
||||
|
||||
Alternatively, a :class:`ROReg` object(s) can be placed within another class.
|
||||
|
||||
.. code-block:: python
|
||||
# import
|
||||
from machine import I2C
|
||||
from register.register import ROReg
|
||||
|
||||
# define I2C
|
||||
i2c = I2C(0)
|
||||
|
||||
# create class with desired functionality
|
||||
class FooDevice:
|
||||
|
||||
def __init__(self, i2c_bus):
|
||||
self._my_reg_1 = ROReg(i2c_bus, 68, 5)
|
||||
self._my_reg_2 = ROReg(i2c_bus, 68, 6)
|
||||
|
||||
def get_my_reg1(self):
|
||||
return self._my_reg_1.__get__()
|
||||
|
||||
@property
|
||||
def my_reg2(self):
|
||||
return self._my_reg_1.__get__()
|
||||
|
||||
# invoke class object
|
||||
device = FooDevice(i2c)
|
||||
|
||||
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
|
||||
|
||||
__check_reg(self)
|
||||
|
||||
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)
|
||||
|
||||
class RWReg:
|
||||
"""
|
||||
Creates a :class:`RWReg` object which allows read and write access to n number of sequential registers,
|
||||
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()
|
||||
: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.
|
||||
: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()
|
||||
|
||||
|
||||
**Quickstart: Importing and using the device**
|
||||
|
||||
Here is an example of using the :class:`RWReg` class.
|
||||
First you will need to import the following libraries:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
i2c = I2C(0) # I2C details are project specific
|
||||
|
||||
my_reg = RWReg(i2c, 68, 5)
|
||||
|
||||
'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
|
||||
|
||||
Alternatively, a :class:`RWReg` object(s) can be placed within another class.
|
||||
|
||||
.. code-block:: python
|
||||
# import
|
||||
from machine import I2C
|
||||
from register.register import RWReg
|
||||
|
||||
# define I2C
|
||||
i2c = I2C(0)
|
||||
|
||||
# create class with desired functionality
|
||||
class FooDevice:
|
||||
|
||||
def __init__(self, i2c_bus):
|
||||
self._my_reg_1 = RWReg(i2c_bus, 68, 5)
|
||||
self._my_reg_2 = RWReg(i2c_bus, 68, 6)
|
||||
|
||||
def get_my_reg1(self):
|
||||
return self._my_reg_1.__get__()
|
||||
|
||||
def set_my_reg1(self, n):
|
||||
return self._my_reg_1.__set__(n)
|
||||
|
||||
@property
|
||||
def my_reg2(self):
|
||||
return self._my_reg_1.__get__()
|
||||
|
||||
@my_reg2.setter
|
||||
def my_reg2(self, n):
|
||||
return self._my_reg_1.__set__(n)
|
||||
|
||||
|
||||
# invoke class object
|
||||
device = FooDevice(i2c)
|
||||
|
||||
device.set_my_reg1(0b110)
|
||||
print(device.get_my_reg1()) # prints 6, assuming nothing changed
|
||||
|
||||
device.my_reg2 = 0b1111_0000
|
||||
print(device.my_reg2) # prints 240
|
||||
|
||||
"""
|
||||
def __init__(self, i2c, dev_addr, reg_addr, num_bytes, endian='', fmt='B'):
|
||||
self._i2c = i2c
|
||||
self._dev_addr = dev_addr
|
||||
self._reg_addr = reg_addr
|
||||
self._num_bytes = num_bytes
|
||||
self._fmt = fmt
|
||||
self._endian = endian
|
||||
|
||||
__check_reg(self)
|
||||
|
||||
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)
|
||||
|
||||
def __set__(self, setting):
|
||||
"""
|
||||
:param setting: Value(s) to be written to register(s). Order must match :param fmt:.
|
||||
: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)
|
||||
|
||||
"""
|
||||
*
|
||||
* GLOBAL HELPER FUNCTIONS
|
||||
*
|
||||
*
|
||||
"""
|
||||
def __getbit(reg_object):
|
||||
if isinstance(reg_object, (RORegBit, RWRegBit)):
|
||||
# Retrieve register value and unpack to int
|
||||
value = reg_object._i2c.readfrom_mem(reg_object._dev_addr, reg_object._reg_addr, reg_object._num_bytes)
|
||||
|
||||
# Unpack byte
|
||||
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:
|
||||
raise TypeError("incorrect object type - must be RORegBit, RWRegBit")
|
||||
|
||||
def __setbit(reg_object, setting):
|
||||
if isinstance(reg_object, RWRegBit):
|
||||
if setting in (0,1):
|
||||
# Retrieve register value and unpack to int
|
||||
value = reg_object._i2c.readfrom_mem(reg_object._dev_addr, reg_object._reg_addr, reg_object._num_bytes)
|
||||
|
||||
# Unpack byte
|
||||
value = unpack(reg_object._endian+reg_object._fmt, value)[0]
|
||||
|
||||
# Assemble byte
|
||||
value = (value®_object._postmask) + (setting<<reg_object._bit_location) + (value®_object._premask)
|
||||
|
||||
# Pack to bytes
|
||||
value = pack(reg_object._endian+reg_object._fmt, value)
|
||||
|
||||
# Write to I2C
|
||||
reg_object._i2c.writeto_mem(reg_object._dev_addr, reg_object._reg_addr, value)
|
||||
|
||||
# Return True for success
|
||||
return True
|
||||
|
||||
else:
|
||||
raise ValueError("setting must be int(0) or int(1)")
|
||||
else:
|
||||
raise TypeError("incorrect object type - must be RWRegBit")
|
||||
|
||||
def __getbits(reg_object):
|
||||
if isinstance(reg_object, (RORegBits, RWRegBits)):
|
||||
# Retrieve register value and unpack to int
|
||||
value = reg_object._i2c.readfrom_mem(reg_object._dev_addr, reg_object._reg_addr, reg_object._num_bytes)
|
||||
|
||||
# Unpack bytes
|
||||
value = unpack(reg_object._endian+reg_object._fmt, value)[0]
|
||||
|
||||
# Return value of bit field
|
||||
return (value & reg_object._mask)>>reg_object._lsb
|
||||
|
||||
else:
|
||||
raise TypeError("incorrect object type - must be RORegBits, RWRegBits")
|
||||
|
||||
def __setbits(reg_object, setting):
|
||||
if isinstance(reg_object, RWRegBits):
|
||||
if isinstance(setting, int) and setting <= reg_object._mask:
|
||||
|
||||
# Retrieve register value and unpack to int
|
||||
value = reg_object._i2c.readfrom_mem(reg_object._dev_addr, reg_object._reg_addr, reg_object._num_bytes)
|
||||
|
||||
# Unpack bytes
|
||||
value = unpack(reg_object._endian+reg_object._fmt, value)[0]
|
||||
|
||||
# Assemble
|
||||
value = (value®_object._postmask) + (setting<<reg_object._lsb) + (value®_object._premask)
|
||||
|
||||
# Pack to bytes object
|
||||
value = struct.pack(reg_object._endian+reg_object._fmt, value)
|
||||
|
||||
# Write to device
|
||||
reg_object._i2c.writeto_mem(reg_object._dev_addr, reg_object._reg_addr, value)
|
||||
|
||||
return True
|
||||
|
||||
else:
|
||||
raise ValueError(f"value of setting exceeds max value of bitfield: {reg_object._mask}")
|
||||
else:
|
||||
raise TypeError("incorrect object type - must be RWRegBits")
|
||||
|
||||
def __getreg(reg_object):
|
||||
if isinstance(reg_object, (ROReg, RWReg)):
|
||||
# Retrieve register value and unpack to int
|
||||
values = reg_object._i2c.readfrom_mem(reg_object._dev_addr, reg_object._reg_addr, reg_object._num_bytes)
|
||||
|
||||
# Return Tuple of values
|
||||
return unpack(reg_object._endian+reg_object._fmt, values)
|
||||
|
||||
else:
|
||||
raise TypeError("incorrect object type - must be ROReg, RWReg")
|
||||
|
||||
def __setreg(reg_object, settings):
|
||||
|
||||
if isinstance(reg_object, RWReg):
|
||||
if isinstance(settings, (bytes, bytearray)):
|
||||
# Write to device
|
||||
reg_object._i2c.writeto_mem(reg_object._dev_addr, reg_object._reg_addr, settings)
|
||||
|
||||
|
||||
elif isinstance(settings, (tuple, list)):
|
||||
# Where our data will go
|
||||
d = bytearray()
|
||||
|
||||
# Pack and append to d
|
||||
for n in range(0, len(settings)):
|
||||
d.extend(pack(reg_object._endian+reg_object._fmt[n] ,settings[n]))
|
||||
|
||||
# Write to device
|
||||
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):
|
||||
d = pack(reg_object._endian+reg_object._fmt ,settings)
|
||||
reg_object._i2c.writeto_mem(reg_object._dev_addr, reg_object._reg_addr, d)
|
||||
|
||||
else:
|
||||
raise TypeError("unsupported object type, settings must be int(), bytes(), bytearray(), tuple(), or list()")
|
||||
else:
|
||||
raise TypeError("incorrect object type - must be ROReg, RWReg")
|
||||
|
||||
def __calc_mask(lsb, msb, numbytes):
|
||||
"""
|
||||
Takes in full description of bitfield that needs masking
|
||||
|
||||
returns ints() pre, mask, post
|
||||
"""
|
||||
|
||||
# Check input types
|
||||
if lsb.__class__() == int() and lsb >= 0:
|
||||
if msb.__class__() == int() and msb >= 0:
|
||||
if numbytes.__class__() == int() and numbytes >= 0:
|
||||
|
||||
# Check for detectable errors
|
||||
if msb>=lsb:
|
||||
|
||||
# Single bit mask
|
||||
if msb == lsb:
|
||||
pre, post = 0b0, 0b0
|
||||
|
||||
# Calc post masking
|
||||
for bit in range(msb+1, numbytes*8):
|
||||
post = (post<<1) + 0b1
|
||||
|
||||
# Calc pre masking
|
||||
for bit in range(0, lsb):
|
||||
pre = (pre<<1) + 0b1
|
||||
|
||||
return pre, post
|
||||
|
||||
# Multibit mask
|
||||
else:
|
||||
|
||||
# Values to return
|
||||
pre, mask, post = 0b0, 0b0, 0b0
|
||||
|
||||
# Calc post masking
|
||||
for bit in range(msb+1, numbytes*8):
|
||||
post = (post<<1) + 0b1
|
||||
|
||||
# Calc bitfield masking
|
||||
for bit in range(lsb, msb+1):
|
||||
mask = (mask<<1) + 0b1
|
||||
|
||||
# No bits lower than 0
|
||||
if lsb == 0:
|
||||
return 0b0, mask, post
|
||||
|
||||
else:
|
||||
for bit in range(0, lsb):
|
||||
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")
|
||||
|
||||
def __check_reg(reg_object):
|
||||
|
||||
# Alowable struct.pack/unpack formats to check for
|
||||
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
|
||||
|
||||
# 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)):
|
||||
if reg_object._fmt[n] in fmts:
|
||||
# Add corresonding byte length to verify _num_bytes and format string agree
|
||||
byte_count = byte_count + fmts[reg_object._fmt[n]]
|
||||
|
||||
else:
|
||||
raise ValueError(f"unsupported format code of '{reg_object._fmt[n]}'")
|
||||
|
||||
if byte_count != reg_object._num_bytes:
|
||||
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:
|
||||
raise TypeError("incorrect object type - must be ROReg, RWReg, ROBits, RWBits, ROReg, RWReg")
|
||||
|
||||
class Transaction:
|
||||
"""
|
||||
The user can supply a transaction object with a list of any number of
|
||||
Register objects. The Transaction object will then perform one I2C
|
||||
transaction and return all data as a list OR perform all write operations.
|
||||
|
||||
1) The Register objects should all be from one physical I2C device
|
||||
2) True = Read, False = Write (Default is read)
|
||||
3) Reads can be from non-sequential registers
|
||||
4) Writes can be made only to sequential registers OR more than one
|
||||
transaction will be generated
|
||||
|
||||
i.e.
|
||||
|
||||
# Define Register objects
|
||||
register1 = ROBits()
|
||||
register2 = ROBits()
|
||||
register3 = ROBits()
|
||||
|
||||
# Create list object containing only Register objects
|
||||
list_of_registers = [register1, register2, register3]
|
||||
|
||||
# Instantiate Transaction object
|
||||
data_from_device = Transaction(list_of_registers)
|
||||
|
||||
# Retrieve data
|
||||
data = data_from_device.__get__()
|
||||
|
||||
# Use data as desired
|
||||
datapoint_1 = data_from_device[0]
|
||||
datapoint_2 = data_from_device[1]
|
||||
datapoint_3 = data_from_device[2]
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, read_or_write:bool = True, list_of_registers:list() =[]):
|
||||
# Data
|
||||
self.__list_of_registers = list_of_registers
|
||||
|
||||
# Check if it is a list
|
||||
if self.__list_of_registers.__class__() == list():
|
||||
|
||||
for reg in self.__list_of_registers:
|
||||
# Check each element against all possible Register types
|
||||
if self.__list_of_registers[reg].__class__() in [RORegBit, RORegBits, RWRegBit, RWRegBits, RORegStruct]:
|
||||
pass
|
||||
|
||||
else:
|
||||
# Error - list_element[reg] not a register object
|
||||
pass
|
||||
|
||||
else:
|
||||
# Error - list_of_registers object must be list()
|
||||
pass
|
||||
|
||||
def add_reg(self, reg_object):
|
||||
"""
|
||||
This function allows for register objects to be added to an already
|
||||
instantiated Transaction object
|
||||
"""
|
||||
if reg_object.__class__() in [RORegBit, RORegBits, RWRegBit, RWRegBits, RORegStruct]:
|
||||
self.__list_of_registers.append(reg_object)
|
||||
|
||||
self._order_list()
|
||||
|
||||
else:
|
||||
# Error - reg object of incorrect type()
|
||||
pass
|
||||
|
||||
def rem_reg(self, index):
|
||||
"""
|
||||
This function allows for a register object to be removed from an
|
||||
already instantiated transaction object
|
||||
"""
|
||||
if index in range(0, len(self.__list_of_registers)):
|
||||
# Remove element
|
||||
self.__list_of_registers.remove(index)
|
||||
else:
|
||||
# Error - index out of bounds
|
||||
pass
|
||||
|
||||
def order_list(self):
|
||||
"""
|
||||
Sorts list of registers by register address
|
||||
"""
|
||||
self.__list_of_registers = sorted(self.__list_of_registers, key=lambda reg:reg.reg_addr)
|
||||
|
||||
def data(self):
|
||||
"""
|
||||
Performs 1 i2c transaction and returns data as list
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Ładowanie…
Reference in New Issue