Skip to main content

Serial Buses — I2C, SPI & UART

Overview

Below the level of PCIe and USB, most chip-to-chip communication on a circuit board — a sensor talking to a microcontroller, an EEPROM storing settings, a display taking pixel data — happens over one of three much simpler serial buses: I2C, SPI, and UART. Each makes a different tradeoff between pin count, speed, and topology, and picking the right one (or reading a datasheet that assumes you know the difference) is a routine embedded-systems and hardware-interfacing task.

Core Concepts

TermMeaning
I2C (Inter-Integrated Circuit)Two-wire, multi-master/multi-slave synchronous serial bus using addressing to reach a specific device.
SPI (Serial Peripheral Interface)Four-wire, full-duplex synchronous serial bus using a dedicated chip-select line per device instead of addressing.
UART (Universal Asynchronous Receiver/Transmitter)Asynchronous, point-to-point serial communication with no shared clock — timing is inferred from a fixed baud rate.
Open-drainAn output stage that can only pull a line low or let it float; a pull-up resistor is required to bring it high — I2C's SDA/SCL work this way.
Chip select (CS/SS)A dedicated line an SPI controller drives to pick which single device is "listening" on the shared data lines.
Baud rateThe agreed-upon bit rate for UART communication; both ends must be configured to the same value since there's no shared clock line.
ACK/NACKI2C's per-byte acknowledgment bit: the receiver pulls SDA low (ACK) to confirm receipt, or leaves it high (NACK).

Architecture / Mechanism

I2C — two wires, addressed, multi-master

An I2C bus schematic: a controller and three peripherals all tapping the same two SDA and SCL lines, which are tied to Vdd through a pair of pull-up resistors
Every device hangs off the same two lines. The two resistors to Vdd are the pull-ups that make the open-drain scheme work — without them the bus never returns high. Wikimedia Commons, CC BY-SA 3.0
Terminology

The figure uses the older master/slave labels. The current I2C specification, and this page, use controller and peripheral (or target) for the same roles.

I2C uses exactly two shared lines — SDA (data) and SCL (clock) — no matter how many devices are on the bus. Both lines are open-drain, pulled high by resistors; any device can pull either line low, which is what makes multi-master arbitration and clock stretching possible without electrical conflicts. Every transaction starts with the controller sending a 7-bit (or, less commonly, 10-bit) address to select which device should respond; that device ACKs its own address and ignores traffic addressed to anyone else.

Clock stretching: a slow slave device can hold SCL low itself to pause the controller mid-transfer until it's ready — the open-drain wiring makes this a natural extension of the same electrical mechanism used for arbitration.

SPI — four wires, full-duplex, chip-select

An SPI controller wired to three peripherals: SCLK, MOSI and MISO are shared by all three, while three separate slave-select lines each run to exactly one peripheral
SCLK, MOSI and MISO are shared; the select lines are not. Three peripherals need three dedicated select pins — the cost that I2C's addressing avoids. Wikimedia Commons, CC BY-SA 3.0

The pin count is the trade-off in one picture. I2C reaches any number of devices on two wires; SPI needs 3 + N wires for N devices, and in exchange gets full-duplex transfers, far higher clock rates, and no addressing or acknowledgement protocol to implement.

SPI has no addressing scheme at all. Instead, the controller wires a separate chip-select line to each device and asserts exactly one at a time — whichever device sees its CS line active is the one listening/responding. Data moves on two separate lines, MOSI (controller-out) and MISO (controller-in), driven by a shared clock (SCLK), so a full read and write can happen simultaneously (full-duplex) — something I2C's single shared data line can't do.

SPI is a de facto standard, not a formal spec

Unlike I2C (formally specified by NXP/Philips) or USB (formally specified by USB-IF), SPI has no single official specification. It originated as a Motorola feature on early-1980s microcontrollers, described in a Motorola application note, and became a de facto industry convention. This is why SPI implementations vary in details like clock polarity/phase modes (CPOL/CPHA — "SPI mode 0-3"), word size, and whether multiple chip-selects or daisy-chaining are supported — always check the specific device's datasheet.

UART — asynchronous, no shared clock

UART connects exactly two devices, one wire each direction (plus ground) — no shared clock line at all. Instead, both ends are configured with the same baud rate in advance, and the receiver recovers timing from start and stop bits framing each byte:

Because there's no shared clock, both sides must agree on baud rate, data bits, parity, and stop bits out of band (a common convention is written as, e.g., "9600 8N1"). This is the classic serial port / /dev/ttyUSB0 style interface still used for console access to routers, microcontroller debug output, and GPS modules.

Practical Usage

A typical embedded PCB mixes all three: a UART for a debug console, I2C for a handful of low-speed sensors (temperature, accelerometer, RTC) where pin count matters more than speed, and SPI for anything needing real throughput — an SD card, a TFT display, or an external flash chip. Reading a sensor's datasheet, the "Interfaces" section will name one of these three (or occasionally both I2C and SPI as configurable options on the same chip).

Edge Cases & Pitfalls

I2C address collisions

Two devices of the same model on one I2C bus (e.g., two identical sensors) often ship with the same fixed 7-bit address, causing a collision. Many parts expose one or two address-select pins to work around this; if not, an I2C multiplexer chip is the usual fix.

Don't mix voltage levels without translation

I2C and SPI signal levels depend entirely on the pull-up/supply voltage each device expects (commonly 3.3 V or 5 V). Wiring a 5V device's SDA/SCL directly to a 3.3V-only microcontroller can exceed the input tolerance of the 3.3V part — use a level shifter, don't assume compatibility.

  • SPI's lack of acknowledgment means the controller has no built-in way to know if a device actually received data correctly — error checking, if any, must be built into the higher-level protocol.
  • UART has no flow control by default; if the receiver can't keep up, bytes are simply lost unless hardware flow control (RTS/CTS) or a higher-level protocol handles it.
  • I2C's open-drain design limits both speed and bus length — long wires or many devices add capacitance, which is why speed modes specify a maximum bus capacitance alongside a clock rate.

Comparisons

AspectI2CSPIUART
Pin count2 (SDA, SCL) + ground4+ (MOSI, MISO, SCLK, + 1 CS per device)2 (TX, RX) + ground
ClockShared (SCL)Shared (SCLK)None — fixed baud rate agreed in advance
DuplexHalf-duplex (one data line)Full-duplexFull-duplex (separate TX/RX wires)
AddressingYes — 7/10-bit device address on the busNo — one dedicated chip-select line per deviceNo — point-to-point only
Typical speedUp to 100 kbit/s (Standard) - 3.4 Mbit/s (High-speed)Often tens of MHz, device-dependent (no fixed ceiling)Typically 9.6 kbit/s - a few Mbit/s
TopologyMulti-master, multi-slave, one shared busOne controller, multiple targets via separate CS linesStrictly point-to-point (two devices)
StandardizationFormal spec (NXP/Philips UM10204)De facto standard, no formal spec (Motorola-originated)Loosely standardized (framing conventions, not electrical)
Typical useLow-speed sensors, EEPROMs, RTCs on a PCBSD cards, displays, flash chips — anything needing throughputDebug consoles, GPS modules, simple point-to-point links

References

  • NXP Semiconductors, UM10204: I2C-bus specification and user manual — the authoritative I2C reference; defines Standard-mode (100 kbit/s), Fast-mode (400 kbit/s), Fast-mode Plus (1 Mbit/s), and High-speed mode (3.4 Mbit/s).
  • Motorola / NXP, AN991: Using the Serial Peripheral Interface to Communicate Between Multiple Microcomputers — the closest thing to an "official" SPI reference document.
  • The Linux Kernel documentation, Overview of Linux kernel SPI support — explicitly notes SPI's lack of a formal standard.

Books & Videos