Common Bus Protocols#
Three serial protocols dominate low-to-medium-speed digital communication between ICs on a PCB: SPI, I2C, and UART. Each makes different tradeoffs between pin count, speed, complexity, and flexibility. This page covers the hardware and timing aspects — the electrical signals, the clocking, and the timing diagrams — not the software drivers or register-level programming.
SPI (Serial Peripheral Interface)#
A synchronous, full-duplex, master-slave serial bus. Developed by Motorola in the 1980s, SPI is the standard for high-speed communication between a controller and peripherals (ADCs, DACs, flash memory, displays, sensors).
Signals:
- SCLK — Serial clock, generated by the master
- MOSI — Master Out, Slave In (data from master to slave)
- MISO — Master In, Slave Out (data from slave to master)
- CS (or SS) — Chip Select, active LOW, one per slave
Operation:
- Master asserts CS (LOW) to select the target slave
- Master generates SCLK and shifts data out on MOSI
- Simultaneously, the selected slave shifts data out on MISO
- After the transfer, master deasserts CS (HIGH)
SPI is fundamentally a pair of shift registers connected in a ring — the master’s shift register and the slave’s shift register exchange data bit-by-bit on each clock edge.
Clock modes (CPOL and CPHA):
- CPOL — Clock polarity. 0 = idle LOW, 1 = idle HIGH
- CPHA — Clock phase. 0 = data sampled on leading edge, 1 = data sampled on trailing edge
- Four combinations (Mode 0-3) define when data is valid relative to the clock. Both master and slave must agree on the mode
Timing considerations:
- Clock frequency: typically 1-50 MHz, some devices support 100+ MHz
- No protocol-level overhead — every clock cycle transfers one bit in each direction. Throughput equals clock frequency (for single-bit mode)
- Setup and hold timing on MOSI/MISO relative to SCLK must be met. At high frequencies, the propagation delay from master SCLK output to slave SCLK input (and back for MISO) limits the maximum frequency
- No acknowledgment mechanism — the master has no way to know if the slave received the data correctly (unless the protocol layer adds one)
Multi-slave topologies:
- Independent CS lines — Most common. Each slave has its own CS. Only one is selected at a time. Pin count grows with slave count
- Daisy chain — MISO of one slave connects to MOSI of the next. Data shifts through all slaves in series. Requires all slaves to support daisy-chaining
Hardware features:
- Quad SPI (QSPI) uses 4 data lines instead of 1, quadrupling throughput. Common for flash memory
- Dual SPI uses 2 data lines. Both are extensions of the basic SPI concept, trading pin count for speed
I2C (Inter-Integrated Circuit)#
A synchronous, half-duplex, multi-master serial bus. Developed by Philips (now NXP). I2C uses just two wires for communication, making it ideal for connecting many low-speed peripherals (sensors, EEPROMs, RTCs, I/O expanders) with minimal pin count.
Signals:
- SCL — Serial clock
- SDA — Serial data
Both lines are open-drain with external pull-up resistors. Any device can pull the line LOW; the pull-up pulls it HIGH when released. This wired-AND topology enables multi-master capability and clock stretching.
Operation:
- Master generates a START condition (SDA goes LOW while SCL is HIGH)
- Master sends a 7-bit slave address plus a read/write bit
- The addressed slave responds with an ACK (pulls SDA LOW during the 9th clock cycle)
- Data bytes transfer, each followed by ACK/NACK
- Master generates a STOP condition (SDA goes HIGH while SCL is HIGH)
Addressing:
- 7-bit addressing: up to 128 devices (minus reserved addresses), giving about 112 usable addresses
- 10-bit addressing: for systems needing more addresses (rarely used)
- Each device has a fixed or partially configurable address. Address conflicts are a common integration problem — two sensors from different manufacturers with the same address cannot share a bus without an I2C multiplexer
Speed modes:
- Standard mode: 100 kHz
- Fast mode: 400 kHz
- Fast mode plus: 1 MHz
- High-speed mode: 3.4 MHz
Timing considerations:
- Rise time depends on pull-up resistor value and bus capacitance. The I2C specification limits bus capacitance to 400 pF (standard mode). More capacitance requires stronger pull-ups (lower resistance) but increases power consumption
- Pull-up resistor sizing: too large = slow rise time (may violate timing), too small = excessive LOW-state current and inability of weak devices to pull the bus LOW
- Clock stretching: a slave can hold SCL LOW to pause the master while it processes data. The master must detect this and wait. Not all masters support clock stretching properly
Electrical characteristics:
- Open-drain signaling means signal integrity is generally good — no bus contention, no drive fights
- But the asymmetric rise/fall (slow rise, fast fall) limits speed. At higher I2C speeds, the pull-up resistor value becomes critical
- Level shifting is natural: a MOSFET-based level shifter works bidirectionally on open-drain lines
UART (Universal Asynchronous Receiver-Transmitter)#
An asynchronous, full-duplex, point-to-point serial interface. No clock line — both sides must agree on the baud rate in advance.
Signals:
- TX — Transmit data (output)
- RX — Receive data (input)
- Optional: RTS/CTS (flow control), DTR/DSR (modem control)
Frame format:
- 1 start bit (LOW)
- 5-9 data bits (usually 8)
- Optional parity bit (even, odd, or none)
- 1-2 stop bits (HIGH)
The most common configuration: 8N1 (8 data bits, no parity, 1 stop bit) = 10 bit-times per byte.
Operation:
- The line idles HIGH
- The transmitter pulls the line LOW for one bit-time (start bit)
- Data bits follow, LSB first, each held for one bit-time
- Optional parity bit
- Stop bit(s) — line returns HIGH for 1-2 bit-times
- Line idles HIGH until the next byte
Baud rate and timing:
- Common baud rates: 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
- The receiver uses the start bit’s falling edge to synchronize, then samples each subsequent bit at the center of the bit-time
- Both transmitter and receiver must agree on the baud rate to within about ±2-3%. If the clocks drift further apart, the receiver samples at the wrong time and data is corrupted
- At 115200 baud: one bit is ~8.7 us, one byte is ~87 us, maximum throughput is ~11.5 KB/s
Voltage levels:
- UART is a protocol, not a voltage standard. The electrical interface varies:
- TTL/CMOS UART — 0 V / 3.3 V or 0 V / 5 V. Directly from the microcontroller or FPGA
- RS-232 — ±3 to ±15 V (typically ±12 V). Inverted logic (positive = logic 0). Needs a level shifter (MAX232 or similar) to connect to CMOS logic
- RS-422 / RS-485 — Differential signaling for noise immunity and long distances. RS-485 supports multi-drop (multiple devices on one pair)
Flow control:
- None — The transmitter sends at will. The receiver must keep up. Data is lost if the receiver’s buffer overflows
- Hardware (RTS/CTS) — The receiver deasserts CTS when its buffer is nearly full, signaling the transmitter to pause. Reliable but requires extra wires
- Software (XON/XOFF) — Special characters in the data stream signal pause and resume. No extra wires, but the control characters cannot appear in the data (problematic for binary data)
Comparing the Three#
| Feature | SPI | I2C | UART |
|---|---|---|---|
| Wires | 4+ (SCLK, MOSI, MISO, CS per slave) | 2 (SCL, SDA) | 2 (TX, RX) |
| Clocking | Synchronous (master provides clock) | Synchronous (master provides clock) | Asynchronous (baud rate agreement) |
| Duplex | Full | Half | Full |
| Speed | 1-100+ MHz | 100 kHz - 3.4 MHz | 9600 baud - 1+ Mbaud |
| Multi-device | One CS per slave | Addressing (112+ devices on 2 wires) | Point-to-point |
| Overhead | None (raw bits) | Address + ACK per transfer | Start/stop bits per byte |
| Complexity | Low | Medium | Low |
Tips#
- Always verify SPI clock mode (CPOL/CPHA) against the specific device datasheet — there is no universal default
- Size I2C pull-up resistors based on bus capacitance and speed mode — calculate or use NXP’s guidelines
- Verify UART baud rates on both ends before debugging protocol issues — rate mismatch is the most common problem
- Plan for I2C address conflicts during system integration — check all device addresses before committing to a bus topology
Caveats#
- SPI has no standard — Unlike I2C (which has a detailed specification from NXP), SPI was never formally standardized. Different manufacturers implement variations: different word sizes, different CS behavior, different byte ordering. Always check the specific device’s datasheet
- I2C pull-up resistors are not optional — Without pull-ups, the bus stays at whatever voltage the parasitic capacitance holds. The signals will be sluggish, unreliable, or completely nonfunctional. Size the pull-ups for the bus capacitance and speed mode
- UART baud rate mismatch produces garbage — If the transmitter and receiver are at different baud rates, the received data is corrupted. There is no handshake or negotiation — both sides must be configured correctly before communication begins
- I2C address conflicts are a system integration problem — Two devices with the same address on the same bus cannot coexist. Solutions include I2C multiplexers (TCA9548A), alternate addresses (some devices have address pins), or separate buses
- Long SPI traces at high speed need signal integrity attention — At 50 MHz, a 10 cm SPI trace is a significant fraction of a wavelength. Impedance matching and termination may be needed
- RS-232 voltage levels will damage CMOS inputs — Connecting an RS-232 signal (±12 V) directly to a 3.3 V CMOS UART input will destroy the chip. Always use a level translator (MAX3232 or equivalent)
In Practice#
- I2C signals with slow rise times and rounded edges indicate pull-up resistors are too large for the bus capacitance
- SPI data that appears shifted or inverted suggests clock polarity or phase mismatch — verify CPOL/CPHA settings
- UART output that displays as gibberish on a terminal almost always indicates baud rate mismatch
- I2C transactions that NAK unexpectedly may be caused by address conflicts or missing pull-ups