An Overview of the Inter-Integrated Circuit (I2C) Protocol
2023-04-12 | By Maker.io Staff
Previous articles discussed the basics of serial communication and SPI, a specific serial communication method, in great detail. In addition, those articles mentioned a few drawbacks of using UART and SPI, which the Inter-Integrated Circuit (I2C) protocol alleviates. This article introduces the core concepts of I2C and how using it allows multiple controllers to communicate reliably with many supported peripherals.
How Does I2C Compare to UART And SPI?
Asynchronous serial communication protocols don’t facilitate a shared clock signal to tell the communication partners when to send and read bits. Instead, both parties rely on a pre-determined start condition, after which bits are transmitted at a pre-defined rate (the baud rate). A message ends with an optional error-correcting bit and a fixed stop signal. This method leads to hardware overhead, as detecting and decoding these conditions can be complex. Further, the additional bits negatively affect the effective overall data throughput. However, asynchronous serial communication requires only two data wires at most.
With SPI, a single controller generates a clock signal that all peripherals use to determine when to read and write bits. In addition, each device has a CS (chip select) input that determines which device should be active at any given time. Unfortunately, these CS lines are also the most apparent drawback of SPI, as they can quickly occupy many I/O ports on the controller. In total, PI requires at least four wires and an additional one for every added peripheral. However, in SPI, the clock frequency can be relatively high; thus, the data transmission rate is improved compared to UART.
In contrast, I2C supports multiple controllers and peripherals on the same bus, consisting of only two wires - one for transmitting data and the other for sharing the clock signal. However, as there’s only a single data line, devices must take turns exchanging data. Furthermore, in SPI, communication is full duplex, meaning that simultaneous sending and receiving are possible, which is not the case in I2C.
The Basics of the I2C Protocol
Messages on the I2C bus must follow an exact specification for the devices on the bus to recognize them as valid transmissions. Generally, messages in the I2C protocol comprise an address frame with a start condition and one or multiple data frames.
When sending a new message, the sender must begin each message by leaving SCL high and pulling SDA low, which signals to all other devices that a transmission is about to start. Then, the sender transmits the address frame, which comprises the 7-bit address of the receiver, followed by a bit that indicates whether the sender wants to perform a read (1) or write (0) operation.
The message recipient (the device whose address matches the requested one) must then take control of the SDA line and pull it low within the next clock period. Failing to pull SDA low signals the sender that the receiving device is either unable to respond or couldn’t interpret the message, and the controller has to decide how to respond to problems.
Data transfer can begin by transmitting data frames if the addressed device responds on time. For this, the controller continues generating clock pulses, and the device in charge of SDA pumps out bits when instructed by the clock, which is whenever SCL transitions from high to low. There can be arbitrarily many data frames, and the transmission ends with a stop condition. The controller generates this stop condition by pulling SDA from low to high after SCL transitions from low to high, with SCL remaining high, effectively stopping the clock.
Luckily, most high-level programming environments for MCUs and development boards support I2C out of the box, so developers won’t have to implement the protocol themselves. Further, note that I2C supports advanced clock and address mechanisms that will not be discussed further in this introductory article.
Connecting Devices to the I2C Bus
As mentioned, I2C only uses two connections: SDA for exchanging data and SCL for supplying the clock signal to all devices on the bus. The currently active controller always provides the clock signal to the bus, and devices know when to listen for data depending on the start condition and their address. However, compared to UART and SPI, the I2C devices are open drain, meaning they require external pull-up resistors to pull the SDA and SCL lines high when no device is active. These resistors are typically 4.7k, but any value between 2k and 10k is generally valid. However, make sure to always refer to a part’s datasheet when deciding what value to use. Generally, lower values are better for systems with many devices or when making longer connections. Either way, the following image shows multiple devices connected to an Arduino on the I2C bus:
This diagram shows three peripherals connected to a single controller via the I2C bus. Note the two pull-up resistors that pull the SDA and SCL lines high when no device is active. Scheme-It link
Summary
I2C offers a good middle-ground between UART and SPI, offering improved data throughput over UART and eliminating the need for separate CS lines for every device, as is the case with SPI. Instead, I2C facilitates a shared clock signal generated by the active controller. In addition to the clock line, the devices only need a single data line for exchanging information, which means that I2C requires only two I/O pins for the devices on the bus to send and receive bits. However, the single data line also implies that devices can’t send and receive data simultaneously. Instead, they have to take turns.
Each transmission begins with the sender broadcasting an address frame that tells one device in the network to listen for an incoming message. If the device responds in time, the sender transmits one or multiple data frames containing the actual information.
Modern MCUs and development boards support I2C out of the box. However, circuit designers must make sure to add pull-up resistors to the SDA and SCL lines of the bus to pull the lines high when no device actively uses the bus. Many breakout boards and development modules already contain these resistors, and you should always refer to the datasheet for further information.
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum