I²C Protocol
Open-Drain Bus · 7-Bit Addressing · ACK/NACK
The 2-wire bus that powers almost every sensor, EEPROM, and RTC in embedded systems — learn it from START condition to clock stretching.
1. What is I²C?
I²C (Inter-Integrated Circuit, pronounced "I-squared-C" or "I-two-C") is a 2-wire synchronous serial protocol developed by Philips in 1982. It supports multiple masters and multiple slaves on a single bus — identified by a 7-bit address.
| Parameter | Value |
|---|---|
| Wires | 2 — SDA (data) + SCL (clock) |
| Topology | Multi-master, multi-slave — all devices share the same 2-wire bus |
| Addressing | 7-bit (112 usable) or 10-bit (1008 usable) |
| Speed modes | Standard: 100 kHz · Fast: 400 kHz · Fast+: 1 MHz · High-Speed: 3.4 MHz |
| Duplex | Half-duplex — SDA carries data in one direction at a time |
| Error detection | ACK/NACK per byte |
| Pull-up required | Yes — external resistors on SDA and SCL |
2. SDA, SCL & the Open-Drain Bus
| Signal | Direction | Description |
|---|---|---|
| SDA | Bidirectional | Serial Data — carries address, data, and ACK bits. Open-drain: device can only pull low or release. |
| SCL | Master drives, slave can stretch | Serial Clock — master generates the clock. Open-drain: slave can hold it low (clock stretching). |
Why open-drain?
Open-drain means a device has two states: pull low (drive 0) or release (float). It can never actively drive the line high. External pull-up resistors (connected to VCC) pull released lines high.
This gives a wired-AND effect: the bus is low if any device pulls it low. Multiple devices can pull at the same time with no short-circuit damage — the "winner" is always zero (low).
This property enables: multi-master arbitration (Section 6), clock stretching (Section 5), and safe bus sharing.
Pull-up resistor sizing
| Speed Mode | Typical Rp | Rule |
|---|---|---|
| Standard (100 kHz) | 4.7 kΩ | Rp_min = (VCC − VOL) / IOL_max ≈ 300 Ω |
| Fast (400 kHz) | 2.2 kΩ | Rp_max = rise_time / bus_capacitance |
| Fast+ (1 MHz) | 1 kΩ | Smaller Rp → faster rise, more current |
Bus capacitance (traces + device pins) must stay below 400 pF for standard/fast modes. Each device adds ~5–10 pF; keep cable lengths short.
3. Bus Transactions — START, Data, ACK, STOP
START condition
SDA falls from HIGH → LOW while SCL is HIGH. This is the unique START signal — no valid data transition happens while SCL is high outside of START/STOP. After START, the master drives SCL low and begins clocking.
Address + R/W byte
The first byte after START is always the address byte: 7 address bits (MSB first) + 1 R/W bit.
| Bit position | Meaning |
|---|---|
| bits [7:1] | Device address (7 bits, MSB first) |
| bit [0] | R/W — 0 = write (master sends data), 1 = read (slave sends data) |
Example: ADS1115 ADC at address 0x48 (7-bit) for a write → byte = 0x90 = 0b1001_0000.
ACK / NACK
After each 8-bit byte (address or data), the master releases SDA and generates a 9th clock pulse. The receiver responds:
| Response | SDA during 9th clock | Meaning |
|---|---|---|
| ACK | LOW (receiver pulls down) | Byte received OK, continue |
| NACK | HIGH (receiver releases) | Error, not ready, or end of read transfer |
Data bytes
SDA must be stable while SCL is HIGH — transitions only allowed while SCL is LOW. Data is clocked MSB first, 8 bits per byte, followed by 1 ACK bit. The master controls SCL; the sender (master on write, slave on read) drives SDA.
STOP condition
SDA rises from LOW → HIGH while SCL is HIGH. Releases the bus. Any master may issue a REPEATED START (Sr) — a new START without a preceding STOP — to address the same or a different slave while retaining bus ownership.
4. 7-bit Addressing
7-bit addresses give 128 slots; 16 are reserved by the I²C spec, leaving 112 usable. Devices often have 1–3 address bits configurable via pins — so you can have multiple instances of the same chip on one bus.
| Device | Address (7-bit) | Configurable bits |
|---|---|---|
| ADS1115 ADC | 0x48–0x4B | ADDR pin: GND/VCC/SDA/SCL |
| SSD1306 OLED | 0x3C or 0x3D | SA0 pin |
| AT24Cxx EEPROM | 0x50–0x57 | A0, A1, A2 pins |
| DS3231 RTC | 0x68 | Fixed |
| MPU6050 IMU | 0x68 or 0x69 | AD0 pin |
| PCF8574 I/O expander | 0x20–0x27 | A0, A1, A2 pins |
| BMP280 pressure sensor | 0x76 or 0x77 | SDO pin |
5. Clock Stretching
A slave that needs processing time can hold SCL LOW after the master releases it. Since SCL is open-drain, the master cannot override this — it must wait until SCL goes high before proceeding. This is clock stretching.
| When it happens | Example device |
|---|---|
| After receiving address (processing lookup) | Some MCU I²C peripherals |
| After receiving data (write to flash settling) | AT24Cxx EEPROM |
| Between bytes (ADC conversion in progress) | ADS1115 in conversion mode |
6. Multi-Master & Arbitration
Multiple masters can coexist on one I²C bus. If two masters start a transaction simultaneously:
- Both drive SCL — since open-drain, the clock is the AND of both.
- Both monitor SDA while driving it. If Master A tries to drive SDA HIGH while Master B holds it LOW, Master A reads back LOW — it lost arbitration.
- The losing master immediately stops driving and releases the bus.
- The winning master (the one driving LOW) continues its transaction uninterrupted.
Arbitration naturally favors the transaction with lower address bits — no priority configuration needed. The process is non-destructive: the winning transaction completes normally.
7. Verilog I²C Master
A synthesizable I²C master FSM. Supports write transactions (START → addr+W → ACK → data → ACK → STOP). The SDA output uses a tristate-style output-enable to implement open-drain in RTL.
module i2c_master #(
parameter CLK_DIV = 125 // 100 kHz SCL from 25 MHz system clk (25M/125/2 = 100kHz)
)(
input clk, rst_n,
input start, // pulse to begin transaction
input [6:0] addr, // 7-bit device address
input rw, // 0=write, 1=read
input [7:0] tx_data, // byte to write
output reg [7:0] rx_data, // byte read from slave
output reg done, // transaction complete
output reg ack_err, // slave sent NACK
// open-drain I2C pins (use external pull-ups)
inout sda,
output reg scl
);
// ── Clock divider ────────────────────────────────────────────
reg [$clog2(CLK_DIV)-1:0] clk_cnt;
reg clk_en;
always @(posedge clk) begin
if (!rst_n) begin clk_cnt <= 0; clk_en <= 0; end
else if (clk_cnt == CLK_DIV-1) begin clk_cnt <= 0; clk_en <= 1; end
else begin clk_cnt <= clk_cnt + 1; clk_en <= 0; end
end
// ── Open-drain SDA ───────────────────────────────────────────
reg sda_oe, sda_out;
assign sda = sda_oe ? (1'b0) : 1'bz; // drive low or release (pull-up takes it high)
// ── FSM states ───────────────────────────────────────────────
localparam
IDLE = 4'd0, ST_START = 4'd1, ST_ADDR = 4'd2,
ST_ACK1 = 4'd3, ST_DATA = 4'd4, ST_ACK2 = 4'd5,
ST_STOP = 4'd6, ST_DONE = 4'd7;
reg [3:0] state;
reg [3:0] bit_cnt;
reg [7:0] shift_reg;
reg scl_phase; // 0=low half, 1=high half
always @(posedge clk) begin
if (!rst_n) begin
state <= IDLE; scl <= 1; sda_oe <= 0;
done <= 0; ack_err <= 0; scl_phase <= 0;
end else if (clk_en) begin
done <= 0;
case (state)
IDLE: begin
scl <= 1; sda_oe <= 0; // bus idle (both lines released HIGH)
if (start) begin
shift_reg <= {addr, rw};
bit_cnt <= 7;
state <= ST_START;
end
end
ST_START: begin
// SDA falls while SCL high → START condition
sda_oe <= 1; // pull SDA low
scl <= 1;
state <= ST_ADDR;
scl_phase <= 0;
end
ST_ADDR: begin
if (!scl_phase) begin
scl <= 0;
sda_oe <= !shift_reg[7]; // drive 0: pull low; drive 1: release
scl_phase <= 1;
end else begin
scl <= 1;
scl_phase <= 0;
if (bit_cnt == 0) state <= ST_ACK1;
else begin shift_reg <= {shift_reg[6:0], 1'b0}; bit_cnt <= bit_cnt - 1; end
end
end
ST_ACK1: begin
if (!scl_phase) begin
scl <= 0; sda_oe <= 0; // release SDA — slave drives ACK
scl_phase <= 1;
end else begin
scl <= 1;
ack_err <= sda; // if SDA=1 → NACK
shift_reg <= tx_data;
bit_cnt <= 7;
scl_phase <= 0;
state <= ST_DATA;
end
end
ST_DATA: begin
if (!scl_phase) begin
scl <= 0;
sda_oe <= !shift_reg[7];
scl_phase <= 1;
end else begin
scl <= 1;
scl_phase <= 0;
if (bit_cnt == 0) state <= ST_ACK2;
else begin shift_reg <= {shift_reg[6:0], 1'b0}; bit_cnt <= bit_cnt - 1; end
end
end
ST_ACK2: begin
if (!scl_phase) begin
scl <= 0; sda_oe <= 0;
scl_phase <= 1;
end else begin
scl <= 1;
ack_err <= ack_err | sda;
scl_phase <= 0;
state <= ST_STOP;
end
end
ST_STOP: begin
// SDA rises while SCL high → STOP condition
if (!scl_phase) begin
scl <= 0; sda_oe <= 1; // pull SDA low first
scl_phase <= 1;
end else begin
scl <= 1;
sda_oe <= 0; // release SDA → HIGH while SCL HIGH = STOP
state <= ST_DONE;
end
end
ST_DONE: begin done <= 1; state <= IDLE; end
endcase
end
end
endmodule
inout sda port becomes a bidirectional pad in synthesis. In FPGA designs, instantiate a IOBUF primitive explicitly for the SDA pin. In simulation, the open-drain behavior (drive-low-or-release) is correctly modeled by assigning 1'bz when not driving.8. Applications
| Device | Type | Address | Notes |
|---|---|---|---|
| ADS1115 | 16-bit ADC | 0x48–0x4B | 4-channel, programmable gain, up to 860 SPS |
| AT24C256 | EEPROM 256 kbit | 0x50–0x57 | Page write 64 bytes, 5 ms write cycle |
| DS3231 | RTC | 0x68 | Temperature-compensated oscillator, ±2 ppm |
| SSD1306 | OLED controller 128×64 | 0x3C/0x3D | Fast mode 400 kHz, command/data register |
| MPU6050 | 6-axis IMU | 0x68/0x69 | 3-axis gyro + 3-axis accel, DMP on-chip |
| PCF8574 | 8-bit I/O expander | 0x20–0x27 | Adds 8 GPIO pins via 2 wires |
| TCA9548A | I²C multiplexer 8-ch | 0x70–0x77 | Isolates address-conflicting devices on separate segments |
| MAX17043 | LiPo fuel gauge | 0x36 | SoC%, voltage, sleep mode via I²C |