Package: py-i2c-register
Python I2C RegisterPython wrapper library around the common I2C controller register
pattern.I2C Register is a python library which aims to make communicating with
registers on I2C devices dead simple. It is meant to directly transfer
the Register Definitions pages of a data sheet into your program.Table Of ContentsInstallationQuick ExampleSystems OverviewCreating a RegisterListDefining RegistersAdding RegisterSegmentsReading from RegisterSegmentsWriting to RegisterSegmentsWritting Wrapper ClassesDevelopmentRunning TestsDistributionSetupStepsInstallationI2C Register is available as a PIP package with the namepy-i2c-register.Simply use PIP to install:pipinstall--userpy-i2c-registerYou will then be able to include thepy_i2c_registermodule and its
various classes:frompy_i2c_register.register_listimportRegisterListfrompy_i2c_register.registerimportRegisterfrompy_i2c_register.register_segmentimportRegisterSegmentQuick ExampleTake these control register definitions from a data sheet:Example Hardware Data Sheet Register Definitions page 1Example Hardware Data Sheet Register Definitions page 2With the help of the I2C Register library they can easily be represented
and manipulated.frompy_i2c_register.register_listimportRegisterListfrompy_i2c_register.registerimportRegister# Create RegisterList instance to hold registers, device's i2c address is 0x62controls=RegisterList(0x62,i2c,{})# Add a definition for an ACQ_COMMAND (Acquisition Command) register, address 0x00 with WRITE permissionscontrols.add("ACQ_COMMAND",0x00,Register.WRITE,{})\.add("ACQ_COMMAND",0,7,[0]*8)# Define the segment of bits to read with LSB index of 0 and MSB index of 7# Add a definition for a STATUS register, address 0x01 with READ permissionscontrols.add("STATUS",0x01,Register.READ,{})\# Define various individual Register Segments which each signify different parts of the status.add("PROC_ERROR_FLAG",6,6,[0])\.add("HEALTH_FLAG",5,5,[0])\.add("SECONDARY_RET_FLAG",4,4,[0])\.add("INVALID_SIGNAL_FLAG",3,3,[0])\.add("SIGNAL_OVERFLOW_FLAG",2,2,[0])\.add("REFERENCE_OVERFLOW_FLAG",1,1,[0])\.add("BUSY_FLAG",0,0,[0])# Add a definition for a VELOCITY register, address 0x09 with READ permissionscontrols.add("VELOCITY",0x09,Register.READ,{})\.add("VELOCITY",0,7,[0]*8)# Define the segment of bits to read for velocity value with LSB index of 0 and MSB index of 7# Super simple to read and write values# Set ACQ_COMMAND Register bits to value of 0x04, then write to registercontrols.set_bits_from_int("ACQ_COMMAND","ACQ_COMMAND",0x04,write_after=True)# Read STATUS register for BUSY_FLAG value and convert to an integerbusy=controls.to_int("STATUS","BUSY_FLAG",read_first=True)# Read VELOCITY register and convert to two's compliment integervelocity=controls.to_twos_comp_int("VELOCITY","VELOCITY",read_first=True)Systems OverviewThe main class this library provides is theRegisterListclass. This
class manages a list ofRegisterdefinitions. It also provides some
useful helper methods to make performing certain common actions quick
and easy.Creating a RegisterListTo create aRegisterListimport theregister_list.RegisterListclass. Then call the constructor giving it a I2C device address, anI2C
Object, and anyRegisters you have
already defined:frompy_i2c_register.register_listimportRegisterListcontrols=RegisterList(0x62,i2c,{})The provided I2C Device address will be used to contact the device which
holds the registers over I2C. TheI2C Objectdepends on your platform, see thedocumentationfor more information. In most
cases you can provide an emptyRegistermap as well.Defining RegistersAfter you create aRegisterListyou must define someRegisters
to control. ARegisteris defined by a name (for easy programmatic
access), an I2C address, and a string containing IO operation
permissions. TheRegisterListclass provides a usefuladd(reg_name, reg_addr, reg_permissions, reg_segments)method for
addingRegisters.frompy_i2c_register.registerimportRegistercontrols.add("REGISTER_NAME",0x00,Registers.READ+Register.WRITE,{})This would define aRegisterwith the nameREGISTER_NAME, the
address0x00and the permission to read and write to/from it.Adding RegisterSegmentsTo actually read or write to/from aRegisteryou need to define at
least oneRegisterSegment. These describe how bits read from
registers map to sub values. This could be useful if a device for
example: provides a health register and each bit represents a different
system’s health. You defineRegisterSegments by giving a name (for
easy programmatic access) and the index of the segment’s least and most
significant bits. The previously mentionedRegisterList.add()method
returns theRegisterthat it just created. You can then in turn use
a similar helper method thatRegisterprovides calledadd(seg_name, lsb_i, msb_i, default_bits):controls.add("HEALTH",0x00,Registers.READ,{})\.add("LEFT_MOTOR_FLAG",2,2,[0])\.add("RIGHT_MOTOR_FLAG",1,1,[0])\.add("NETWORK_FLAG",0,0,[0])This would define aRegisternamedHEALTHat address0x00with read permissions. ThisRegisterwould have 3RegisterSegments. These 3 register segments would look at bits 0,
1, and 2 for the status of the left and right motors as well as some
made up network module.Reading from RegisterSegmentsTheRegisterListprovides some useful helper methods for readingRegisterSegments as integer values. They are calledto_intandto_twos_comp_int. They both take the name of aRegisterandRegisterSegmentto read. Optionally you can pass aread_firstvalue. WhenTruethese methods will read theRegisteroff the
I2C device before returning theRegisterSegmentvalue:network_status=controls.to_int("HEALTH","NETWORK_FLAG",read_first=True)velocity=controls.to_twos_comp_int("VELOCITY","VELOCITY",read_first=True)This would read theNETWORK_FLAGsegment of theHEALTHregister
and theVELOCITYsegment of theVELOCITYregister.Ontop of usingRegisterLists helper methods one can access rawRegisterSegmentvalues via theRegisterSegment.bitsarray. This
array contains the raw0or1values of the register. Just be
sure to callRegister.readbefore accessing theRegisterSegment.bitsarray:controls.get("VELOCITY").read()velocity_bits=controls.get("VELOCITY").get("VELOCITY").bitsWriting to RegisterSegmentsTheRegisterListclass provides theset_bitsandset_bits_from_inthelper methods. Similar to the reading helper
methods mentioned aboveset_bitsandset_bits_from_intboth also
take aRegisterandRegisterSegmentname as their first two
parameters. The third value of both functions is the value to set. In
the case of theset_bitsmethod it is expected to be an array of
bits to set. In the case of theset_bits_from_intmethod it is
expected to be an integer value to set. Theset_bitsandset_bits_from_intmethods also offer an optionalwrite_afterflag. IfTruethey will write the value of theRegisterto the
I2C device after the value has been set.controls.set_bits("ACQ_COMMAND","ACQ_COMMAND",[0,0,0,0,0,1,0,0],write_after=True)controls.set_bits_from_int("ACQ_COMMAND","ACQ_COMMAND",0x04,write_after=True)This would set theACQ_COMMANDsegment of theACQ_COMMANDregister to the value0x04using theset_bitsandset_bits_from_intmethods.Writing Wrapper ClassesI2C Register’s simple architecture lends itself well to being used in
hardware wrapper classes. All one must do is create a class with its ownRegisterListinstance. Then addRegisterandRegisterSegmentdefinitions in the__init__()method:frompy_i2c_register.register_listimportRegisterListfrompy_i2c_register.registerimportRegisterclassLidarLiteV3():# Register and Segment name constantsREG_ACQ_COMMAND="ACQ_COMMAND"SEG_ACQ_COMMAND=REG_ACQ_COMMANDREG_STATUS="STATUS"SEG_PROC_ERROR_FLAG="PROC_ERROR_FLAG"SEG_HEALTH_FLAG="HEALTH_FLAG"SEG_SECONDARY_RET_FLAG="SECONDARY_RET_FLAG"SEG_INVALID_SIGNAL_FLAG="INVALID_SIGNAL_FLAG"SEG_SIGNAL_OVERFLOW_FLAG="SIGNAL_OVERFLOW_FLAG"SEG_REFERENCE_OVERFLOW_FLAG="REFERENCE_OVERFLOW_FLAG"SEG_BUSY_FLAG="BUSY_FLAG"REG_VELOCITY="VELOCITY"SEG_VELOCITY=REG_VELOCITYREG_DISTANCE="DISTANCE"SEG_DISTANCE=REG_DISTANCEdef__init__(self):# Create some device specific I2C Objectself.i2c=...# Configure control registersself.controls=RegisterList(0x62,self.i2c,{})self.controls.add(LightLiteV3.REG_ACQ_COMMAND,0x00,Register.WRITE,{})\.add(LightLiteV3.SEG_ACQ_COMMAND,0,7,[0]*8)self.controls.add(LightLiteV3.REG_STATUS,0x01,Register.READ,{})\.add(LightLiteV3.SEG_PROC_ERROR_FLAG,6,6,[0])\.add(LightLiteV3.SEG_HEALTH_FLAG,5,5,[0])\.add(LightLiteV3.SEG_SECONDARY_RET_FLAG,4,4,[0])\.add(LightLiteV3.SEG_INVALID_SIGNAL_FLAG,3,3,[0])\.add(LightLiteV3.SEG_SIGNAL_OVERFLOW_FLAG,2,2,[0])\.add(LightLiteV3.SEG_REFERENCE_OVERFLOW_FLAG,1,1,[0])\.add(LightLiteV3.SEG_BUSY_FLAG,0,0,[0])self.controls.add(LightLiteV3.REG_VELOCITY,0x09,Register.READ,{})\.add(LightLiteV3.SEG_VELOCITY,0,7,[0]*8)self.controls.add(LightLiteV3.REG_DISTANCE,0x8f,Register.READ,{})\.add(LightLiteV3.SEG_DISTANCE,0,15,[0]*16)# Provide useful helper methodsdefmeasure(self):self.controls.set_bits_from_int(LidarLiteV3.REG_ACQ_COMMAND,LidarLiteV3.SEG_ACQ_COMMAND,0x04,write_after=True)defdistance(self):returnself.controls.to_int(LidarLiteV3.REG_DISTANCE,LidarLiteV3.SEG_DISTANCE,read_first=True)defvelocity(self):returnself.controls.to_int(LidarLiteV3.REG_VELOCITY,LidarLiteV3.SEG_VELOCITY,read_first=True)# Now using your hardware has never been easierlidar=LidarLiteV3()whileTrue:lidar.measure()print("Car is going{}m/s when it was{}m away".format(lidar.velocity(),lidar.distance()))DevelopmentThe code for I2C Register is located in thepy_i2c_registerdirectory. Feel free to contribute by opening a pull request. I try to
test and document as much as I can.Supported Python Versions: 2.7, 3.6Running TestsTo run tests a couple python packages are required. To install them you
can run thetest-installMake target:maketest-installYou can then run test by executing thetestMake target:maketestTo see a more detailed HTML report you can run thetest-htmlMake
target. The results will then be saved tohtmlcov/index.html.DistributionThis repository provides a PIP package calledpy-i2c-register. To
publish this distribution a variety of helpers are provided in the
Makefile.SetupThePandoctool is required for the release
process along with some miscellaneous Python packages. Please refer to
thePandoc Websitefor
installation instructions. You can install the misc. Python packages
with thedist-installMake target:makedist-installYou can verify that all distribution dependencies are install and
accessible by running thedist-checkMake target. If it exits
successfully all dependencies were found.Finally you must create.pypircfile in your home directory with the
contents:[distutils]
index-servers=pypi

[pypi]
repository = https://upload.pypi.org/legacy/
username = Your Username
password = Your PasswordThis gives the PyPi release tool some basic configuration options and
your credentials.StepsThis section details the steps required to release this package.TestEnsure that all tests pass by running thetestMake target:maketestClean and buildClean up previous distribution materials by running thedist-cleanMake target:makedist-cleanBuild the distribution by running thedist-buildMake target:makedist-buildUploadUpload the distribution to PyPi by running thedist-uploadMake target:makedist-uploadThis requires that you have a.pypircfile setup with your
username and passwordThe Makefile provides a useful target which runs steps 1 and 2 under one
command nameddist. However the upload step must still be completed
separately.