Using I2C in Linux using C (i2cdev)

I2C or Inter IC Communication is a very commonly found low-speed communication method used in embedded systems. The biggest advantages of I2C are the sheer ease of implementation, widespread adoption by semiconductor manufacturers and the ability to easily add multiple nodes on the same bus allowing for more efficient designs (and maybe even more cost-efficient).

I2C was defined by Philips Semiconductors which later was acquired by NXP semiconductors. I2C is basically a master-slave(s) bus and is purely half-duplex. Also, it is important to know that a master can talk to only one slave at a time.

Perhaps one of the very few criticisms of this bus (if any) is that one bad slave can result in a stalled bus for everyone – master included! One of the ways this can happen is that the bus can be “pulled LOW” by one of the slaves while it is carrying out some internal operations and then never recovers from this condition due to some bad state machine implementation.

However, all said and done, I2C is by far the bus of choice for interfacing sensors like accelerometers, gyroscopes, magnetometers, temperature, humidity, etc. as well as other ICs like EEPROM memories, audio codes, CMOS sensors, etc. For the latter categories, I2C is used purely to configure the IC for their main operation. For example, in a typical audio codec, the master would use the I2C bus to configure the codec type, channels used, data format, etc. and then would “enable” the codec operation.

A Typical I2C Communication

The I2C bus is very simple to understand.

The very first event on the bus is a START condition. A start condition is defined as the data line going LOW while the SCL line is still HIGH.

The event that signals a close to a particular I2C transaction is a STOP condition. A stop condition is defined as the data line going HIGH while the SCL line is still HIGH.

There is also an event called a repeated START which is similar to the start condition but the only difference is that it happens before any STOP condition has occurred. A repeated START helps the master to avoid a STOP-then-START sequence which can be wasteful in case of a long communication session.

Start and Stop conditions on I2C bus

At a protocol level, the master starts a communication with a slave by using the slave’s address (7-bit or 10-bit). The master also specifies if this is a Read or a Write operation using a single bit following the address. If the R/W bit is LOW, it is a Write operation and if the R/W bit is HIGH, it is a Read operation.

The next bit is called the ACK/NACK bit. This is used by the master to ascertain that the intended slave i.e. whose address was sent on the bus is able to respond to it. The master releases the data line and if the slave is able to pull the data line LOW, it is an ACK. If not, then the line remains HIGH and the master considers this a NACK.

Below is an example of a write operation to the I2C slave.

The master sends the address, followed by two writes to the I2C slave

Similarly, below is an example of a read operation to the I2C slave. The master first specifies the address within the I2C slave from where it wants to read the content and then sends the read transaction. Notice the change in the R/W bit and also, use of the repeated START condition.

Notice the change in the R/W bit and the repeated start that avoids the stop-then-start sequence

I2C in Linux

I2Cdev is a very common user-space framework in linux that makes using I2C a breeze! This is in line with the philosophy of linux – heavy abstraction, modularity and portability!

Would you like to learn about i2cdev, its usage and how you can do a simple I2C implementation in linux using i2cdev? Check out this video on our YouTube channel!

The GitHub link for the temperature sensor read (TI TMP006) application used in the video is located here:

https://github.com/sckulkarni246/ke-rpi-samples/blob/main/i2c-c-ioctl/i2c_ioctl_tmp006.c

Leave a Reply