Concept Q&A
12 questions
Popular

I2C Protocol — Interview Questions & Answers

Common I2C interview questions covering physical layer, addressing, arbitration, clock stretching, pull-up resistor selection, SMBus, bus recovery, and bit-bang implementation.

Study the fundamentals first

Read the I2C topic page for in-depth concepts before practicing Q&A

Physical Layer

QExplain the I2C physical layer. Why does it use open-drain outputs with pull-up resistors?

I2C uses two wires — SDA (data) and SCL (clock) — both configured as open-drain (or open-collector) outputs with external pull-up resistors. Open-drain means each device can only actively pull the line low by turning on its NMOS transistor; it cannot actively drive the line high. When no device is pulling the line low, the pull-up resistor passively returns it to VDD.

This design solves a fundamental problem: multiple devices share the same wires, and any of them might need to drive the bus at any time. With push-pull outputs, if one device drives high while another drives low, a short-circuit current flows from VDD through the high-side driver of one device and the low-side driver of the other. Open-drain eliminates this possibility — the worst case is two devices both pulling low simultaneously, which is electrically benign (they both agree on the bus state).

This wire-AND topology enables three critical I2C features: (1) multi-master arbitration — a master can detect that another master is pulling the bus low when it expected it to be high, and gracefully back off; (2) clock stretching — a slave can hold SCL low to pause the master; (3) ACK/NACK signaling — the slave pulls SDA low during the ACK clock pulse to acknowledge a byte. None of these would work with push-pull outputs, because the devices need to collaboratively share the same signal lines without conflict.

The trade-off is speed: the pull-up resistor and bus capacitance form an RC time constant that limits how fast the line can transition from low to high. The falling edge (active pull-down) is fast, but the rising edge (passive pull-up) is slow. This asymmetry is why I2C is fundamentally slower than SPI and why pull-up resistor selection directly affects maximum bus speed.

QHow do you choose pull-up resistor values for I2C, and what happens if you get them wrong?

Pull-up resistor selection is a trade-off between rise time, power consumption, and noise margin. The I2C specification defines maximum rise times for each speed mode: 1000 ns for standard mode (100 kHz), 300 ns for fast mode (400 kHz), and 120 ns for fast mode plus (1 MHz). The rise time is determined by the RC time constant of the pull-up resistor (R) and the total bus capacitance (C, including trace capacitance, pin capacitance of all connected devices, and connector capacitance).

The minimum resistor value is limited by the maximum sink current of the I2C output drivers. The specification requires that the bus voltage is below 0.4V (VOL) when a device pulls it low while current flows through the pull-up. For a 3.3V bus: R_min = (VDD - VOL) / I_sink = (3.3 - 0.4) / 3 mA = 967 ohm. In practice, 1 kohm is the floor.

The maximum resistor value is limited by the rise time requirement. For fast mode (300 ns max rise time) with 200 pF bus capacitance: the rise time to 70% of VDD is approximately 0.8 x R x C, so R_max = 300 ns / (0.8 x 200 pF) = 1875 ohm. Typical values are 2.2 kohm for fast mode and 4.7 kohm for standard mode.

If resistors are too large: rise times are slow, causing the bus to fail at the target speed. The signal never fully reaches VDD before the next clock edge, and devices may not recognize high-to-low transitions reliably. At standard mode (100 kHz) with low capacitance, 10 kohm may work; at fast mode (400 kHz), it will fail.

If resistors are too small: the pull-up current exceeds the output driver's sink capability, and the device cannot pull the line below VOL. This causes false NACKs and communication failures. Excessively small resistors also waste power — at 1 kohm on a 3.3V bus, each pulled-low line draws 3.3 mA, which matters in battery-powered designs.

A common interview trap: candidates say "4.7 kohm" without understanding why. That value works for standard mode with moderate capacitance but is often too high for fast mode or buses with many devices.

Addressing and Transactions

QWalk through an I2C register read transaction, explaining each phase.

Reading a register from an I2C sensor (e.g., reading the WHO_AM_I register at address 0x0F from a device at address 0x68) involves a two-phase transaction — a write phase to set the register pointer, followed by a read phase to retrieve the data:

Phase 1 — Write the register address:

  1. Master asserts START (SDA high-to-low while SCL is high)
  2. Master sends slave address (0x68) + Write bit (0) = 0xD0, MSB first (8 bits)
  3. Slave pulls SDA low during the 9th clock pulse = ACK (slave is present and listening)
  4. Master sends register address (0x0F) (8 bits)
  5. Slave ACKs — it has set its internal register pointer to 0x0F

Phase 2 — Read the register value: 6. Master asserts repeated START (START without preceding STOP) 7. Master sends slave address (0x68) + Read bit (1) = 0xD1 (8 bits) 8. Slave ACKs 9. Slave drives SDA with the register data while master clocks SCL (8 bits) 10. Master sends NACK (leaves SDA high during the 9th clock) to signal "no more bytes needed" 11. Master asserts STOP (SDA low-to-high while SCL is high)

The repeated START in step 6 is critical — if the master sent a STOP after step 5 and then a new START for step 7, another master could seize the bus in between, and the register pointer might get changed by the other master's transaction. The repeated START keeps the bus locked for the entire read operation.

QWhat is a repeated START and why does it matter?

A repeated START (Sr) is a START condition generated by the master without first issuing a STOP to release the bus. Electrically, it looks identical to a regular START (SDA falls while SCL is high), but it occurs in the middle of a transaction rather than from an idle bus state.

Repeated START matters for two reasons. First, in multi-master systems, it prevents another master from stealing the bus between two related operations. Consider a register read: the master must write the register address, then read the data. If it releases the bus with a STOP between these two steps, another master can jump in, address the same slave, and change its register pointer — causing the original master to read the wrong register when it resumes. The repeated START keeps the bus owned by the current master throughout the compound operation.

Second, some slave devices require it for correct operation. Many I2C sensors and EEPROMs interpret a STOP as "finalize the current operation" — for example, an EEPROM might begin an internal write cycle upon receiving STOP. Sending a STOP after the address phase and before the read phase would trigger the wrong internal behavior. The repeated START avoids this by keeping the transaction logically continuous.

In practice, almost every I2C register read uses a repeated START. It is so common that many HAL libraries (STM32 HAL's HAL_I2C_Mem_Read()) implement it automatically — the programmer specifies the device address, register address, and buffer, and the driver generates the write-Sr-read sequence internally.

QHow does 10-bit addressing work in I2C, and when is it needed?

Standard I2C uses 7-bit addresses, allowing up to 128 possible addresses (112 usable after subtracting reserved addresses). For systems with many identical devices or when the 7-bit space is exhausted, I2C supports 10-bit addressing, which extends the address space to 1024 devices.

A 10-bit address is sent in two bytes. The first byte uses the reserved prefix 11110 in the upper 5 bits, followed by the two MSBs of the 10-bit address and the R/W bit. The second byte contains the remaining 8 bits of the address. The slave ACKs each byte separately. This format is backward-compatible — 7-bit devices ignore the 11110xx prefix because it falls in the reserved address range.

In practice, 10-bit addressing is rarely used. The 7-bit address space is sufficient for the vast majority of designs, and most I2C devices only support 7-bit addressing. The main scenario where 10-bit addressing becomes relevant is large sensor networks (industrial monitoring systems with dozens of identical sensors) or custom I2C slave implementations (FPGA-based or MCU-based slaves) where you control the address scheme. Before reaching for 10-bit addressing, consider simpler alternatives: I2C multiplexers (TCA9548A), which create isolated bus segments each with their own address space, or configurable address pins (A0, A1, A2) that most I2C ICs provide for exactly this purpose.

Bus Arbitration

QExplain I2C bus arbitration step by step. What happens when two masters transmit simultaneously?

I2C bus arbitration is a non-destructive process that occurs bit-by-bit on the SDA line. It relies on the open-drain/wire-AND property of the bus: if any device pulls SDA low, it reads low regardless of what other devices are doing.

Step-by-step: (1) Both masters detect that the bus is idle (SDA and SCL both high) and assert START at approximately the same time. (2) Both begin transmitting the first byte (slave address + R/W bit), driving SDA on the falling edge of SCL and reading it on the rising edge. (3) For each bit, each master compares what it sent against what it reads on the bus. As long as both masters are sending the same bit value, both see the expected value and continue. (4) The first bit where they differ decides the winner: the master sending a 0 (dominant) sees a 0 on the bus — as expected — and continues transmitting. The master sending a 1 (recessive) also sees a 0 on the bus (because the other master is pulling it low), realizes it has lost arbitration, and immediately stops driving SDA. It switches to listening mode and waits for the bus to become free.

Example: Master A sends address 0x48 (0b1001000 + W = 0b10010000), Master B sends 0x44 (0b1000100 + W = 0b10001000). Bits 7-5 are identical (100). At bit 4, Master A sends 1 (recessive), Master B sends 0 (dominant). Master A reads 0, realizes it lost, and backs off. Master B's transaction continues without corruption.

The arbitration process is completely transparent to the winning master — it never knows arbitration occurred. The losing master retries its transaction after the bus becomes free (after the winning master's STOP). No data is lost or corrupted. This is possible only because the open-drain bus physics guarantee that a dominant bit always overrides a recessive bit.

Clock Stretching

QWhat is clock stretching, and why must every I2C master support it?

Clock stretching is a flow-control mechanism where a slave holds the SCL line low after the master releases it, effectively pausing the transaction. The master generates each clock pulse by pulling SCL low, then releasing it. Normally, the pull-up resistor brings SCL back high immediately. But if the slave is still holding SCL low (via its open-drain output), the line stays low — and the master must wait.

A slave stretches the clock when it needs time to process data before the next byte. Typical scenarios: (1) an EEPROM that needs 5 ms to complete an internal page write before it can accept the next command, (2) a software-implemented I2C slave on a slow MCU that cannot keep up with a fast master, (3) an I2C slave that must perform a computation (e.g., CRC calculation) before providing the response byte.

The I2C specification requires every master to support clock stretching — the master must check that SCL has actually gone high after releasing it, rather than assuming it will. A master that ignores clock stretching will proceed with the next bit while SCL is still low, causing complete desynchronization of the transaction. In HAL-based drivers this is handled automatically, but in bit-banged implementations, failing to read SCL back after releasing it is a common and subtle bug.

The danger of clock stretching is that there is no timeout defined in the base I2C specification. A malfunctioning slave can hold SCL low indefinitely, locking up the entire bus. This is one reason SMBus (which adds a 35 ms timeout) was created. In practice, firmware should implement a timeout on the master side — if SCL does not go high within a reasonable period (e.g., 25 ms), declare a bus error and initiate bus recovery.

Bus Recovery

QWhat causes an I2C bus lockup, and how do you recover from it?

An I2C bus lockup occurs when a slave device holds the SDA line low indefinitely, preventing the master from generating a START or STOP condition (both require SDA to transition while SCL is high, which is impossible if SDA is stuck low). This typically happens when: (1) the master resets mid-transaction (e.g., due to a watchdog or debugger) and the slave is still driving a data bit; (2) a noise glitch causes the slave's state machine to desynchronize, leaving it in the middle of a byte; (3) a software bug in a slave (especially bit-banged slaves) causes it to hang in a data-drive state.

The standard recovery procedure is the 9-clock-pulse technique: the master drives SCL through up to 9 clock cycles while leaving SDA released (high-impedance). Each clock pulse gives the stuck slave a chance to advance through its state machine and release SDA. If the slave is stuck mid-byte, it will eventually reach the ACK/NACK bit where it releases SDA. Once SDA goes high during a clock high phase, the master can assert a proper STOP condition to reset the bus.

c
void i2c_bus_recovery(void) {
// Configure SDA and SCL as GPIOs (not I2C alternate function)
gpio_set_mode(SCL_PIN, GPIO_OUTPUT_OPEN_DRAIN);
gpio_set_mode(SDA_PIN, GPIO_INPUT); // Release SDA
for (int i = 0; i < 9; i++) {
gpio_write(SCL_PIN, 0); // SCL low
delay_us(5);
gpio_write(SCL_PIN, 1); // SCL high (via pull-up)
delay_us(5);
if (gpio_read(SDA_PIN)) break; // SDA released, done
}
// Generate STOP: SDA low-to-high while SCL is high
gpio_set_mode(SDA_PIN, GPIO_OUTPUT_OPEN_DRAIN);
gpio_write(SDA_PIN, 0);
delay_us(5);
gpio_write(SCL_PIN, 1);
delay_us(5);
gpio_write(SDA_PIN, 1); // STOP condition
// Reconfigure pins for I2C alternate function
}

This recovery must be done using GPIO bit-banging, not the I2C peripheral — because the peripheral itself may be in a stuck state. After recovery, reinitialize the I2C peripheral. Many production designs run this recovery at boot and after any I2C timeout.

SMBus Differences

QWhat is the difference between I2C and SMBus? When does SMBus matter?

SMBus (System Management Bus) is based on I2C and is wire-compatible at the electrical level, but it adds constraints and features that make it more robust and deterministic:

Timeout (35 ms): SMBus requires that any bus transaction complete within 35 ms. If a clock low period exceeds 25 ms, all devices must reset their communication state. This prevents the bus lockup problem that can occur in I2C when a slave stretches the clock indefinitely. This single feature is the most important practical difference — SMBus buses cannot get permanently stuck.

Clock speed range: I2C allows clock speeds down to DC (zero hertz — the master can pause arbitrarily). SMBus requires the clock to stay within 10-100 kHz. This means SMBus devices must respond within a bounded time, enabling deterministic communication.

Packet Error Checking (PEC): SMBus optionally supports an 8-bit CRC (using the polynomial x^8 + x^2 + x + 1) appended to each transaction. I2C has no error detection beyond ACK/NACK.

Alert Response Address (ARA): SMBus slaves can assert a shared ALERT line to signal the master that they need attention. The master then reads from the ARA address (0x0C) to discover which device raised the alert, avoiding the need to poll every device.

Address Resolution Protocol (ARP): Allows dynamic address assignment, solving the problem of address conflicts when identical devices share a bus.

SMBus matters primarily in power management (battery fuel gauges, voltage regulators, platform management in servers) and PC hardware (where SMBus is the standard for SPD EEPROM communication with DIMMs, temperature monitoring, and fan control). If your device's datasheet says "SMBus compatible," you can generally use it on an I2C bus — but an I2C-only device on an SMBus system may fail if it stretches the clock beyond 25 ms.

Advantages, Disadvantages, and Practical Concerns

QWhat are the advantages and disadvantages of I2C?

Advantages:

I2C's greatest strength is its minimal pin count — only two wires (SDA, SCL) regardless of how many devices are connected. A bus with 20 sensors, EEPROMs, and port expanders still uses just two MCU pins. This is invaluable in pin-constrained designs. The built-in addressing scheme (7-bit or 10-bit addresses embedded in each transaction) eliminates the need for dedicated select lines. Multi-master support with non-destructive arbitration allows complex topologies where multiple MCUs share the same peripherals. ACK/NACK at the byte level provides immediate feedback that a device is present and received the data. Clock stretching gives slow slaves a way to throttle the master without data loss.

Disadvantages:

I2C is half-duplex — a single data line means data flows in only one direction at a time. Maximum speed is limited: standard mode is 100 kHz, fast mode is 400 kHz, and fast mode plus is 1 MHz. Even at 1 MHz, I2C is 10-50x slower than SPI. The pull-up resistors and bus capacitance create an asymmetric rise time that limits speed and bus length. The I2C specification caps bus capacitance at 400 pF for standard/fast mode, which limits practical cable length to 1-3 meters depending on speed. A single misbehaving slave — one that holds SDA or SCL low — can lock up the entire bus, and recovery requires the 9-clock-pulse procedure. The addressing overhead (device address + register address + ACK for each byte) reduces effective throughput for small transfers. Finally, electrical noise on SDA can be misinterpreted as ACK, causing false positive acknowledgments — there is no CRC in base I2C.

QWhat limits the number of devices and maximum bus length on I2C?

The primary physical constraint is total bus capacitance. The I2C specification limits bus capacitance to 400 pF for standard mode and fast mode. Each device adds input capacitance (typically 5-15 pF per device), and each centimeter of PCB trace adds approximately 1-2 pF. Connectors, through-hole headers, and cables add more. When total capacitance approaches the limit, rise times become too slow for the target clock speed, causing data corruption.

With 7-bit addressing, the theoretical maximum is 128 addresses, but 16 are reserved (0x00-0x07 and 0x78-0x7F), leaving 112 usable addresses. In practice, bus capacitance limits the device count to 20-30 devices on a PCB before the 400 pF limit is reached, well before the address space is exhausted.

For bus length, the limitation is again capacitive. Typical wire capacitance is about 50-100 pF per meter. A 2-meter cable adds 100-200 pF, consuming most of the 400 pF budget before any device capacitance is counted. At standard mode (100 kHz), buses up to 2-3 meters are practical with 4.7 kohm pull-ups. At fast mode (400 kHz), 1 meter is a more realistic limit.

To extend beyond these limits: (1) use active pull-ups (current-source pull-ups that charge the bus faster regardless of capacitance), (2) use I2C bus buffers/extenders (like PCA9600 or LTC4311) that isolate the capacitance of different bus segments, (3) reduce the clock speed, or (4) use lower-value pull-up resistors (but not below the minimum dictated by VOL and I_sink). For truly long-distance I2C (tens of meters), differential I2C extenders exist, but at that point you should question whether I2C is the right protocol — RS-485 may be a better fit.

Bit-Bang Implementation

QHow would you bit-bang I2C START and STOP conditions? What is the most common mistake?

START and STOP are the only I2C conditions where SDA transitions while SCL is high. During normal data transfer, SDA may only change when SCL is low. This is how receivers distinguish control conditions from data.

c
void i2c_start(void) {
sda_high(); // Release SDA (pull-up brings it high)
scl_high(); // Release SCL
delay_us(5); // Bus free time (t_BUF)
sda_low(); // SDA falls while SCL is high = START
delay_us(5); // Hold time (t_HD;STA)
scl_low(); // Pull SCL low to prepare for first data bit
}
void i2c_stop(void) {
sda_low(); // Ensure SDA is low
delay_us(5);
scl_high(); // Release SCL (pull-up brings it high)
delay_us(5); // Setup time (t_SU;STO)
sda_high(); // SDA rises while SCL is high = STOP
delay_us(5); // Bus free time before next START
}

The most common mistake in bit-banged I2C is not reading SCL back after releasing it. When the code calls scl_high(), it releases the open-drain output and expects the pull-up to bring SCL high. But if a slave is performing clock stretching, SCL stays low. The code must poll SCL until it actually goes high before proceeding — otherwise, timing is wrong and the transaction fails silently:

c
void scl_high_with_stretch(void) {
scl_release(); // Stop driving SCL low
uint32_t timeout = 10000;
while (!scl_read() && --timeout); // Wait for slave to release
if (!timeout) handle_bus_error(); // Timeout = bus stuck
}

Other common bit-bang mistakes: (1) using push-pull GPIO mode instead of open-drain, (2) forgetting delays between transitions (violating setup/hold times), and (3) not implementing the repeated START correctly (failing to pull SCL low before transitioning SDA for the repeated START, which creates an accidental STOP).