When a board needs to connect a few low-speed peripherals, the first problem is often not “how do we move more data?” but “how do we use fewer wires and fewer pins while still letting multiple chips work together?” Temperature sensors, EEPROMs, RTCs, and power-management chips are not high-throughput devices, but they all need to be accessed by the host. If every extra chip needs its own dedicated wire set, cost, routing, and packaging pressure rise quickly.
I2C solves exactly that kind of problem: multiple devices share one low-speed control bus, and the host does not need a dedicated data channel for each peripheral. It optimizes first for pin cost, wiring simplicity, and coexistence among multiple devices, not for bandwidth.
The most useful first mental model is simple: I2C is a way to compress “who is talking, who it is talking to, whether the other side received it, and whether multiple masters will collide” into one shared timing model on two wires. Start bits, address bits, ACK, arbitration, and clock stretching are all there to close those loops.
How Two Wires Can Still Carry Many Devices
I2C has only two signal lines:
SCL: the clock lineSDA: the data line
Unlike SPI, it does not give each slave its own chip-select line. Instead, all devices are wired directly onto the same SCL and SDA pair. That only works because the electrical rules are different.
I2C uses open-drain outputs. A line’s high level is not actively driven by any device; it is pulled up by a resistor. A device can only do two things:
- pull the line low
- release it so the line returns high
That matters because it makes shared-bus conflicts manageable. If two devices act at the same time, the bus simply stays low when any one of them pulls low. You do not get a dangerous push-pull fight where one side forces high and another forces low.
The tradeoff is direct:
- rise time depends on pull-up resistors and bus capacitance, so the speed cannot go too high
- line length, loading, and capacitance all affect waveform quality directly
- it is better for board-level, short-distance, low-speed control than for long-distance communication
So the first constraint in I2C is not a protocol field. It is the reality that this is meant to be a cheap, shared, low-speed, short-range bus.
How a Transfer Starts and Ends
One thing shared buses fear most is ambiguity about whether a bit sequence belongs to a new transaction. I2C uses START and STOP to define that boundary.
START: whenSCLis high,SDAgoes from high to lowSTOP: whenSCLis high,SDAgoes from low to high
Those conditions are not ordinary data bits. During normal data transfer, SDA should change only while SCL is low. That way, every device on the bus can recognize them as “transaction start” and “transaction end.”
The main I2C flow is therefore:
- The master sends
START - The master sends the device address and the read/write bit
- The selected slave acknowledges
- The two sides continue transferring one or more bytes, with an ACK after each byte
- The master sends
STOPat the right time
If the master does not want to release the bus and wants to move immediately to the next phase, it can send a Repeated START. That is common when you first write a register address and then immediately read data, because it avoids opening a gap in which another master might cut in.
Addresses Are Not for Classification, They Are for Calling Out a Device on a Shared Wire
Without a dedicated chip-select wire, I2C has to answer “who are we talking to this round?” using an address.
The most common form is a 7-bit address plus a 1-bit read/write direction bit:
0means the master is going to write1means the master is going to read
Many descriptions make I2C sound like an “address protocol,” but the address itself is not the point. The point is that it replaces a dedicated wire with a name on the shared bus. The master broadcasts an address on the bus, every slave listens, and only the one that matches continues with the transaction.
The benefits are clear:
- many devices share just two wires
- when you add a new peripheral, you do not need another chip-select pin
- the software access model is usually uniform: address + register/data
The limits are also clear:
- the address space is limited, so collisions appear when too many devices are used
- many chips expose only a few configurable address bits, so board-level expansion space is limited
- the address only names the target; it does not discover topology, authenticate devices, or solve higher-layer compatibility
Address conflict is a very common field problem. If two devices on the bus use the same default address, the protocol will not coordinate for you. Typical solutions are to use a variant with configurable addresses, change hardware straps, or add an I2C mux.
What ACK Is Solving
Another core problem on a shared bus is how the master knows the other side really accepted a byte instead of just leaving the line alive.
I2C answers that by adding a 9th clock after every 8 data bits:
- the receiver pulls
SDAlow to sayACK - the receiver leaves it high to say
NACK
That small mechanism carries several different meanings.
The first layer is device presence. After the master sends an address, if no device pulls SDA low during the acknowledge bit, that usually means no one is listening at that address, or the target device is not ready to accept this transfer.
The second layer is byte-level flow control. During a write, the slave can tell the master whether a byte was accepted. During a read, the master can also send NACK after the last byte to say “I do not want any more data now,” and then end the transfer.
The third layer is minimal error exposure. Unlike heavier protocols, I2C does not have a complex built-in retransmission or sequence negotiation model. It keeps confirmation at the per-byte ACK level. That keeps the implementation simple, but it leaves more recovery responsibility to the higher-level driver and device protocol.
So ACK is useful, but do not overread it. It usually only says:
- someone responded on the bus at that moment
- the byte was accepted at the link layer
It does not guarantee:
- the device has already finished the internal action
- a write has reached nonvolatile storage
- the returned data is valid in business semantics
For example, EEPROM writes often have a write cycle after the data is sent. During that time the device may NACK subsequent access until the internal write completes. That NACK does not mean the link is broken. It means the device is still busy.
Why I2C Can Support Multiple Masters
Once the bus is shared, the harder question inevitably comes up: if more than one master exists, what happens when they try to start at the same time?
I2C does not solve that by pre-booking the bus. It uses the physical behavior of open-drain lines for arbitration. The rule is: while sending, the master also reads the bus level back continuously.
If a master tries to send high but reads back low, that means another device pulled the line low. Since low dominates on the wire, that master immediately knows it lost arbitration and must withdraw from the transaction.
That has several important implications:
- arbitration happens during the transfer, not in a separate negotiation phase
- the winning side can continue sending the current message
- the losing side does not turn the bus into an unrecoverable electrical conflict
The cost is that multi-master support, while part of the spec, is not common in many real systems. That is not because the mechanism is fake; it is because the system complexity rises:
- software must handle retries after arbitration loss
- timing analysis and failure reproduction become harder
- in most board-level systems, a single MCU master is already enough
So “the protocol supports multi-master” and “multi-master is commonly deployed” are not the same thing. Many driver stacks are built around a single-master mental model by default.
Why the Clock Can Also Be Held Back by a Slave
At first glance, SCL sounds like it should be entirely under the master’s control. The ideal flow is not that simple, because a slave may not always be ready to keep up.
I2C allows clock stretching: a slave can hold SCL low when it needs more processing time, and the master must wait.
That solves the problem of “low-cost, slow peripherals need to keep up with one common bus rhythm.” Without it, the master would have to use extremely conservative timing, or every device would need its own separate waiting scheme.
But clock stretching also brings real cost:
- not every controller fully supports it, especially in some SoCs or bridges
- it can be confused with a dead bus or a fault during debugging
- the higher layer needs timeouts, or one misbehaving slave can freeze the whole bus
That is a very typical piece of I2C field experience: a flexibility mechanism that seems to simply “wait a bit” is also one of the easiest places to leave a hidden bug.
What a Typical Register Read Flow Looks Like, and Why It Often Uses a Repeated Start
Many sensors, RTCs, and PMICs look like they are “reading a register,” but the actual wire-level flow is usually:
START- Send the slave address with write direction
- Send the register address or internal offset
Repeated START- Send the slave address again, this time with read direction
- Read the data bytes continuously
- After the last byte, the master sends
NACK STOP
That is a very important pattern to remember because it combines several I2C ideas at once:
- the address names the target
- the write phase tells the slave which register to look at
- the repeated start keeps the transaction continuous without releasing the bus
- the read phase lets the slave drive data onto
SDA - the master uses the final
NACKto end the read
When you inspect a capture, if you cannot reconstruct even this basic flow yet, do not jump straight to the register values. First check whether the address is wrong, the direction bit is wrong, the repeated start is missing, the ACK behavior is wrong, or the ending is wrong.
What I2C Is Most Easily Mistaken For
I2C is often mistaken for “SPI with two wires,” or for “a simpler serial bus.” Both readings skew later engineering judgment.
It is not like SPI because:
- there is no dedicated chip select for each slave
- the read/write direction and target device are embedded in the timing
- the bus uses wired-AND sharing, not push-pull exclusivity
It is not like UART because:
- it is a shared bus, not a point-to-point link
- it cannot run by itself with just a loose byte stream; it depends on start, stop, ACK, and addressing semantics
- the electrical layer and protocol layer are designed together, not as a pure byte pipe
The more accurate view is: I2C is a shared control bus for board-level peripheral interconnect. It accepts limited speed, capacitance sensitivity, and weaker error recovery in exchange for fewer wires and lower cost.
What To Check First in Engineering
When implementing, capturing, or debugging I2C, the most valuable thing is usually not “copy every bit.” It is to check a few high-information points first.
First, see whether the bus is actually idle and whether both SCL and SDA can return high. If one line stays low for a long time, check pull-ups, wiring, device lock-up, or controller state first.
Second, check whether the address phase gets an ACK. Many problems can be separated right there:
- no
ACK: check the address, power, wiring, pull-ups, and device busy state first ACKis present but data later looks wrong: move on to register semantics or the driver state machine
Third, when a register read fails, check whether the repeated start is missing or the direction bit is wrong. Many “the device does nothing” bugs are really transaction-shaping bugs.
Fourth, when there are occasional timeouts or the whole bus gets stuck, do not look only at software logic. Bring clock stretching, pull-up resistors, bus capacitance, line length, and total shared load into the same analysis.
Fifth, do not merge protocol guarantees with device behavior. ACK only means link-layer response. It does not replace the busy status, conversion time, or register constraints in the device datasheet.
When It Fits and When It Does Not
I2C is a good fit for:
- short board-level connections
- multiple low-speed sensors or configuration chips sharing one bus
- situations where pin count and routing are more important than throughput
I2C is not a good fit for:
- long-distance communication
- large data movement
- buses with heavy capacitance and many devices when you still need high-frequency stability
- scenarios with stronger real-time and error-recovery requirements
So choosing I2C is not about it being “simple.” It is about being a good tradeoff for a class of problems: lots of low-speed peripherals, limited resources, short distance, and a desire to keep access shared with minimal wiring.