Inter-Integrated Circuit (I²C)

What is it?

I²C (spoken as I-Squared-C) is a bus originally invented by Philips. It is designed as a classic master-slave bus. A data transfer is always i nitiated by a master. It can also be set up in a multi-master system. I²C is connected via two signal lines (data line and clock line). The transmission rate of the bus can be between 0.1 Mbit/s up to 3.4 Mbit/s depending on the clock rate. If only a unidirectional connection is required, even 5.0 Mbit/s would be possible. It should be noted: the higher the clock rate, the more susceptible to failure the overall system becomes. The low operating voltage of only 3.3V does not contribute to interference resistance either.

Uses

I²C is mainly used for communication between microcontrollers. The advantage that a whole series of microcontrollers can be controlled via just 2 lines is of course very interesting for the circuit board layout. The main advantages of I²C are its simplicity. There are certainly newer bus systems with better transmission rates. Hardly any bus system is as easy to use as I²C. Even “hot plugging”, ie plugging in and unplugging the devices during operation, is possible with I²C.

Addressing

I²C uses an address space of 7 bits. This allows up to 112 nodes on one bus. The remaining 16 addresses are reserved for special applications. Usually the address of a device is defined directly by the manufacturer. It can therefore be found in the relevant data sheets. Due to the shortage of addresses, there is also a variant with a 10-bit address space. Up to 1136 nodes are possible, and the protocol is compatible with the smaller 7-bit address space.

Transfer rates

ModeMax. transfer rateDirection
Standard Mode0.1 Mbit/sbidirektional
Fast Mode0.4 Mbit/sbidirektional
Fast Mode Plus1.0 Mbit/sbidirektional
High Speed Mode3.4 Mbit/sbidirektional
Ultra Fast-mode5.0 Mbit/sunidirektional

Additional information

Please be aware there are some hardware issues when using the Raspberry Pi with devices that expect to be able to use clock stretching, for more info see “Adventures in I2C: clock stretching on the Raspberry Pi” and “I2C stretch bug. Been fixed or not?".

Code example

The following code shows setting the pins on a TCA 9534 which can be found on “Sequent Microsystems”

import com.pi4j.Pi4J;
import com.pi4j.context.Context;
import com.pi4j.io.i2c.I2C;
import com.pi4j.io.i2c.I2CConfig;
import com.pi4j.io.i2c.I2CProvider;

public class SimpleTca9534I2cTest {

	private static final byte TCA9534_REG_ADDR_OUT_PORT = 0x01;
	private static final byte TCA9534_REG_ADDR_CFG = 0x03;

	public static void main(String[] args) throws Exception {

		Context pi4j = Pi4J.newAutoContext();
		I2CProvider i2CProvider = pi4j.provider("linuxfs-i2c");
		I2CConfig i2cConfig = I2C.newConfigBuilder(pi4j).id("TCA9534").bus(1).device(0x3f).build();
		try (I2C tca9534Dev = i2CProvider.create(i2cConfig)) {

			int config = tca9534Dev.readRegister(TCA9534_REG_ADDR_CFG);
			if (config < 0)
				throw new IllegalStateException(
						"Failed to read configuration from address 0x" + String.format("%02x", TCA9534_REG_ADDR_CFG));

			byte currentState = (byte) tca9534Dev.readRegister(TCA9534_REG_ADDR_OUT_PORT);

			if (config != 0x00) {
				System.out.println("TCA9534 is not configured as OUTPUT, setting register 0x" + String
						.format("%02x", TCA9534_REG_ADDR_CFG) + " to 0x00");
				currentState = 0x00;
				tca9534Dev.writeRegister(TCA9534_REG_ADDR_OUT_PORT, currentState);
				tca9534Dev.writeRegister(TCA9534_REG_ADDR_CFG, (byte) 0x00);
			}

			// bit 8, is pin 1 on the board itself, so set pins in reverse:
			currentState = setPin(currentState, 8, tca9534Dev, true);
			Thread.sleep(500L);
			currentState = setPin(currentState, 8, tca9534Dev, false);
			Thread.sleep(500L);

			currentState = setPin(currentState, 7, tca9534Dev, true);
			Thread.sleep(500L);
			currentState = setPin(currentState, 7, tca9534Dev, false);
			Thread.sleep(500L);
		}
	}

	public static byte setPin(byte currentState, int pin, I2C tca9534Dev, boolean high) {
		byte newState;
		if (high)
			newState = (byte) (currentState | (1 << pin));
		else
			newState = (byte) (currentState & ~(1 << pin));

		System.out.println("Setting TCA9534 to new state " + asBinary(newState));
		tca9534Dev.writeRegister(TCA9534_REG_ADDR_OUT_PORT, newState);
		return newState;
	}

	public static String asBinary(byte b) {

		StringBuilder sb = new StringBuilder();

		sb.append(((b >>> 7) & 1));
		sb.append(((b >>> 6) & 1));
		sb.append(((b >>> 5) & 1));
		sb.append(((b >>> 4) & 1));
		sb.append(((b >>> 3) & 1));
		sb.append(((b >>> 2) & 1));
		sb.append(((b >>> 1) & 1));
		sb.append(((b >>> 0) & 1));

		return sb.toString();
	}
}