|Pi IoT In Python Using Linux Drivers - I2C|
|Written by Harry Fairhead & Mike James|
|Monday, 01 November 2021|
Page 1 of 4
I2C is an important interface for many devices and Linux has a driver that means that you can use it worrying about the exact details of the hardware - if you know how.
This content comes from our newly published book:
Raspberry Pi IoT In Python Using Linux Drivers
By Harry Fairhead & Mike James
Buy from Amazon.
There are many specific device drivers which make use of the I2C bus and, as in the case of the SPI bus, there is usually a choice of hand-coding the interaction using the I2C driver or using the specific device driver. In this chapter we will look at the basics of making use of the driver for BSC1 which works on all versions of the Pi.
Enabling The Driver
To make use of the Linux I2C driver you have to enable it by adding dtparam=i2c_arm=on to the /boot/config.txt file.
Alternatively you can load it dynamically:
def checkI2CBus(): temp = subprocess.Popen(["sudo", "dtparam", "-l"], stdout = subprocess.PIPE) output = str(temp.communicate()) print(output) lasti2c=output.rfind("i2c_arm") if lasti2c!=-1: lasti2c=output.find("i2c_arm=on",lasti2c) if lasti2c==-1: temp = subprocess.Popen(["sudo",
This is slightly different to the earlier driver loading functions. The first part of the function uses dtparam -1 to get a list of loaded overlays. If it finds ic2_arm=on as the last ic2_arm overlay then it does nothing. If it doesn’t find it then it activates the overlay. As you can also use ic2_arm=off and it is the last overlay that controls the state of the system we need to find the last occurrence of ic2_arm and make sure it is “=on”.
Both actions enable BSC1 and create a device file:
You can check that the driver has been installed using:
which will return a list of I2C devices.
Using The I2C Driver From Python
As is the case for all Linux devices, the I2C device, /dev/i2c-x where x is the I2C bus number, looks like a file. You can do a block read by simply opening the file for reading and reading a list of bytes:
import io fdr = io.open("/dev/i2c-1", "rb", buffering=0) data=fdr.read(n)
This reads a maximum of n bytes of data and returns it as a list of bytes. The only problem is how do you specify the address of the device? This looks complicated but it isn’t. You need to use standard Linux ioctl functions to send a command to the device. In the case of the I2C driver, the most important is:
This is used to set the address of the slave that subsequent read and writes apply to. So to set the address of the device you want to read from to 0x40 you would use:
import fcntl fcntl.ioctl(fdr, I2C_SLAVE, 0x40)
Finally to reset the hardware and return all GPIO lines to their default modes you have to close the file:
Putting all of this together, a block read is:
import io import fcntl I2C_SLAVE=0x0703 fdr = io.open("/dev/i2c-1", "rb", buffering=0) fcntl.ioctl(fdr, I2C_SLAVE, 0x40) data=fdr.read(n) fdr.close()
By default bits not are sent between each byte read.
Writing data follows the same pattern:
import io import fcntl I2C_SLAVE=0x0703 fdw = io.open("/dev/i2c-1", "wb", buffering=0) fcntl.ioctl(fdw, I2C_SLAVE, 0x40) fdw.write( bytearray([0xE7,0x01,0x02])) fdw.close()
As in the case of a read, a stop bit is only sent at the end of the block of data.
If you try these programs out you will discover that the I2C clock frequency is the default 100kHz. You can’t change the clock frequency dynamically, but you can add:
to the /boot/config.txt file and after a reboot the I2C clock will be set to the frequency specified as baudrate. The baud rate is simply the clock speed in Hz.
Notice that the I2C clock speed depends on the core clock rate and this can be slower than the maximum possible when the device is idling or under heat pressure. To run at the fastest clock speed even when idling, use the command:
sudo sh -c "echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
If you find using two file objects irritating then you can use the os module to open a Linux device descriptor file. For example, in:
import os import fcntl I2C_SLAVE=0x0703 i2cfd = os.open("/dev/i2c-1", os.O_RDWR | os.O_SYNC) fcntl.ioctl(i2cfd, I2C_SLAVE, 0x40) data=os.read(i2cfd,n) os.write(i2cfd,data)
reading and writing is performed on a single file.
|Last Updated ( Wednesday, 23 November 2022 )|