Skip to content

First, What Is It

  • I2C (Inter-Integrated Circuit): a serial bus that connects many devices with only SCL (clock) + SDA (data) two wires, addressing each device — commonly used for sensors, EEPROMs, and displays.
  • Half-duplex: there is a single data line, so only one direction at a time.
  • Open-drain + pull-up: both lines are open-drain outputs with external pull-up resistors — the physical basis for multi-device sharing.

Breaking Down the Principle

1. How Only Two Wires Serve Many Devices

Each slave has a unique 7-bit address (e.g., 0x3C for many OLEDs). The master broadcasts "I'm looking for address X"; only the matching slave responds, the rest stay silent. So "is the device address correct" is the first thing to check when debugging I2C.

2. Open-Drain + Pull-Up: Wired-AND

SDA/SCL are open-drain: devices can only pull the line low; the high level comes from pull-up resistors. Therefore:

  • Many devices can pull the bus low; if any one does, the bus is low (wired-AND).
  • A device must release the bus (not drive it) when idle, otherwise it "holds" the bus and blocks everyone else.

3. One Complete Transfer Sequence

Key concepts:

  • START/STOP: special level transitions that mark the frame boundaries.
  • ACK/NACK: after receiving a byte, the receiver pulls SDA low to acknowledge (ACK); not pulling it (NACK) signals error/end/address not found.
  • R/W bit: the lowest bit of the address byte is the direction (0 = write, 1 = read), so a 7-bit device at 0x3C sends 0x3C to write and 0x3D to read.

4. Clock and Speed

The I2C rate is set by the SCL frequency — common tiers: standard 100 kbps, fast 400 kbps, high-speed 1 Mbps+. The clock comes from the master; a slave can use clock stretching (holding SCL low) to ask the master to wait until it is ready.

How the SDK Implements It

SDK I2C follows "define the device struct and init → configure speed/address → write/read":

c
/* Define the I2C device struct (HOSAL unified device model, no get_by_name) */
hosal_i2c_dev_t i2c0;
/* Master mode, 400 kHz, 7-bit address 0x3C */
i2c0.config.mode = HOSAL_I2C_MODE_MASTER;
i2c0.config.freq = 400000;
i2c0.config.address_width = HOSAL_I2C_ADDRESS_WIDTH_7BIT;
hosal_i2c_init(&i2c0);
/* Write 1 byte to slave register 0x00 */
hosal_i2c_master_send(&i2c0, 0x3C, data, 1, 100);
/* Read n bytes from register 0x00 */
hosal_i2c_master_recv(&i2c0, 0x3C, rx_buf, n, 100);

Many sensors use a "write register address first, then read data" flow — together these two steps are one complete I2C transfer.

Common Exam & Interview Questions

Why open-drain with external pull-up instead of push-pull?

Open-drain plus wired-AND lets multiple devices share the bus without shorting each other; two push-pull devices driving opposite levels would short. This is why I2C natively supports multi-device sharing.

Why do read and write addresses differ for a 7-bit device?

The address byte = 7-bit device address + 1 R/W bit (0 write / 1 read), so write 0x3C and read 0x3D refer to the same device.

I2C scan finds no device — what to check first?

Wiring (SDA/SCL swapped?), missing pull-up resistors, wrong device address, and level mismatch (a 3.3 V device with a 5 V pull-up may be damaged or fail to communicate).

What are ACK/NACK?

After each byte, the receiver pulls SDA low for ACK (confirmed). No pull (NACK) usually means address not found, data error, or the transfer is ending.

When to choose I2C vs SPI?

I2C: fewer pins (2 wires), many devices on one bus, medium speed — good for sensors/EEPROMs. SPI: higher speed, full duplex — good for Flash/displays, but more pins and per-device chip selects.

Have questions?

For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

Released under the MIT License. Build Time 2026-09-11 14:52:23