04 Jun 2020

An introduction to I²C

I²C is a protocol which defines a bus used for the communication between multiple integrated circuits, such as processors and sensors. In this context a bus refers to a connection between multiple entitys using only one shared communication channel.

The devices connected to an I²C bus are differentiated by their type: either master or slave. Only master devices are allowed to start a transaction on the bus, during such a transaction they either write to a slave device or read from a slave by requesting data. Typically processors are master devices and sensors slave devices. A device is identified by an 7-bit number which is called the address, thus there can be a maximum of devices on a single bus (the address 0 is reserved for broadcast messages).

Architecture of an I²C Bus

For the actual transmission of data I²C relies on a serial communication scheme, this means that the data is transmitted bit after bit over the connection, the contrary principle would be parallel communication where multiple bits are transfered at once using multiple data lines. The single data line of I²C is typically called SDA (Serial Data). Additionally there is a second line, on which a clock signal is transmitted synchronous to the data. This second line provides a synchronization between the devices. The clock line is typically called SCL (Serial Clock).

The Protocol

Nearly all application are using a single master. Thus this explanation is limited to single bus systems, this removes the challenges that occur when multiple masters try to access the bus at the same time.

To signal events during transaction special signals are used. The next section explains how the signals are represented as actual electronic signals in hardware, for now we will just have a look at the protocol. The required signals are:

  • Start: Is emitted by the master to signal the beginning of a new transaction
  • Stop: Is emitted by the master to signal the end of a transaction
  • Repeated Start: A start when no stop has been sent before, is used when the transaction mode (read or write) changes
  • Acknowledge (ACK): Is sent by the receiving device to signal that data has been received and the device is ready to receive more data
  • Not-Acknowledge (NACK): Is sent by the receiving device to signal that data has been received but the device does not want to receive more data

Write Transaction

A write transaction begins with the master writing Start onto the bus. This results in all slaves waiting for the following address byte. This is next address byte consists of the 7 address bits (in the upper 7 bit), and a read/write (R/W) bit as the 0-th bit (see table below). If the R/W bit is 0 the master wants to write data, if it is 1 it wants to read data from the slave.

Structure of the address byte, Addr-Bit-N marks the N-th bit of the address, with being the least significant:

Bit 7 6 5 4 3 2 1 0
Content Addr-Bit 6         Addr-Bit 0 R/W

As a response to the address byte the slave with the matching address replies with an ACK signal. Now the master can start writing data to the slave, for this the data is written byte by byte, between each byte the master is waiting for the confirmation in form of an ACK signal by the slave.

At the end of the transaction the master sends a Stop signal. This makes the bus available for other transactions.

Write transaction

Read Transaction

A read transaction starts with a write sequence similar to how the write transaction starts. This counterintuitive behaviour is necessary as the slave needs to know which data the master wants to read.

In more detail the initial request phase consists of (see above for more details): the start signal, the address byte with the R/W bit set to 0 (write), and the data which specifies which data the master wants to read. In contrast to the write transaction there is no stop signal at the end. Instead the master transmits another start signal, which is often called a repeated start. This repeated start is followed by an address byte, like it was the case at the first Start, but this time the R/W bit is set to 1 to read data.

Like above the slave with the matching address confirms the address byte with an ACK. This ACK of the slave is followed by an ACK of the master to request the first data byte.

As long as the master confirms the received data byte with an ACK message the slave sends more data bytes. If the master is finished receiving data it sends an NACK message to signal the slave that the transmission is finished. This NACK message is then followed by Stop signal from the master to signal the end of the transaction and release the bus.

Read transaction

Virtual Registers

The above protocol specifies how a master and multiple slaves can exchange data over a shared bus. What is not specified by the protocol is how the data that is sent is to be interpreted by the instances (so to speak the application layer protocol). For this there is no uniform standard but many devices base their communication on the principle of virtual registers.

A register is memory cell of fixed size, for I²C 8-bit is mostly used, as this is the size used for communication. Virtual in this context refers to the fact that the slave does not expose it’s actual registers to the master but only provides an interface which behaves like a register.

Every register is identified by it’s address, which is an 8-bit number. The register map, that is the interpretation of the data in the registers, is different for every sensors but can be found in the datasheet of the respective sensors. Additionally the register map also specifies if a register can be read from and written to or only read from, for example a register containing the sensor data is most probably read-only as the sensor data is only written by the sensors itself and can not be written by the user.

Excerpt of the register map of an environment sensor (CCS811):

Address Name R/W Size Description
0 STATUS R 1 byte Status register
1 MEAS_MODE R/W 1 byte Measurement mode and conditions register
       
3 RAW_DATA R 2 bytes Raw ADC data values for resistance and current source used.
       
32 HW_ID R 1 byte Hardware ID. The value is 0x81

Using this principle of virtual registers the protocol can be refined. For a write transaction the first data byte is the address of the register in which the master wants to write. The next byte is the actual data which should be written into the register. If the master sends more data these values are usually written into the subsequent registers. So if data should be written into successive registers it is sufficient to send the address of the first register and then continue with writing all data.

For a read transaction the master starts by writing a single byte, the address of the register that the master wants to read. After the repeated start the master can then read the register. Similar to the behaviour when writing it is also possible to continue reading data, this will read the next registers.

Most I²C slaves implement the virtual register principle like explained above, but it is important to remember that this behaviour is not part of the protocol and depends entirely on the manufacturer of the I²C slave. It is always necessary to consult the datasheet before implementing a library.

Physical Layer

Electrical Properties

The I²C bus consists of two data lines: SDA and SCL. Additionally, to have well defined voltage levels, all devices need to be connected to the same ground reference.

All signals are binary this means they are either set to ground (0V) or . Common values for are 5V and 3.3V, depending on the devices connected to the bus. The two voltage levels are also often refered to as high and low.

Like explained above all devices are connected to both the SDA and the SCL line. In a naive implementation the device that is currently active would pull the data lines either high or low depending on the desired state. This implementation has a major drawback: if more than one device is active at a time, this can be the case when the active device changes or if there is an error with one of the devices, there can be a short circuit. Assume that a slave is too fast and tries to respond before the master has finished sending data, in this case the slave tries to set the data lines to one voltage and the master to the other (see figure below).

Naive implementation

I²C solves this problem by allowing devices only to pull the data lines low and not high. If the device wants to have a high level on one data lines it needs to set the output pin to high impedance and an external pull up resistor will pull the data line up (see figure below). Typical values for the pull up resistors are 1k, 4.7k or 10k depending on the desired clock speed.

Pull-Up Resistors

The clock is always generated by the master, even when a slave is writing data via SDA. If a slave requires more time for writing data it too can pull down SCL, this is refered to as clock stretching and will effectively result in a lower SCL frequency.

Data Transmission

A single bit is transfered in every clock cycle, staring with most significant bit of the byte. When SCL is low the transmitter writtes SDA, when SCL becomes high the receiver can read the bit. During the SCL high time the transmitter must ensure that SDA does not change, this behaviour is depicted in the figure below:

Data transmission

Signals

Start

A start signal is sent by the master by pulling SDA to low while SCL is high (see figure below).

Start signal

Stop

A stop signal is sent by the master by pulling SDA to high while SCL is high (see figure below).

Stop signal

Acknowledge and Not-Acknowledge

The receiver acknowledges the received byte by pulling SDA to low during the ninth clock cycle, the timing is the same as if the receiver would send a ninth bit

A not acknowledge is sent by the receiver by not pulling SDA low, thus SDA is high.

ACK-Signal: ACK signal

NACK-Signal: NACK signal