I2C and the Pi Pico - Part 1

Introduction

In this series of posts, we will explore the I2C (I2C or IIC) bus and how to use it on a Raspberry Pi Pico.

I2C  is a serial, two-wire (plus ground) bidirectional bus used for communication between a controller and a peripheral device with clock speeds of 100kHz or 400kHz (faster modes are possible with additional line drivers). Communication is in blocks of one or more 8-bit bytes and, although simple devices may support a single byte read and/or write operation, it's usual for there to be a device-defined, higher-level command/response protocol.

Mostly, there will be only one controller, but multiple devices may be attached as the two bus lines are open-drain/open-collector (so pull-up resistors are required). Multiple-controller configurations are also possible, but uncommon. Unlike the SPI, I2C has a clearly-defined addressing mechanism to distinguish between bus devices: either 7- or (rarely) 10-bit addresses may be used. It also has intrinsic mechanisms for peripheral devices to temporarily stall a transaction if a response cannot be made in strict conformance with the usual timing. 

Intel's SMBus is a variant of I2C sharing many of the same features.

Basics

The two active signals are SDA (serial data) and SCL (serial clock) and, in the idle condition, the lines are pulled high by their respective pull-up resistors. The appropriate value depends on the bus capcitance, but 10kΩ is not atypical. The 'weak' pull-up resistors that many microcontrollers offer on their GPIO pins are typically of much greater resistance and are unlikely to suffice, particularly at the higher bus speed as they can't charge the bus capacitance sufficiently quickly to avoid degrading the signal.

Typical I2C Wiring
Typical I2C Wiring

A bus transaction begins with a START signal - the controller causes SDA to transitions low while SCL remains high. The device address is then shifted out onto SDA and the corresponding clock signal is sent on SCL. It's important that SDA remains stable while SCL is high because any transition in this period will be interpreted as either a START or STOP condition. Following the address bits, a READ/WRITE bit indicates whether the controller wants to write data to the addressed device (SDA is low) or read data from it (SDA is high). The addressed device then pulls SDA low for the next clock pulse, signalling ACKNOWLEDGE. If no device responds, the line will remain high. 

If the controller is transmitting, it sends 8 bits of data and then expects a further ACKNOWLEDGE from the device after which it may continue to send further data or conclude the transaction with a STOP condition (SDA transitions high while SCL is high). You can see an illustration of this below.

I2C Bus Transaction
I2C Bus Transaction

If the controller is receiving, the device has control of SDA and sends its data in response to transitions on SCL. After every 8 bits, the controller will send ACKNOWLEDGE (by pulling down SDA in the next cycle), except for the last (or only) byte it expects, when SDA will remain high to signal the device to relinquish the bus. The controller then sends a STOP condition.

If the device has a command/response protocol, it will be necessary for the controller to send the command as a WRITE transaction and, in the event a response is expected, to turn the bus around to allow the device to transmit, but without starting a new bus transaction. To do this, instead of ending the WRITE transaction with a STOP condition, the controller repeats the START condition and continues by repeating the device address, but this time indicating a READ transaction. Once the READ transaction is complete, a STOP condition is finally sent. More rarely, the controller may chain several READ and WRITE transactions together in the same way.

Normally, only the controller drives SCL, however I2C permits the device to hold SCL low when it needs more time to complete the operation (clock-stretching). The controller should detect this condition when it tries to send the next clock pulse and delay it. There are (now) limits on how long a device is allowed to put off its response. 

Summary

The I2C bus is widely used for low- and medium-speed, byte-oriented serial connections between microcontrollers and multiple peripheral devices (each of which has its own address). With additional bus drivers it can also be used at higher speeds. 

In the next post in this series, we'll explore how to use I2C with the Raspberry Pi Pico using the HUSB238 USB-C Power Delivery controller.


Comments