Giter Site home page Giter Site logo

i2cdevice-python's Introduction

i2cdevice

Build Status Coverage Status PyPi Package Python Versions

i2cdevice is a Python framework aimed at dealing with common SMBus/i2c device interaction patterns.

This project aims to make group-up implementations of Python libraries for i2c devices easier, simpler and inherently self-documenting.

It does this by separating a detailed description of the hardware registers and how they should be manipulated into a structured definition language.

This project does not aim to help you make a public API for Python devices- that should be built on top of the fundamentals presented here.

Using This Library

You should generally aim for a 1:1 representation of the hardware registers in the device you're implementing, even if you don't plan to use all the functionality. Having the full register set implemented allows for the easy addition of new features in future.

Check out the libraries listed below for real-world examples.

Features

  • Classes for describing devices, registers and individual bit fields within registers in a fashion which maps closely with the datasheet
  • Value translation from real world numbers (such as 512ms) to register values (such as 0b111) and back again
  • Read registers into a namedtuple of fields using get
  • Write multiple register fields in a transaction using set with keyword arguments
  • Support for treating multiple-bytes as a single value, or single register with multiple values

Built With i2cdevice

Examples

The below example defines the ALS_CONTROL register on an ltr559, with register address 0x80.

It has 3 fields; gain - which is mapped to real world values - and sw_reset/mode which are single bit flags.

ALS_CONTROL = Register('ALS_CONTROL', 0x80, fields=(
    BitField('gain', 0b00011100, values_map={1: 0b000, 2: 0b001, 4: 0b011, 8:0b011, 48:0b110, 96:0b111}),
    BitField('sw_reset', 0b00000010),
    BitField('mode', 0b00000001)
))

A lookup table is not required for values, however, a function can be used to translate values from and to a format that the device understands.

The below example uses i2cdevice._byte_swap to change the endianness of two 16bit values before they are stored/retrieved.

# This will address 0x88, 0x89, 0x8A and 0x8B as a continuous 32bit register
ALS_DATA = Register('ALS_DATA', 0x88, fields=(
    BitField('ch1', 0xFFFF0000, bitwidth=16, values_in=_byte_swap, values_out=_byte_swap),
    BitField('ch0', 0x0000FFFF, bitwidth=16, values_in=_byte_swap, values_out=_byte_swap)
), read_only=True, bitwidth=32)

A "Register" and its "BitField"s define a set of rules and logic for detailing with the hardware register which is interpreted by the device class. Registers are declared on a device using the registers=() keyword argument:

I2C_ADDR = 0x23
ltr559 = Device(I2C_ADDR, bit_width=8, registers=(
	ALS_CONTROL,
	ALS_DATA
))

Reading Registers

One configured a register's fields can be read into a namedtuple using the get method:

register_values = ltr559.get('ALS_CONTROL')
gain = register_values.gain
sw_reset = register_values.sw_reset
mode = register_values.mode

Writing Registers

The namedtuple returned from get is immutable and does not attempt to map values back to the hardware, in order to write one or more fields to a register you must use set with a keyword argument for each field:

ltr559.set('ALS_CONTROL',
           gain=4,
           sw_reset=1)

This will read the register state from the device, update the bitfields accordingly and write the result back.

i2cdevice-python's People

Contributors

dkao avatar gadgetoid avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

i2cdevice-python's Issues

Feature suggestion: variable register address length

I really liked this library and as I used it via pimoroni's BME280 library I wanted to write a SCD30 library with it.
But it turns out the SCD30 uses 16 bit addresses and it seems i2cdevice always assumes 8 bit addresses.
I guess 8 and 16 bit addresses should be enough choice.

TODO: Allow registers to be passed as an argument to Device

The current pattern is slightly topsy-turvey in that a Device is defined, and then passed to each register- some wonky underlying logic then handles relating these two things together reasonably.

Ideally the Register should be passed as an argument to the Device, in much the same way as the BitField is passed to the register:

my_device = Device(
    0x44,
    PS_CONTROL = Register('PS_CONTROL', 0x81, fields=(
        BitField('saturation_indicator_enable', 0b00100000),
        BitField('active', 0b00000011, values_map={False: 0b00, True: 0b11}),
    ))
)

And then accessed with:

my_device.PS_CONTROL.set_saturation_indicator_enable(True)

Registers could then be defined once and re-used in multiple devices. Granted this is unlikely, but I'm trying to achieve the expressiveness of Construct.

Similarly values_in, values_out and values_map should be just "adaptor=" and a factory class should be capable of taking a dictionary of values and producing an "adaptor" that translates them accordingly:

class Adaptor():
    def in(self, value):
        return value
    def out(self, value):
        return value

TypeError

This afternoon I successfully finished a port of the combo: i2cdevice-python and rv3028-python for use with a RPi Pico mounted on a Pimoroni 'pico explorer pack' on which also stacked a rv3028 breakout.

The only problem left is the fact that there occurs the following error in i2cdevice/__init__.py file, line 174 (class Device.__init__()).
self.__dict__[register.name] = _RegisterProxy(self, register)

For the moment I put this line 'out of service' by commenting it out. For the normal get/set date and time functions this 'being out of service' of this line looks not to create problems. However: this line was there for a reason, isn't it?
Any advice is welcome!

Register class makes it impossible to handover parameter fields

I think that /i2device/ __ini__.py, line 124, should read: 'self.fields = fields' instead of: 'self.fields = {}' because with this command it makes it impossible to handover the value of the parameter 'fields'. After I changed this, I received a "TypeError: 'BitField' object is not iterable" (in line 127). I am trying to investigate/solve that.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.