I2C is a two wire bus developed by Phillips originally for the inside of TVs. It is low performance, low distances but simple and very cheap. This has been discovered also by others to hold plug and play info, read temperature and so on. I2C has a master host interface and various sensors. On the motherboard there can be more than one host, resulting in having more than one bus. Usually the host is the master and the other devices are slaves, however be aware, also slave devices can act as masters.
SMbus (System Management bus) defined by Intel is derived from the I2C bus with additional specifications, restrictions and rules. It introduced PEC and CRC checks.
i2cdetect -l will show what interfaces are
Many evolutionary steps have been made to I2C and this causes some incompatibilities. I2C is meant for device internal communications where the configurations is roughly known. Therefore using it as plug and play might cause some misinterpretations, confusions and failures.
The simplest way is using I2C to send or receive a stream of bits into a device (usually memory devices). Such chips can be read and written as files, whereas the file is /dev/i2c-
and the chip I2C address is set by the files ioctl function call. <nn>
Since devices behaving this way are limited, a new approach got introduced to address registers in a device. Devices supporting registers can be used with the command i2cset and i2cget. There is a pointer register that can be written as usual. To read it or read/write to other registers the device considers the content of this pointer register as the register address. On write access, the bytes following the pointer register end in the register selected by the pointer register. To read, two accesses are necessary write to pointer register, then read from what is selected by the pointer register.
Be aware that there are mainly two I2C bus behaviors, devices that use a pointer registers and devices that don't.
Register access commands can cause disastrous effects on devices that do not have a pointer register as eeproms. When not taking care eeproms can loose their contents, and if this happens on the eeprom of a DIMM, a PC might even permanently refuse to boot afterwards.
There is the python library smbus2 that can be used for all kinds of adapters. It promises that the software can be written once and then be used for all the different I2C hardware. Unfortunately this is not the case hardware and also their drivers might not support all the features and also behave differently. To be on the safe side the software should read the hardware type and just support known and tested devices.
For the hardware hackers, the parallel port can be used as I2C host. So you can extend the I2C bus so it leaves the PC. Build a kernel to:
modprobe i2c-parport type=3
for the ELV adapter. The ELV adapter is the only well documented and identified adapter in
/usr/src/linux/Documentation/i2c/busses/i2c-parport
. Check dmesg if
the driver could grant
access to the parallel port.
You might have a conflict with the driver lp for the printer, so compile the kernel with lp as
module. This driver is poor on the hardware side but supports all modes as i2cdetect -F<adapter no>
shows.
To not risk a conflict with the parallel port check the simple USB adapter http://www.harbaum.org/till/i2c_tiny_usb/index.shtml.
Be aware it is just a USB 1 device so it will not run on USB 2.0
Load its driver by
modprobe i2c-tiny-usb comes with the Linux sources and gets compiled when
CONFIG_I2C_TINY_USB is set. cat /usr/src/linux/.config | grep TINY
shows if you have it. i2cdetect -l
should show it including its I2C bus number i2c-4 i2c i2c-tiny-usb at bus 001 device 013 I2C adapter
. On this example it is I2C bus number 4 attached to USB bus number 1 where it is device number 13.
Unfortunately this driver does not support all functions as i2cdetect -F <adapter no>
shows.
The kernel needs to have a driver, there are two drivers that could be used for that.
CONFIG_HID_MCP2221 gets the hid_mcp2221 driver and creates /dev/i2c-
. This is the usual way. But there is also a generic hid driver (human interface driver) hid_generic that is used for all kinds of human interfaces as mouse and keyboard. <x>
What driver to be used depends on the software that wants to access the mpc2221 chip.
If hid_mcp2221 gets loaded but is not desirable it can be unloaded by rmmod hid_mcp2221 (compiled as module). dmesg will confirm it and show that the generic driver takes over creating a /dev/hidraw
device. Putting <n>
blacklist hid_mcp2221
to /etc/modprobe.d/ blacklist.conf it will be prevented to be loaded again. Alternatively build a kernel without hid_mcp2221.
If not blacklisted, modprobe hid_mpc2221 will load the driver again and dmesg will show the removal of the generic /dev/hidraw
device.<n>
i2cdetect -l should show it as
i2c-5 i2c MCP2221 usb-i2c bridge I2C adapter
with the functionalities as i2cdetect -F <adapter no>
:
To have it accessible to the users in the i2c group add a udev rule as /etc/udev/rules.d/10-mpc2221.rules
SUBSYSTEMS=="usb", ATTR{name}=="MCP2221 usb-i2c bridge", GROUP="i2c"
and then udevadm control --reload-rules and plug in and out the i2c adapter to have udev doing its work.
The device can be configured via USB-HID, there is a Utility program available for windows that allows to change the settings. Settings are stored in flash and copied over to SRAM. There are security options available as write just after password or write once and then be lock for further writes.
The general Purpose bits need to be set for certain setting, for ADC and DAC this is obvious but not for the clock settings. Clock settings for I2C can just be done if the clock reference output is put to the GP1 pin.
The mcp2221 has also support in Linux.
Since it is a HID device it can be accessed using hid https://trezor.github.io/cython-hidapi/api.html
https://easymcp2221.readthedocs.io/en/latest/install.html uses hidapi to get all kinds of support for the mcp2221 even a a python based chip utility program that does a similar job as the one from microchip but running under Linux as well https://github.com/electronicayciencia/EasyMCP2221-GUI. For Gentoo Linux just start EasyMCP2221-workbench.pyw
There are also a python libraries as https://github.com/vpaeder/pymcp2221, https://github.com/nonNoise/PyMCP2221A, https://github.com/pilotak/python-mcp2221
An even more easy way is using a spare VGA (or monitor) socket of your PC. On a analog VGA connector Pin 10 is ground, pin 12 SDA and pin 15 SCL. To get power the +5V from pin 9 can be used carefully as long not too much current is required.
Too high current or a short to ground might damage something on the motherboard. A Acer Aspire One note book motherboard has a small SMD diode in the range of 100mA that supplies the required 5V. Larger current will blow this diode.
The graphic cards i2c driver as modprobe i2c-nforce2 or modprobe i2c-i801 can then be used.
The system could be configured in a way that just root has access to the i2c adapters as ls -l /dev/i2c-* shows
Most of the i2c adapters are used for the system and as mentioned i2c is not plug and play so i2c chips could be confused and this even permanently. However if having an external i2c adapter with some sensor chips it is highly recommended not running as your application as root.
The way out is giving group access to the device file and writing a udev rule /etc/udev/rules.d/10-i2c.rules
as for the i2c-tiny-usb adapter.
It is not recommended to give access to any i2c adapter available, since wrong i2c writes to i2c or smbus devices on the motherboard could end up in a disaster
The cleanest way is creating a i2c group, (in the past the uucp "Unix-to-Unix Copy Protocol" group has also be taken). Check if the i2c group exists: getent group i2c
If not create it groupadd i2c
Add the user to the i2c group: usermod -aG i2c <username>
and log out and log in
The rule allows the uucp group that is also used for serial connections to access this i2c adapter:
SUBSYSTEMS=="usb", ATTRS{product}=="i2c-tiny-usb", GROUP="i2c"
Then udevadm control --reload-rules and plug in and out the i2c adapter to have udev doing its work or udevadm trigger
A way to find the i2c-tiny-usb is trying to open sequentially all /dev/i2c-*
until the desired chip attached to the i2c-tiny-usb host adapter is found. The software doing this should distinguish between having access to the i2c adapter or if open fails due to not existing host adapter.
There are newer and more advanced chips around as the LM92. Those chips behave similar but have other i2C addresses and need other formulas to convert the raw value to °C.
The LM75 is a temperature sensor chip using the dev-interface the chip data sheet must be known. The access works then using the i2c calls.
To see how many i2c bus master you have
i2cdetect -l
The i2c tiny adapter has been detected as i2c bus number 0.
i2c-0 i2c i2c-tiny-usb at bus 004 device 003 I2C adapter
Check what is attached to i2c bus number 0 by scanning through the addresses
i2cdetect <bus>
Check if something is found. If UU appears the device might be there but busy, so unload the driver (e.g rmmod lm75) and redo the scanning. Under 0x47 a chip is found. The address of the LM75 chip can be 0x48 for the fist chip (chip no 1) to 0x4f for the last chip (chip no 8). If all address pins of the chip are wired to Vcc the address is 0x4f. Check the registers of the device found
i2cdump <bus>
0x4f
A device can have 256 registers that are selected by the 8 bit wide pointer register.
Knowing that is a LM75 chip and looking at its data sheet, shows the chip has in its 8bit wide pointer register just 3 bits implemented. This explains why the bytes read by i2cdump 0 0x4f repeat every 8Bytes.
Instead of dumping everything individual registers can be read:
i2cget <bus> <chip> <register>
The temperature is inside register 0x00
i2cget <bus>
0x4f 0x00
When sure what to do, avoid the question and confirmation
i2cget -y <bus>
0x4f 0x00
Just 8 bits are read, but the temperature register contains 16 bit. Therefore the i2cget program needs to be called as follows:
i2cget -y <bus>
0x4f 0x00 w
0x801c
or using Packet Error Checking (PEC) check if it is supported by your adapter i2cdetect -F 0
i2cget -y <bus>
0x4f 0x00 w p
hold the finger on the chip to heat it up and getting:
0x8025
Or to have it hands-free
watch i2cget -y <bus>
0x4f 0x00 w
Just the upper 9 bits contain the two's complement temperature where the least significant bit represents a step of 0.5°C and when set, the most significant indicates below 0°C. The i2cget puts low byte first. It is actually better to read just 8 bits since the result is exactly the temperature in °C (but hex)
The chip has other registers to control a temperature alarm output. Those registers can be read. The alarm off register:
i2cget -y <bus>
0x4f 0x02 w
0x004b
= 75°C = 0x4b and the default value after reset
The alarm on register:
i2cget -y <bus>
0x4f 0x03 w
0x0050
= 80°C 0 0x50 and the default value after reset
There is the kernel device driver lm75 documented /usr/src/linux-*-gentoo/Documentation/hwmon/lm75.txt
that creates the interface. modprobe lm75 and lsmod and to see where it appears in /sys
do tree /sys/bus/i2c/ or more direct tree /sys/module/lm75 this is the driver and not the chip. To access the chip the bus number used on the adapter as found with i2cdetect -l (in the example 10) and chip address as found with i2cdetect 10 (in the example 4f) need to be known. To see the temperature in m°C
cat /sys/module/lm75/drivers/i2c\:lm75/10-004f/temp1_input
The max temperature register can be set as
echo 60000 > /sys/module/lm75/drivers/i2c\:lm75/10-004f/temp1_max
The MLX90614 chip from Melexis is a Infra Red Thermometer in TO-39 case with 4 pins that can be directly connected to an external i2c bus.
Check what i2c buses you have i2cdetect -l
Check if the chip is there under its default address 0x5a i2cdetect <bus number>
Other chips having the same i2c address might be there. To be sure this is not the case a the internal EEPROM where the bus address is configured can be read (writing to this address is also possible to program a chips with different addresses to be used on the same i2c bus) i2cget -y <bus number>
0x5a 0x2e. If the response is 0x5a then the chances are high that the chip has been found and is identified. In the datasheet the address for this i2c is on address 0x0e and not 0x2e, this is correct since the chip has a RAM inside starting at address 0x00 and the EEPROM has an offset of 0x20.
Operating the chip gets easy i2cget -y <bus number>
0x5a 0x06 gets the hex value for the ambient temperature and i2cget -y <bus number>
0x5a 0x07 gets the hex value for the object temperature, this is the infrared radiation passing through the chips window. To get the temperatures in Kelvin those two hex values need to be multiplied with 0.02K. 273.15 need then to be subtracted to get °C. The sensor is able to measure temperatures from -70.01°C to + 382.19°C with a resolution of 0.02°C.
The 256Byte 24LC02 contains also pins that look as to set the I2C bus address, but those pins are fake and internally not connected. Other devices as the 24C01and the 24C02 use all those pins, the 24C04 uses just the upper two, the and 24C08 just the upper most and finally the 24C16 uses none of them. The device can be addressed using the range 0x50..05f. To read from the device the internal address counter has to be set. This is done by a write without data. (If possible wire the WP pin high to have the chip write protected, so accidentally writes to not damage the data inside). After that it can be read from the device, and with every read the address counters increases and the next byte appears.
First check witch of the many i2c buses that you have has the memory chip attached:
i2cdetect -l
i2cset and i2cget are not suited to read from the memory device since they assume that there is a pointer register, however memory devices use an pointer register free way of accessing them.
Memory devices can be read more direct and faster. However this require a different behavior and bring also danger, in case i2c sensor software accidentally write to a eeprom. The program eeprog from http://www.codesink.org/eeprog.html can be used to read and write i2c devices.
eeprog -xf /dev/i2c-0 0x50 -r 0:256
and to write, as example what data returns goes into the device at address 0.
date | eeprog -f /dev/i2c-0 0x50 -w 0
The package i2c-tools from version 3.1.0 comes also with tools to program eeproms
Some small SMD I2C memory devices come in a 5pin package and illustrating therefore how simple and cheap a I2C interface is. Data transfer is done using a data line SDA and a clock line SCL.
To avoid bus contention when two devices want to drive the two lines SDA and SCL (I2C allows more than one bus master) the devices can just sink current and the sourcing of the high level is done using pull up resistors.
Therefore in idle condition both signals have a high level.
Watch out I2C might use different voltages 3V or 5V. Connection a 3.3V device to a 5V I2C master will probably damage the 3.3V chip.
To transmit a single bit on the SDA line i2c uses the following definitions:
When SCL is high SDA has to be stable and can be read.
When SCL is low SDA can change.
MSB will be sent first.
To transmit single bits is not enough, a framing is required to tell when a Byte begins and it even has to tell when a frame (combination of many Byte transfers) terminates.
The framing is done by violating the regular bit transfer rules, where SDA is not allowed to change when SCL is high. Therefore the following conditions are possible:
Changing SDA to low when SCL is high resulting as start => after bus idle
Changing SDA to high when SCL is high resulting as stop => after transfer, bus returns to idle
This results that the idle bus has both lines high (SDA and SCL). An un-powered bus has therefore both lines low.
From the idle condition with both signals high, the i2c master sets the SDA line to low and indicates that a transfer continuous. Now SCL goes low and the master can put the first data bit (most significant) on the bus and continuously transmit the bits. After the last bit has been transferred SCL goes low and the device having received the data has to acknowledge the transfer by putting a zero to SDA.
Bytes are acknowledged when received
When the receiving node does not acknowledge the byte transfer, the SDA line stays high. After rising SCL, no stop condition can occur since the SDA line is already high. Therefore the master has to react, it can (violating the rule that data must be stable while clock is high) and repeat a new start condition to start a new transfer.
After the acknowledge bit the master can send the next byte or keep the SDA low raise the SCL and then raise SDA to create a stop condition.
Slaves can slow down the master by additionally pulling down the SCL, after a low SCL is detected.
It can happen, that communication stops in the middle of a transfer. To not block the bus forever, after a timeout a master can re-initiate the communication. This puts some minimum clock speed to the devices.
Since I2C allows more than one master on the bus and both lines have pull up resistor, it may happen that when a master want to put 1 on the bus, a 0 remains, since some other devices are active. This can be other masters, but also slaves can slow down the clock signals like that. Therefore a master must read back what it has sent.
Since I2C is a multi device bidirectional bus frames containing multiple Bytes are necessary.
Frames start with a start condition and stop with a stop condition, therefore between start and stop condition multiple bytes can be sent.
Addressing is necessary to select the destination devices. The most significant 7bits of first byte put on the bus indicate therefore the address of the chip. The last significant bit indicates:
When zero a write access
When one a read access
This limits the i2c to maximum 127 devices.
Usually the address is fixed assigned to the chip, but to allow more than one chip of the same type to be used on the same i2C bus, some lower address bits are often fed to pins to add an offset to the address.
Obviously every bus device needs it own address. The http://www.smbus.org/ defines how those addresses have to be used and Phillips the inventor of I2C has also such a list.
There is even a address resolution protocol defined when addressing gets complicated.
This is straight forward, after the address the second byte holds the data. Depending on the read/write bit in the first byte the master provides it and makes a write or it comes from the slave to be read by the master. Basically the read write bit says who has to put the data to the bus. Write means the master read means the slave.
Write/Read byte is not the same as Send/Receive bits.
Send/Receive bits goes to or comes from the device, but usually a device is not just a pipe where bits can be stuffed in or taken from.
The write/read byte has therefore as second byte a command code, and the third byte is the data. The write/read word command has a forth byte containing the high byte of the word.
Often something is sent to a device and then a response is requested. The process call combines a write word command with a read word command less version and without a stop condition between them. The slave address is sent twice but the command just once.
When more than a word needs to be written, the third bytes holds the number of bytes, and then the data bytes follow. However the block read sequence is different. After the command code and other start bit is set followed by the slave address and then the count byte and data.
I2C frames usually hold just the destination address, but in some cases also the source address is required, so the receiving device knows who wants something from him.
Therefore the first byte holds the SMBus host address and a write bit. The second is the master address followed by two data bytes holding a 16 bit status. Whit this message SMBus devices can become masters and the SMBus host acts as slave.
Linux offers two ways of accessing the devices on the I2C bus:
Using a kernel driver as /usr/src/linux/drivers/hwmon/lm75.h
that matches the chip. This driver needs to be added to the ic2-core device driver and see /usr/src/linux/Documentation/i2c/instantiating-devices.rst
access is then done via /sys/devices
Use the dev-interface to get the routines to access the chip that can be used in a C program /usr/src/linux/Documentation/i2c/dev-interface.rst
. The i2c-dev driver will access the i2c-core driver.
See http://venkateshabbarapu.blogspot.de/2012/11/i2c-driver-in-linux.html for a good overview
lspci | grep SMBus shows what i2c adapters are available on pci.
Create a kernel with support for it plus enable i2c-dev. Check if the files under /dev
get created ls /dev/i2c*
The i2c-tools package comes with program i2cdetect -l lets you all adapters available.
An alternative is:
cat /sys/class/i2c-adapter/i2c-*/name
If there are problems check with ls -l /dev/i2c* if device files are there. To check the functionality of the <n>
i2c host adapter i2cdetect -F <n>
, this list gives also a good overview of all access modes i2c supports
The packages lm_sensors is mainly targeted to i2c chips on the motherboard.
A lot of device drivers are involved when it comes to i2c. The i2c-core driver communicates to the i2c host hardware via other kernel drivers. In case of a parallel port i2c adapter i2c-core communicates to i2c-algo-bit that actually does bit banging. i2c-algo-bit then might call i2c-philips-par the device driver that philips proposed and this then calls the device driver parport a abstraction layer of all parports and finally on a PC the device driver parport_pc is called that then accesses the hardware. Finally there is either i2c-dev to access the devices using i2c commands or a device driver as LM75 that creates the data under /sys
Run sensors-detect to find out what chips are available and what kernel modules are required. It creates as output /etc/modules-load.d/lm_sensors.conf
holding i2c drivers as fam15h_power or k10temp. Build or check if the kernel has been build with those modules (build them as modules). Reboot and check with lsmod if the drivers are there.
It might happen, that some drivers get loaded in an other way before the lm_sensors.conf
file gets processed. This creates then errors since the drivers are already loaded. In this case comment those drivers in lm_sensors.conf
Add it to the rc-update add lm_sensors default
/etc/init.d/modules restart will produce errors for all kernel drivers missing. Now build a kernel with the missing drivers.
Type sensors to see what data can be get