adafruit-circuitpython-register-spi


Nameadafruit-circuitpython-register-spi JSON
Version 1.0.1 PyPI version JSON
download
home_page
SummaryPython data descriptor classes to represent hardware registers on SPI devices.
upload_time2023-10-02 20:58:45
maintainer
docs_urlNone
author
requires_python
licenseMIT
keywords adafruit blinka circuitpython micropython register_spi register spi
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            Introduction
============


.. image:: https://readthedocs.org/projects/adafruit-circuitpython-register-spi/badge/?version=latest
    :target: https://docs.circuitpython.org/projects/register_spi/en/latest/
    :alt: Documentation Status


.. image:: https://github.com/adafruit/Adafruit_CircuitPython_Bundle/blob/main/badges/adafruit_discord.svg
    :target: https://adafru.it/discord
    :alt: Discord


.. image:: https://github.com/adafruit/Adafruit_CircuitPython_Register_SPI/workflows/Build%20CI/badge.svg
    :target: https://github.com/adafruit/Adafruit_CircuitPython_Register_SPI/actions
    :alt: Build Status


.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
    :target: https://github.com/psf/black
    :alt: Code Style: Black

Python data descriptor classes to represent hardware registers on SPI devices.


Dependencies
=============
This driver depends on:

* `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
* `Bus Device <https://github.com/adafruit/Adafruit_CircuitPython_BusDevice>`_

Please ensure all dependencies are available on the CircuitPython filesystem.
This is easily achieved by downloading
`the Adafruit library and driver bundle <https://circuitpython.org/libraries>`_
or individual libraries can be installed using
`circup <https://github.com/adafruit/circup>`_.



Installing from PyPI
=====================

On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from
PyPI <https://pypi.org/project/adafruit-circuitpython-register-spi/>`_.
To install for current user:

.. code-block:: shell

    pip3 install adafruit-circuitpython-register-spi

To install system-wide (this may be required in some cases):

.. code-block:: shell

    sudo pip3 install adafruit-circuitpython-register-spi

To install in a virtual environment in your current project:

.. code-block:: shell

    mkdir project-name && cd project-name
    python3 -m venv .venv
    source .venv/bin/activate
    pip3 install adafruit-circuitpython-register-spi



Installing to a Connected CircuitPython Device with Circup
==========================================================

Make sure that you have ``circup`` installed in your Python environment.
Install it with the following command if necessary:

.. code-block:: shell

    pip3 install circup

With ``circup`` installed and your CircuitPython device connected use the
following command to install:

.. code-block:: shell

    circup install register_spi

Or the following command to update an existing version:

.. code-block:: shell

    circup update

Usage Example
=============

Creating a driver
-----------------

Creating a driver with the register library is really easy. First, import the
register modules you need from the `available modules <adafruit_register/index.html>`_:

.. code-block:: python

    from adafruit_register import i2c_bit
    from adafruit_bus_device import i2c_device

Next, define where the bit is located in the device's memory map:

.. code-block:: python

    class HelloWorldDevice:
        """Device with two bits to control when the words 'hello' and 'world' are lit."""

        hello = i2c_bit.RWBit(0x0, 0x0)
        """Bit to indicate if hello is lit."""

        world = i2c_bit.RWBit(0x1, 0x0)
        """Bit to indicate if world is lit."""

Lastly, we need to add an ``i2c_device`` member of type `I2CDevice <https://circuitpython.readthedocs.io/projects/busdevice/en/latest/api.html#adafruit_bus_device.i2c_device.I2CDevice>`_
that manages sharing the I2C bus for us. Make sure the name is exact, otherwise
the registers will not be able to find it. Also, make sure that the i2c device
implements the `busio.I2C` interface.

.. code-block:: python

        def __init__(self, i2c, device_address=0x0):
            self.i2c_device = i2c_device.I2CDevice(i2c, device_address)

Thats it! Now we have a class we can use to talk to those registers:

.. code-block:: python

    import busio
    from board import *

    with busio.I2C(SCL, SDA) as i2c:
        device = HelloWorldDevice(i2c)
        device.hello = True
        device.world = True

Adding register types
--------------------------

Adding a new register type is a little more complicated because you need to be
careful and minimize the amount of memory the class will take. If you don't,
then a driver with five registers of your type could take up five times more
extra memory.

First, determine whether the new register class should go in an existing module
or not. When in doubt choose a new module. The more finer grained the modules
are, the fewer extra classes a driver needs to load in.

Here is the start of the `RWBit` class:

.. code-block:: python

    class RWBit:
        """
        Single bit register that is readable and writeable.

        Values are `bool`

        :param int register_address: The register address to read the bit from
        :param type bit: The bit index within the byte at ``register_address``
        """
        def __init__(self, register_address, bit):
            self.bit_mask = 1 << bit
            self.buffer = bytearray(2)
            self.buffer[0] = register_address

The first thing done is writing an RST formatted class comment that explains the
functionality of the register class and any requirements of the register layout.
It also documents the parameters passed into the constructor (``__init__``) which
configure the register location in the device map. It does not include the
device address or the i2c object because its shared on the device class instance
instead. That way if you have multiple of the same device on the same bus, the
register classes will be shared.

In ``__init__`` we only use two member variable because each costs 8 bytes of
memory plus the memory for the value. And remember this gets multiplied by the
number of registers of this type in a driver! Thats why we pack both the
register address and data byte into one bytearray. We could use two byte arrays
of size one but each MicroPython object is 16 bytes minimum due to the garbage
collector. So, by sharing a byte array we keep it to the 16 byte minimum instead
of 32 bytes. Each `memoryview` also costs 16 bytes minimum so we avoid them too.

Another thing we could do is allocate the `bytearray` only when we need it. This
has the advantage of taking less memory up front but the cost of allocating it
every access and risking it failing. If you want to add a version of ``Foo`` that
lazily allocates the underlying buffer call it ``FooLazy``.

Ok, onward. To make a `data descriptor <https://docs.python.org/3/howto/descriptor.html>`_
we must implement ``__get__`` and ``__set__``.

.. code-block:: python

    def __get__(self, obj, objtype=None):
        with obj.i2c_device as i2c:
            i2c.write_then_readinto(self.buffer, self.buffer, out_end=1, in_start=1)
        return bool(self.buffer[1] & self.bit_mask)

    def __set__(self, obj, value):
        with obj.i2c_device as i2c:
            i2c.write_then_readinto(self.buffer, self.buffer, out_end=1, in_start=1)
            if value:
                self.buffer[1] |= self.bit_mask
            else:
                self.buffer[1] &= ~self.bit_mask
            obj.i2c_device.write(self.buffer)

As you can see, we have two places to get state from. First, ``self`` stores the
register class members which locate the register within the device memory map.
Second, ``obj`` is the driver class that uses the register class which must by
definition provide a `I2CDevice <https://circuitpython.readthedocs.io/projects/busdevice/en/latest/api.html#adafruit_bus_device.i2c_device.I2CDevice>`_ compatible
object as ``i2c_device``. This object does two thing for us:

  1. Waits for the bus to free, locks it as we use it and frees it after.
  2. Saves the device address and other settings so we don't have to.

Note that we take heavy advantage of the ``start`` and ``end`` parameters to the
i2c functions to slice the buffer without actually allocating anything extra.
They function just like ``self.buffer[start:end]`` without the extra allocation.

Thats it! Now you can use your new register class like the example above. Just
remember to keep the number of members to a minimum because the class may be
used a bunch of times.


Documentation
=============
API documentation for this library can be found on `Read the Docs <https://docs.circuitpython.org/projects/register_spi/en/latest/>`_.

For information on building library documentation, please check out
`this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.

Contributing
============

Contributions are welcome! Please read our `Code of Conduct
<https://github.com/adafruit/Adafruit_CircuitPython_Register_SPI/blob/HEAD/CODE_OF_CONDUCT.md>`_
before contributing to help this project stay welcoming.

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "adafruit-circuitpython-register-spi",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "adafruit,blinka,circuitpython,micropython,register_spi,register,spi",
    "author": "",
    "author_email": "Adafruit Industries <circuitpython@adafruit.com>",
    "download_url": "https://files.pythonhosted.org/packages/85/8c/2e0346146e8962f233b0e50cdf9d6e267d230c4326c9bf5d58c7729f3b26/adafruit-circuitpython-register-spi-1.0.1.tar.gz",
    "platform": null,
    "description": "Introduction\n============\n\n\n.. image:: https://readthedocs.org/projects/adafruit-circuitpython-register-spi/badge/?version=latest\n    :target: https://docs.circuitpython.org/projects/register_spi/en/latest/\n    :alt: Documentation Status\n\n\n.. image:: https://github.com/adafruit/Adafruit_CircuitPython_Bundle/blob/main/badges/adafruit_discord.svg\n    :target: https://adafru.it/discord\n    :alt: Discord\n\n\n.. image:: https://github.com/adafruit/Adafruit_CircuitPython_Register_SPI/workflows/Build%20CI/badge.svg\n    :target: https://github.com/adafruit/Adafruit_CircuitPython_Register_SPI/actions\n    :alt: Build Status\n\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg\n    :target: https://github.com/psf/black\n    :alt: Code Style: Black\n\nPython data descriptor classes to represent hardware registers on SPI devices.\n\n\nDependencies\n=============\nThis driver depends on:\n\n* `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_\n* `Bus Device <https://github.com/adafruit/Adafruit_CircuitPython_BusDevice>`_\n\nPlease ensure all dependencies are available on the CircuitPython filesystem.\nThis is easily achieved by downloading\n`the Adafruit library and driver bundle <https://circuitpython.org/libraries>`_\nor individual libraries can be installed using\n`circup <https://github.com/adafruit/circup>`_.\n\n\n\nInstalling from PyPI\n=====================\n\nOn supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from\nPyPI <https://pypi.org/project/adafruit-circuitpython-register-spi/>`_.\nTo install for current user:\n\n.. code-block:: shell\n\n    pip3 install adafruit-circuitpython-register-spi\n\nTo install system-wide (this may be required in some cases):\n\n.. code-block:: shell\n\n    sudo pip3 install adafruit-circuitpython-register-spi\n\nTo install in a virtual environment in your current project:\n\n.. code-block:: shell\n\n    mkdir project-name && cd project-name\n    python3 -m venv .venv\n    source .venv/bin/activate\n    pip3 install adafruit-circuitpython-register-spi\n\n\n\nInstalling to a Connected CircuitPython Device with Circup\n==========================================================\n\nMake sure that you have ``circup`` installed in your Python environment.\nInstall it with the following command if necessary:\n\n.. code-block:: shell\n\n    pip3 install circup\n\nWith ``circup`` installed and your CircuitPython device connected use the\nfollowing command to install:\n\n.. code-block:: shell\n\n    circup install register_spi\n\nOr the following command to update an existing version:\n\n.. code-block:: shell\n\n    circup update\n\nUsage Example\n=============\n\nCreating a driver\n-----------------\n\nCreating a driver with the register library is really easy. First, import the\nregister modules you need from the `available modules <adafruit_register/index.html>`_:\n\n.. code-block:: python\n\n    from adafruit_register import i2c_bit\n    from adafruit_bus_device import i2c_device\n\nNext, define where the bit is located in the device's memory map:\n\n.. code-block:: python\n\n    class HelloWorldDevice:\n        \"\"\"Device with two bits to control when the words 'hello' and 'world' are lit.\"\"\"\n\n        hello = i2c_bit.RWBit(0x0, 0x0)\n        \"\"\"Bit to indicate if hello is lit.\"\"\"\n\n        world = i2c_bit.RWBit(0x1, 0x0)\n        \"\"\"Bit to indicate if world is lit.\"\"\"\n\nLastly, we need to add an ``i2c_device`` member of type `I2CDevice <https://circuitpython.readthedocs.io/projects/busdevice/en/latest/api.html#adafruit_bus_device.i2c_device.I2CDevice>`_\nthat manages sharing the I2C bus for us. Make sure the name is exact, otherwise\nthe registers will not be able to find it. Also, make sure that the i2c device\nimplements the `busio.I2C` interface.\n\n.. code-block:: python\n\n        def __init__(self, i2c, device_address=0x0):\n            self.i2c_device = i2c_device.I2CDevice(i2c, device_address)\n\nThats it! Now we have a class we can use to talk to those registers:\n\n.. code-block:: python\n\n    import busio\n    from board import *\n\n    with busio.I2C(SCL, SDA) as i2c:\n        device = HelloWorldDevice(i2c)\n        device.hello = True\n        device.world = True\n\nAdding register types\n--------------------------\n\nAdding a new register type is a little more complicated because you need to be\ncareful and minimize the amount of memory the class will take. If you don't,\nthen a driver with five registers of your type could take up five times more\nextra memory.\n\nFirst, determine whether the new register class should go in an existing module\nor not. When in doubt choose a new module. The more finer grained the modules\nare, the fewer extra classes a driver needs to load in.\n\nHere is the start of the `RWBit` class:\n\n.. code-block:: python\n\n    class RWBit:\n        \"\"\"\n        Single bit register that is readable and writeable.\n\n        Values are `bool`\n\n        :param int register_address: The register address to read the bit from\n        :param type bit: The bit index within the byte at ``register_address``\n        \"\"\"\n        def __init__(self, register_address, bit):\n            self.bit_mask = 1 << bit\n            self.buffer = bytearray(2)\n            self.buffer[0] = register_address\n\nThe first thing done is writing an RST formatted class comment that explains the\nfunctionality of the register class and any requirements of the register layout.\nIt also documents the parameters passed into the constructor (``__init__``) which\nconfigure the register location in the device map. It does not include the\ndevice address or the i2c object because its shared on the device class instance\ninstead. That way if you have multiple of the same device on the same bus, the\nregister classes will be shared.\n\nIn ``__init__`` we only use two member variable because each costs 8 bytes of\nmemory plus the memory for the value. And remember this gets multiplied by the\nnumber of registers of this type in a driver! Thats why we pack both the\nregister address and data byte into one bytearray. We could use two byte arrays\nof size one but each MicroPython object is 16 bytes minimum due to the garbage\ncollector. So, by sharing a byte array we keep it to the 16 byte minimum instead\nof 32 bytes. Each `memoryview` also costs 16 bytes minimum so we avoid them too.\n\nAnother thing we could do is allocate the `bytearray` only when we need it. This\nhas the advantage of taking less memory up front but the cost of allocating it\nevery access and risking it failing. If you want to add a version of ``Foo`` that\nlazily allocates the underlying buffer call it ``FooLazy``.\n\nOk, onward. To make a `data descriptor <https://docs.python.org/3/howto/descriptor.html>`_\nwe must implement ``__get__`` and ``__set__``.\n\n.. code-block:: python\n\n    def __get__(self, obj, objtype=None):\n        with obj.i2c_device as i2c:\n            i2c.write_then_readinto(self.buffer, self.buffer, out_end=1, in_start=1)\n        return bool(self.buffer[1] & self.bit_mask)\n\n    def __set__(self, obj, value):\n        with obj.i2c_device as i2c:\n            i2c.write_then_readinto(self.buffer, self.buffer, out_end=1, in_start=1)\n            if value:\n                self.buffer[1] |= self.bit_mask\n            else:\n                self.buffer[1] &= ~self.bit_mask\n            obj.i2c_device.write(self.buffer)\n\nAs you can see, we have two places to get state from. First, ``self`` stores the\nregister class members which locate the register within the device memory map.\nSecond, ``obj`` is the driver class that uses the register class which must by\ndefinition provide a `I2CDevice <https://circuitpython.readthedocs.io/projects/busdevice/en/latest/api.html#adafruit_bus_device.i2c_device.I2CDevice>`_ compatible\nobject as ``i2c_device``. This object does two thing for us:\n\n  1. Waits for the bus to free, locks it as we use it and frees it after.\n  2. Saves the device address and other settings so we don't have to.\n\nNote that we take heavy advantage of the ``start`` and ``end`` parameters to the\ni2c functions to slice the buffer without actually allocating anything extra.\nThey function just like ``self.buffer[start:end]`` without the extra allocation.\n\nThats it! Now you can use your new register class like the example above. Just\nremember to keep the number of members to a minimum because the class may be\nused a bunch of times.\n\n\nDocumentation\n=============\nAPI documentation for this library can be found on `Read the Docs <https://docs.circuitpython.org/projects/register_spi/en/latest/>`_.\n\nFor information on building library documentation, please check out\n`this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.\n\nContributing\n============\n\nContributions are welcome! Please read our `Code of Conduct\n<https://github.com/adafruit/Adafruit_CircuitPython_Register_SPI/blob/HEAD/CODE_OF_CONDUCT.md>`_\nbefore contributing to help this project stay welcoming.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Python data descriptor classes to represent hardware registers on SPI devices.",
    "version": "1.0.1",
    "project_urls": {
        "Homepage": "https://github.com/adafruit/Adafruit_CircuitPython_Register_SPI"
    },
    "split_keywords": [
        "adafruit",
        "blinka",
        "circuitpython",
        "micropython",
        "register_spi",
        "register",
        "spi"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "bf1ea69e73b8bf1ff08b24fcc5f63577aa80694b2072f018f382e0ca43596f5b",
                "md5": "1c81fe52762e6e18d5633d30c9c0a5a5",
                "sha256": "2bde72e79e0dc4f3ce4dd507c236616b4266bd4f5c4eb5ab824170348fb9ad3f"
            },
            "downloads": -1,
            "filename": "adafruit_circuitpython_register_spi-1.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "1c81fe52762e6e18d5633d30c9c0a5a5",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 8053,
            "upload_time": "2023-10-02T20:58:43",
            "upload_time_iso_8601": "2023-10-02T20:58:43.860132Z",
            "url": "https://files.pythonhosted.org/packages/bf/1e/a69e73b8bf1ff08b24fcc5f63577aa80694b2072f018f382e0ca43596f5b/adafruit_circuitpython_register_spi-1.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "858c2e0346146e8962f233b0e50cdf9d6e267d230c4326c9bf5d58c7729f3b26",
                "md5": "e4908a5a6e48cf09d13059dad28aed11",
                "sha256": "48fbffd9fb43e11c77a8c79284b085ebbce88faffe98baae71f68a169348d43f"
            },
            "downloads": -1,
            "filename": "adafruit-circuitpython-register-spi-1.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "e4908a5a6e48cf09d13059dad28aed11",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 28112,
            "upload_time": "2023-10-02T20:58:45",
            "upload_time_iso_8601": "2023-10-02T20:58:45.248909Z",
            "url": "https://files.pythonhosted.org/packages/85/8c/2e0346146e8962f233b0e50cdf9d6e267d230c4326c9bf5d58c7729f3b26/adafruit-circuitpython-register-spi-1.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-10-02 20:58:45",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "adafruit",
    "github_project": "Adafruit_CircuitPython_Register_SPI",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [],
    "lcname": "adafruit-circuitpython-register-spi"
}
        
Elapsed time: 0.16690s