Skip to main content

Command Palette

Search for a command to run...

Understanding I²C From a Engineer's Perspective

Updated
5 min read
Understanding I²C From a Engineer's Perspective

Embedded Systems Fundamentals #3

If SPI is the protocol of speed, Inter-Integrated Circuit (I²C )is the protocol of simplicity.

You'll find I²C everywhere:

  • Temperature sensors

  • Accelerometers

  • PMICs

  • EEPROMs

  • Touch controllers

  • Camera sensors

  • Real-time clocks

Most introductions stop after explaining:

SDA
SCL
Address
ACK

But that leaves out the most interesting parts.

Why does I²C use open-drain signaling?

How can multiple controllers share the same bus?

How does arbitration work?

What happens when a peripheral slows down communication?

How do drivers recover from a stuck bus?

This article explores what really happens inside an I²C transaction.

Why I²C Exists

Imagine a board with:

  • Temperature sensor

  • Accelerometer

  • EEPROM

  • RTC

Using UART:

MCU ---- Sensor
MCU ---- EEPROM
MCU ---- RTC
MCU ---- Accelerometer

Pins quickly become a problem.

I²C solves this.

SDA ----------------
SCL ----------------

      |     |     |
   Sensor EEPROM RTC

Only two signals. Multiple devices. Minimal pin count.

Application Layer

A modern software stack typically looks like:

sensor_read_temperature();

or

hal_sensor_read();

rather than:

i2c_write(...);

A hardware abstraction layer decouples application logic from the communication protocol.

Benefits:

  • Easier portability

  • Improved testability

  • Reduced hardware coupling

The application should care about "reading temperature", not whether the transport uses I²C, SPI, UART, or something else.

The Software Stack

A typical I²C transaction path:

Application
      ↓
Sensor HAL
      ↓
I²C Driver
      ↓
I²C Controller
      ↓
SDA / SCL
      ↓
Peripheral

Each layer has distinct responsibilities.

The Most Important I²C Concept: Open-Drain Signaling

Unlike SPI and UART:

I²C devices do not actively drive logic high.

Instead:

0 = Drive Low

1 = Release Line

Pull-up resistors generate the high state.

Why Open Drain?

Because multiple devices share the bus.

If one device drives:

HIGH

while another drives:

LOW

a short circuit can occur. Open-drain avoids this. Any device can safely pull the line low.

Pull-Up Resistors

Without pull-ups:

SDA
SCL

would float.

Typical values:

2.2kΩ
4.7kΩ
10kΩ

depending on:

  • Bus capacitance

  • Voltage level

  • Speed

Pull-up selection directly affects signal quality.

START Condition

Data transfers begin with START.

Generated when:

SDA: HIGH → LOW
while
SCL: HIGH

This condition cannot appear during normal data transfer. Every device on the bus recognizes it.

STOP Condition

Generated when:

SDA: LOW → HIGH
while
SCL: HIGH

This marks the end of the transaction.

Addressing

After START:

START
ADDRESS
R/W
ACK

Example:

0x48

for a temperature sensor. Addressing allows multiple peripherals to share the same bus.

ACK and NACK

Every byte transferred is followed by:

ACK

or

NACK

ACK:

0

NACK:

1

This provides a basic handshake mechanism.

Examples:

  • Device not present

  • Invalid register

  • Transaction complete

Unlike UART parity bits, ACK/NACK provides transaction-level confirmation rather than bit-level error detection.

I²C Controller Architecture

A modern controller contains:

Control Registers

Clock Generator

TX FIFO

RX FIFO

Shift Register

Interrupt Logic

DMA Interface

Status Engine

Many concepts are similar to SPI. The protocol handling is significantly more complex.

Clock Generation

The controller generates:

SCL

Clock frequency examples:

100 kHz
400 kHz
1 MHz
3.4 MHz

Different peripherals support different speed grades.

Arbitration

One of the most elegant features of I²C.

Consider:

Controller A
Controller B

Both start transmitting. Each controller monitors SDA while transmitting.

If a controller transmits:

1

but observes:

0

it immediately stops. The winning controller continues. No corruption occurs. No retransmission collision occurs. This is why I²C supports multi-controller systems.

Clock Stretching

Sometimes a peripheral is not ready.

Instead of losing data:

The peripheral holds:

SCL = LOW

This pauses communication. The controller waits until the peripheral releases the line. Not all modern systems enable clock stretching. Many high-performance systems disable it for predictability.

Interrupt Driven I²C

For small transfers:

START
Address
Data
STOP

may complete quickly.

For larger workloads:

FIFO Threshold
↓
Interrupt
↓
ISR
↓
Continue Transfer

Interrupts reduce CPU overhead.

DMA Driven I²C

Some controllers support DMA.

Flow:

Memory
 ↓
DMA
 ↓
I²C FIFO
 ↓
Bus

Useful for:

  • EEPROM operations

  • Sensor logging

  • Large register transfers

Common Real-World Problems

  1. Missing Pull-Ups - The symptoms are Bus stuck low No communication

  2. Wrong Address - The symptoms are NACK after address phase

  3. Bus Contention - The symptoms are Random communication failures

  4. Stuck SDA - Common after peripheral reset issues. Bus recovery may require manually toggling SCL.

  5. Clock Stretching Timeout - Peripheral never releases SCL. Driver must recover.

Debugging With a Logic Analyzer

A logic analyzer is often the fastest path to diagnosis.

Check:

✓ START condition

✓ Address

✓ ACK/NACK

✓ Register address

✓ Data bytes

✓ STOP condition

Many I²C issues become obvious immediately once the waveform is visible.

Putting Everything Together

The next time an application calls:

temperature = sensor_read_temperature();

The actual flow is:

Application
      ↓
Sensor HAL
      ↓
I²C Driver
      ↓
Controller Registers
      ↓
START
      ↓
Address
      ↓
ACK
      ↓
Data Transfer
      ↓
STOP
      ↓
Application

What appears to be a simple sensor read is actually a coordinated interaction between software layers, hardware state machines, open-drain signaling, arbitration logic, clock generation, and bus timing.

Understanding these mechanisms is essential for building reliable embedded systems.


What's Next?

In the next article we'll explore:

Why I3C Exists: The Evolution Beyond I²C:

  • Limitations of I²C

  • Why MIPI created I3C

  • Dynamic addressing

  • In-band interrupts

  • HDR modes

  • Backward compatibility

  • Driver architecture changes

  • Real-world migration challenges

And most importantly:

Why modern sensors are increasingly moving toward I3C.