Peripherals
intermediate
Weight: 4/10

I2C

Master I2C (Inter-Integrated Circuit) communication including addressing, repeated start, clock stretching, and error recovery for embedded systems.

peripherals
i2c
communication
addressing
clock-stretching

Quick Cap

I2C (Inter-Integrated Circuit) is a two-wire serial protocol that lets multiple devices share a bus using just SDA (data) and SCL (clock). A master initiates every transaction by sending a START condition followed by a 7-bit or 10-bit slave address; the addressed slave responds with ACK/NACK. Built-in features like repeated START, clock stretching, and bus arbitration make I2C the go-to choice for connecting sensors, EEPROMs, RTCs, and other low-to-medium speed peripherals in embedded systems.

Key Facts:

  • Two-wire interface: SDA (data) and SCL (clock) lines with open-drain outputs and pull-up resistors
  • Master-slave architecture: Master controls bus timing and initiates all communication
  • Addressing modes: 7-bit (up to 112 usable addresses) and 10-bit (up to 1024 addresses)
  • Clock stretching: Slaves can hold SCL low to buy processing time
  • Repeated START: Allows direction change or address change without releasing the bus
  • Error detection: ACK/NACK mechanism after every byte; arbitration detection for multi-master

Deep Dive

At a Glance

CharacteristicDetail
Wires2 -- SDA (data) and SCL (clock)
ClockSynchronous (master-generated)
DuplexHalf-duplex
TopologyMulti-master, multi-slave on a shared bus
Addressing7-bit (standard) or 10-bit (extended)
Speed Modes100 kHz / 400 kHz / 1 MHz / 3.4 MHz
Error DetectionACK/NACK after every byte, arbitration loss detection

Bus Interface

I2C Bus — Shared SDA/SCL with Pull-up Resistors
VCCRpRpSDASCLMasterMCUSensor0x48EEPROM0x50RTC0x68

Physical Layer

I2C lines use open-drain (or open-collector) outputs. A device can only pull a line LOW; it never actively drives HIGH. External pull-up resistors connected to VCC allow the line to float HIGH when no device is pulling it down. This creates a wire-AND configuration: if any device pulls a line LOW, the line reads LOW for every device on the bus. This single electrical property is what makes three of I2C's most important features possible: bus arbitration (two masters can drive the bus simultaneously without damage), clock stretching (a slave can hold SCL LOW and the master will see it), and multi-drop addressing (all devices share the same two wires).

The rule-of-thumb pull-up value is 4.7 kOhm for standard-mode (100 kHz) at 3.3 V. Faster speeds or higher bus capacitance require lower resistor values (e.g., 2.2 kOhm or 1 kOhm) to achieve the faster rise times demanded by the specification. Too-high a value causes sluggish rise times that corrupt data at higher clock rates; too-low a value draws excessive current every time a device pulls the line LOW, wasting power and potentially exceeding the open-drain sink current rating of the pin (typically 3 mA for standard I2C).

Both SDA and SCL are bidirectional in principle. The master generates clock pulses on SCL, but a slave can hold SCL LOW after the master releases it (clock stretching). SDA is driven by the master during address and write phases and by the slave during read phases and ACK/NACK. During idle, both lines float HIGH -- this is how devices detect a free bus.

Why open-drain matters in practice: if you accidentally configure an I2C GPIO as push-pull output, a contention scenario can arise where one device drives HIGH and another drives LOW simultaneously, creating a short circuit through the pull-up resistor. This can damage GPIO pins and is a common hardware bring-up mistake.

Speed Modes

ModeMax ClockRise Time (max)Typical Use Case
Standard100 kHz1000 nsGeneral-purpose sensors, EEPROMs
Fast400 kHz300 nsMost modern sensors, OLED displays
Fast Plus1 MHz120 nsHigh-bandwidth peripherals
High-speed3.4 MHz~40 nsSpecialized high-throughput devices

Standard and Fast modes are by far the most commonly encountered. Fast Plus is gaining adoption in newer sensor ICs. High-speed mode (Hs) requires a special "master code" preamble byte on the bus before switching to high-speed clocking, and it uses a separate current-source pull-up during the Hs phase rather than passive resistors. In practice, you will rarely encounter Hs-mode devices outside of specialized applications.

Note that rise time requirements tighten with each speed tier. This is why bus capacitance management becomes increasingly critical at higher speeds -- more capacitance means slower rise times, and if the rise time exceeds the mode's maximum, bits will be misread.

I2C Message Frame

I2C Write Transaction
S1 bit
Address7 bits
W1 bit
ACK1 bit
Data8 bits
ACK1 bit
Data8 bits
ACK1 bit
P1 bit
I2C Read Transaction (with Repeated START)
S1 bit
Address7 bits
W1 bit
ACK1 bit
Reg Addr8 bits
ACK1 bit
Sr1 bit
Address7 bits
R1 bit
ACK1 bit
Data8 bits
NACK1 bit
P1 bit
  • S / P (red) — START and STOP conditions. SDA transitions while SCL is HIGH.
  • Address + R/W (blue) — 7-bit slave address + 1-bit direction (0=Write, 1=Read).
  • ACK/NACK (green/gray) — Receiver pulls SDA LOW (ACK) or leaves HIGH (NACK).
  • Data / Reg Addr (orange) — 8-bit payload, MSB first.
  • Sr (purple) — Repeated START for direction change without releasing bus.

Transaction Structure

Every I2C transaction follows the same fundamental sequence. Understanding each phase is essential for both implementing drivers and debugging bus issues with a logic analyzer.

Write transaction, step by step:

  1. START condition -- Master pulls SDA LOW while SCL is HIGH. This unique signal (SDA transitioning during SCL HIGH) alerts all slaves on the bus to begin listening. During normal data transfer, SDA only changes while SCL is LOW.

  2. Address byte -- Master clocks out 7 address bits followed by 1 R/W bit (0 for write), MSB first. All slaves compare these bits against their own address. Only the slave with a matching address will respond.

  3. ACK from slave -- On the 9th clock pulse, the master releases SDA and the addressed slave pulls SDA LOW to acknowledge. If no slave responds, SDA floats HIGH (NACK), and the master knows the device is absent, busy, or the address is wrong. This is the basis of the common "I2C bus scan" utility.

  4. Data bytes -- The master clocks out one or more data bytes, MSB first. After each byte, the slave sends an ACK. If the slave NACKs a data byte, it typically means its receive buffer is full or an error occurred, and the master should stop sending.

  5. STOP condition -- Master lets SDA rise HIGH while SCL is HIGH. This releases the bus, making it available for any master to initiate the next transaction.

Read transaction -- The R/W bit is set to 1. After the address phase, the slave drives SDA to send data while the master provides the clock. The master ACKs each received byte to request more data. Before issuing the STOP, the master sends a NACK on the last byte to signal the slave that the read is complete and the slave should release SDA.

Address byte format for 7-bit addressing: [A6 A5 A4 A3 A2 A1 A0 R/W]. The 7-bit address is shifted left by one, and the R/W bit occupies the LSB. This is why driver code commonly contains address << 1 for write and (address << 1) | 1 for read.

For 10-bit addressing, two address bytes are used. The first byte has the fixed marker 11110 in bits [7:3], the two MSBs of the 10-bit address in bits [2:1], and the R/W bit in bit [0]. The second byte carries the lower 8 bits of the address. Ten-bit addressing is backward compatible -- 7-bit devices simply ignore the 11110 prefix since it falls in the reserved address range.

Bit-Bang START and STOP

The START and STOP conditions are the only moments when SDA changes while SCL is HIGH. During normal data transfer, SDA must remain stable whenever SCL is HIGH (this is the "data valid" window). This bit-bang implementation makes the timing relationship crystal clear:

c
void I2C_Start(void) {
I2C_SDA_High(); I2C_SCL_High(); I2C_Delay();
I2C_SDA_Low(); I2C_Delay();
I2C_SCL_Low();
}
void I2C_Stop(void) {
I2C_SDA_Low(); I2C_SCL_High(); I2C_Delay();
I2C_SDA_High();
}

In I2C_Start, both lines begin HIGH (the bus idle state). Then SDA is pulled LOW while SCL remains HIGH -- that falling edge on SDA is the START condition as defined by the specification. Finally, SCL is pulled LOW to prepare for clocking out the first data bit.

In I2C_Stop, SDA starts LOW (guaranteed by the last clock cycle), SCL goes HIGH, then SDA rises -- that rising edge on SDA while SCL is HIGH is the STOP condition. After this, both lines are HIGH and the bus is idle.

Note that in a real bit-bang driver, I2C_SCL_High() should read SCL back to handle clock stretching. A slave may hold SCL LOW, and the master must wait until SCL actually goes HIGH before proceeding.

Repeated START

A repeated START (Sr) is a START condition issued without a preceding STOP. The master transitions SDA HIGH, then SCL HIGH, then pulls SDA LOW -- identical timing to a regular START, but occurring in the middle of a transaction rather than from an idle bus.

Repeated START serves two critical purposes:

Direction change -- This is the most common use case and arises constantly when working with register-based devices. To read a register from a sensor, the master must first write the register address (telling the sensor which register to read), then read the data. Without repeated START, the master would have to issue a STOP (releasing the bus) and then a new START to change direction. With repeated START, the complete sequence is atomic:

START -> Address+W -> RegisterAddr -> Sr -> Address+R -> Data -> STOP

This pattern is so common that many I2C APIs offer a dedicated "write-then-read" function that handles the repeated START internally.

Bus ownership in multi-master systems -- Between a STOP and the next START, any master can claim the bus. A repeated START keeps the bus locked to the current master, preventing another master from inserting a transaction between the write and read phases. Without this guarantee, a second master could change the sensor's register pointer between your write and read, causing you to read the wrong register.

Clock Stretching

Clock stretching is a flow-control mechanism built into the I2C protocol. When a slave needs more time to process a received byte or prepare the next byte to transmit, it holds SCL LOW after the master releases it. The master must check that SCL has actually gone HIGH before proceeding with the next bit. If it does not -- for example, a purely GPIO-driven bit-bang implementation that writes SCL HIGH and immediately continues without reading the pin back -- communication will silently fail with any slave that uses clock stretching.

Clock stretching is common in practice. Many microcontrollers acting as I2C slaves use it because their interrupt latency may not be fast enough to respond within the master's clock cycle. EEPROMs stretch the clock during internal write cycles. Complex sensors stretch while performing ADC conversions or calculations.

Implications for firmware design:

  • Bit-bang drivers: After setting SCL HIGH, always read the pin state in a loop until it actually reads HIGH. Add a timeout to this loop.
  • Hardware I2C peripherals: Most handle clock stretching automatically, but you still need a transaction-level timeout in case a slave stretches indefinitely.
  • Timeout values: SMBus specifies a maximum clock stretch of 35 ms. Standard I2C has no defined limit, which means a broken slave can hang the bus forever. Always enforce a timeout.
  • System-level impact: Clock stretching by one slave delays communication with all slaves on the bus, since the bus is occupied for the duration of the stretch.

Bus Arbitration

I2C supports multiple masters on the same bus. When two masters attempt to start a transaction simultaneously, arbitration determines which one proceeds. The mechanism relies on the wire-AND property of SDA and is completely non-destructive:

  1. Simultaneous START: Two masters may both detect an idle bus (SDA and SCL HIGH) and issue a START condition at the same time. The I2C specification explicitly allows this.

  2. Bit-by-bit comparison: Both masters begin clocking out their address byte. After driving each bit onto SDA, each master reads SDA back to verify the line matches what it drove.

  3. Arbitration loss: If a master drives SDA HIGH but reads SDA as LOW, it knows another master is driving SDA LOW on the same bit. Since LOW dominates on a wire-AND bus, the master that sent HIGH has lost arbitration. It must immediately release SDA and SCL and stop driving the bus.

  4. Transparent to the winner: The winning master (the one that happened to be sending the lower address value, since LOW beats HIGH) continues its transaction completely unaware that arbitration occurred. No data is corrupted.

  5. Retry: The losing master waits for the bus to become idle (STOP detected) and then retries its transaction. In practice, collisions are rare if the bus is not heavily loaded.

This arbitration scheme means you do not need any external hardware or software protocol for multi-master coordination -- it is handled entirely by the electrical characteristics of the bus and the protocol itself.

Design Considerations

Bus capacitance: The I2C specification limits total bus capacitance to 400 pF for standard and fast modes. Every device on the bus adds input capacitance (typically 5-10 pF per pin), and every centimeter of PCB trace adds roughly 1-2 pF. Connectors, vias, and protection devices (TVS diodes, ESD clamps) all contribute. Exceeding the 400 pF limit causes rise times to exceed specification, leading to bit errors, especially at higher clock speeds. To calculate your budget: sum the input capacitance of all devices, add trace and connector capacitance, and ensure the total stays under 400 pF.

Maximum bus length: The practical limit is roughly 1 to 1.5 meters for fast mode (400 kHz) on a well-designed PCB or short cable. At standard mode (100 kHz), slightly longer runs are possible. Beyond a meter or so, cable capacitance dominates the capacitance budget, and the cable itself can act as an antenna picking up noise. For longer distances, consider I2C bus extenders (which buffer and re-drive the signals), level-shifted bus segments, or switching to a differential protocol like RS-485 or CAN.

Pull-up resistor selection: The pull-up value must satisfy two constraints simultaneously. The minimum value is set by the maximum LOW-state sink current of the weakest device on the bus (typically 3 mA for standard I2C). At 3.3 V, this gives Rp(min) = 3.3 V / 3 mA = 1.1 kOhm. The maximum value is set by the rise time requirement: Rp(max) = t_rise / (0.8473 x C_bus). For standard mode with 200 pF bus capacitance and 1000 ns rise time: Rp(max) ~= 5.9 kOhm. The classic 4.7 kOhm value falls comfortably within this range for most standard-mode systems. For fast mode, you will typically need 2.2 kOhm or lower.

Reserved addresses: The I2C specification reserves addresses 0x00-0x07 and 0x78-0x7F for special purposes. Address 0x00 is the general call (broadcast) address. Address 0x01 is the CBUS address. Addresses 0x02 and 0x03 are reserved for different bus formats. This leaves 112 usable addresses (0x08-0x77) in 7-bit mode. When selecting devices, verify their addresses do not conflict with other devices already on your bus. Many I2C devices provide address pins (A0, A1, A2) to allow limited address customization.

I2C vs SMBus

SMBus (System Management Bus) is derived from I2C but adds stricter requirements targeted at system management applications like battery monitoring, thermal management, and power supply communication:

FeatureI2CSMBus
Clock range0 Hz - 100 kHz (std), up to 3.4 MHz10 kHz - 100 kHz
TimeoutNone specified35 ms clock low timeout
Logic levelsFixed voltage thresholdsRelative to VDD
Hot-swapNot specifiedSupported via SMBALERT# and ARA
Packet error checkingNot standardOptional CRC-8 (PEC byte)
Max LOW current3 mA350 uA (low-power) / 4 mA (high-power)
Bus free time4.7 us (std)4.7 us minimum

The most important practical difference is the timeout. SMBus mandates that any device holding SCL LOW for more than 35 ms must be considered faulty, and the bus should be reset. Standard I2C has no such requirement, which means a malfunctioning slave can lock up the entire bus indefinitely. This is why bus-hang recovery routines (toggling SCL up to 9 times to clock out stuck data, then issuing a STOP) are critical in pure I2C systems.

SMBus also defines a minimum clock frequency of 10 kHz, which means you cannot clock SMBus devices arbitrarily slowly. Standard I2C allows clocking all the way down to DC (0 Hz), which is useful for debugging with a slow bit-bang implementation.

⚠️Common Trap: Missing Timeouts

Every I2C wait loop -- waiting for ACK, waiting for SCL release after clock stretching, waiting for transfer-complete flags -- must have a timeout. A single slave holding SDA or SCL LOW will hang the entire bus and your firmware if you spin forever. Always pair wait loops with a timeout counter and a bus recovery path.

⚠️Common Trap: Push-Pull GPIO Configuration

If you configure I2C pins as push-pull outputs instead of open-drain, two devices driving opposite levels will short through the pull-up resistor, potentially damaging hardware. Always verify the GPIO alternate-function configuration during board bring-up.

Debugging Story: The Automotive Sensor Node

A colleague was debugging an automotive sensor node that communicated flawlessly on the bench but failed intermittently once installed in a vehicle. After days of investigation, the root cause turned out to be surprisingly simple: the I2C driver used bare while loops without any timeout to wait for ACK and transfer-complete status flags. Electrical noise in the vehicle environment occasionally caused a missed clock edge, leaving the I2C peripheral state machine in a wedged state. The firmware then spun forever, waiting for a flag that would never be set.

The fix was twofold. First, every wait loop was wrapped with a timeout counter that would abort the transaction and return an error after a configurable deadline (5 ms for most transactions). Second, the error path triggered a bus recovery routine: the driver reconfigured SDA and SCL as GPIO outputs, toggled SCL up to 9 times (to clock out any partial byte a slave might be holding), issued a manual STOP condition, and then reconfigured the pins back to I2C alternate function.

The lesson: every production I2C driver needs timeout handling and a bus recovery path, no matter how reliable the hardware seems during development. Bench conditions rarely represent the electrical environment of the final product.

Comparison Tables

I2C vs Other Serial Protocols:

FeatureI2CSPIUART
Wires2 (SDA, SCL)4+ (MOSI, MISO, SCK, CS)2 (TX, RX)
TopologyMulti-master, multi-slaveSingle master, multi-slave (via CS)Point-to-point
SpeedUp to 3.4 MHzUp to 50+ MHzUp to ~1 Mbps typical
DuplexHalfFullFull
AddressingIn-band (7/10-bit)Out-of-band (CS lines)None (point-to-point)
OverheadACK after every byteNoneStart/stop bits per byte
Flow controlClock stretchingNone built-inRTS/CTS (optional)
Best forMany low-speed devices, minimal pinsHigh-speed, low-latency dataLong-distance, inter-board links

I2C Addressing Modes:

ModeUsable AddressesProsCons
7-bit112 (0x08-0x77)Simple, universally supportedAddress conflicts possible with many devices
10-bit1024Large address space, backward compatibleTwo-byte address overhead, less commonly used

Interview Focus

Classic I2C Interview Questions

Q1: "How do you configure I2C for different addressing modes, and when would you use each?"

Model Answer Starter: "I2C supports 7-bit addressing for up to 128 devices and 10-bit addressing for up to 1024 devices. For 7-bit mode, I configure the slave address directly in the address register. For 10-bit mode, I send the first 7 bits of the address followed by a repeated start condition and the remaining 3 bits. I use 7-bit addressing for simple sensor networks and EEPROMs, and 10-bit addressing for complex systems with many devices or when device addresses might conflict. The key is understanding that 10-bit addressing uses two address bytes and requires careful handling of the repeated start condition."

Q2: "What's the difference between I2C master and slave roles, and how do you implement each?"

Model Answer Starter: "The I2C master controls the bus timing, generates clock signals, and initiates all communication by sending START conditions and device addresses. The slave responds to its address and follows the master's clock. As a master, I configure clock speed, handle bus arbitration, and manage transaction flow. As a slave, I set my address, enable address matching interrupts, and respond with ACK/NACK. The key difference is that only the master can generate START/STOP conditions, while slaves can only stretch the clock when they need more processing time."

Q3: "How do you implement I2C repeated start and why is it important?"

Model Answer Starter: "Repeated start allows changing communication direction without releasing bus control, which is essential for efficient multi-device transactions. I implement it by generating a START condition without a preceding STOP condition, typically when switching from writing to reading. This prevents other masters from accessing the bus between operations and reduces transaction overhead. The sequence is: START -> Address+Write -> Data -> Repeated START -> Address+Read -> Data -> STOP. This is crucial for reading from devices like EEPROMs that require a write operation to set the address before reading."

Q4: "What causes I2C communication failures and how do you debug them?"

Model Answer Starter: "Common I2C failures include ACK timeouts, bus lockups, clock stretching issues, and electrical problems. ACK timeouts indicate missing or malfunctioning devices. Bus lockups occur when a slave holds SDA low or when START/STOP conditions are missed. Clock stretching problems happen when slaves hold SCL too long. I debug by checking pull-up resistor values, measuring bus capacitance, using oscilloscope to verify timing, and implementing proper error recovery with bus reset procedures. I also verify device addresses and ensure proper power sequencing."

Q5: "How do you handle I2C clock stretching and what are the timing considerations?"

Model Answer Starter: "Clock stretching allows slaves to hold the SCL line low to slow down communication when they need processing time. I handle it by monitoring SCL state and waiting for it to go high before continuing. Key considerations include maximum stretch duration (typically 25ms), proper timeout handling to prevent bus lockups, and understanding that stretching affects all devices on the bus. I implement timeout mechanisms and avoid infinite waits. The master must detect when SCL is held low and wait for the slave to release it before proceeding with the next clock pulse."

Trap Alerts

  • Don't say: "I2C is just SPI with two wires" -- they have fundamentally different topologies, clocking, and overhead models
  • Don't forget: Pull-up resistor requirements and bus capacitance effects on signal integrity
  • Don't ignore: Clock stretching timeout handling and bus recovery procedures

Follow-up Questions

  • "How would you implement I2C communication with devices that have different clock stretching requirements?"
  • "What's your strategy for I2C bus recovery when a slave device malfunctions?"
  • "How do you handle I2C communication in a system with multiple masters?"
💡Practice I2C Interview Questions

Ready to test yourself? Head over to the I2C Interview Questions page for a full set of Q&A with collapsible answers — perfect for self-study and mock interview practice.

Practice

What is the main advantage of I2C repeated start condition?

What happens when an I2C slave stretches the clock?

How many devices can be connected to an I2C bus with 7-bit addressing?

Real-World Tie-In

Smart Sensor Hub -- Managed 12 sensors (temperature, humidity, pressure, air quality, light, motion) plus EEPROM and RTC on a single I2C bus. Used careful address allocation to avoid conflicts, repeated START for efficient EEPROM register reads, and interrupt-driven communication with timeout-guarded waits. Achieved 99.9% transaction success rate by tuning pull-up resistors to match measured bus capacitance and implementing per-device retry logic with exponential backoff.

Industrial Control in Harsh Environments -- Deployed I2C across multiple control modules in a chemical processing plant with heavy electrical noise and temperature extremes. Implemented automatic bus recovery (9-clock SCL toggle + STOP) on every timeout, used optoisolated I2C bus segments for noise immunity, and added comprehensive transaction logging. The system ran for over a year with zero unrecoverable communication failures.