Skip to content
Tech Interview Prep home
Technical interview guide

Communication Protocols: SPI, I2C, UART

The three workhorse serial buses for talking to peripherals and other boards — how each is wired and clocked, and when to reach for which.

Read
60 min
Practice MCQs
25
Interview QA
25
Edition
v3
Editorial status
Review pending

Scope: NXP UM10204 revision 7; CMSIS-Driver 6 SPI/I2C/USART interfaces; current Zephyr peripheral APIs reviewed 2026-09-04.

Overview

Curated: · Written: · Reviewed:

SPI, I2C, and UART are electrical and framing mechanisms, not complete application protocols. A robust link contract also defines voltage compatibility, topology, pull-ups or termination, pin multiplexing, clocking, bit order, byte order, framing, addressing or chip selection, transaction boundaries, timeout, integrity, retry safety, reset synchronization, concurrency ownership, and observability. A successful driver return only proves the driver's documented completion condition, not that a remote device accepted the intended semantic operation.

SPI is synchronous and typically full duplex: clock polarity and phase, word width, bit order, chip-select timing, dummy clocks, and device-specific command phases must match. I2C uses open-drain/open-collector signaling with pull-ups, START/STOP and repeated START, address and acknowledge phases, possible clock stretching and arbitration, and rise-time/capacitance limits. UART is asynchronous: both endpoints must agree on baud, data bits, parity, stop bits, polarity, and higher-level frame recovery despite noise, drops, or reboot mid-frame.

Drivers must serialize bus ownership, use bounded state machines, distinguish transport completion from protocol success, and handle NACK, arbitration loss, mode fault, overrun, framing/parity error, timeout, cancellation, and peripheral reset. DMA and interrupt paths add buffer-lifetime, cache-coherency, and completion-order requirements. Volatile does not provide DMA cache coherence. Retries are safe only when the operation is idempotent or guarded by transaction identity.

The production invariant is transaction-boundary fidelity: every transmitted or received byte is attributable to one configured transaction with a known peer, framing, deadline, buffer owner, outcome, and recovery path. Logic-analyzer captures, device status, driver events, counters, and release configuration must make electrical, transport, and application failures distinguishable.

Electrical numbers decide whether the protocol exists. I2C Fast-mode (400 kHz) wants ≤300 ns rise time; with 4.7 kΩ pull-ups on 400 pF of cable the RC is 1.88 µs, so the clock never reaches Vil in time and slaves stretch or NACK. Changing to 1.0 kΩ pull-ups on the same bus gives 400 ns, still too slow; 330 Ω and a shorter stub at 80 pF yields 26 ns and a working 400 kHz. UART baud error is similar: 16 MHz / (16 × 115200) is 8.68, rounded to 9, actual baud 111111, error −3.5%, which many receivers tolerate; at 3% clock mismatch on both ends the combined 6.5% drops framing. SPI mode is a one-bit disaster: CPHA 0 versus 1 samples on opposite edges; a 10 MHz transfer of a 0xA5 command becomes 0x4B on the slave and the status register write lands in a reserved field.

Retries are only safe when the operation is idempotent. A non-idempotent I2C write of “increment setpoint” that NACKs after the data byte may have already been ACKed internally; retrying steps twice. The failure looked like a 2 °C overshoot every time the bus glitched. Guard with a transaction id or write an absolute setpoint. DMA versus FIFO is a lifetime question: a 32-byte SPI FIFO emptied by a 1 µs ISR at 20 MHz SPI (1.6 µs for 32 bits) races; DMA into a buffer the task reuses 40 µs later without waiting for the TC flag aliases the next transfer. Bus mutexes must cover configuration through chip-select deassert: a 12 MHz flash transaction interrupted so a 1 MHz display can run leaves CS low on the flash for 4 ms and that part interprets display clocks as commands. Capture the whole transaction on a logic analyzer against the driver’s completion callback timestamp; if they disagree, the driver is lying about the wire.Clock stretch on I2C is a timeout policy. A slave that holds SCL for 25 ms while the master timeout is 10 ms aborts; the slave still thinks the transaction is live and the next START is a bus error. Specify stretch budget in the contract (SMBus 25 ms is a common cap) and recover with a 9-clock toggle sequence that is tested, not hoped. 10-bit addressing mixes with 7-bit devices if the driver sends 0x78 as a 7-bit address; 0x78 is the 10-bit header. 9-bit UART for multidrop needs the address bit set only on the first byte; setting it on every byte means nobody recognizes data.

Dummy clocks on SPI flash reads: a Winbond after 0x03 wants the 24-bit address then data; a QSPI 0xEB wants 8 dummy cycles at the current dummy-cycle register, often 6 or 8 depending on frequency. At 48 MHz with 4 dummy cycles the first data byte is still mode bits and the filesystem sees 0xFF. Own the dummy-cycle table with the clock. FIFO thresholds: RXNE at 1/4 of a 32-byte FIFO on 10 MHz SPI (3.2 µs per byte) means 8 bytes × 3.2 µs = 25.6 µs between IRQs; if the ISR is delayed 40 µs by a flash erase ISR, 4 bytes overflow. DMA is the remedy, with TC and error IRQs, not a bigger FIFO hope.

Link verification is a release-image ritual that starts at a logic analyzer and ends at a transaction id. Record SYSCLK, wait states, compiler flags, .map sizes, painted stack high-water marks, ISR GPIO timing, logic-analyzer traces of CS/SCK/SDA, current-shunt waveforms at not less than 100 kHz, reset-cause and fault registers, and the boot slot/security counter after every power-loss injection. A pass is a number that can be recomputed from those artifacts: flash LOAD versus FLASH LENGTH, ISR high-water versus period, Stop current versus the schematic budget, confirm window versus the health checks, and disable-to-deny for debug and keys. If the only evidence is a green LED, a UART log, or a debugger session on an -O0 build, the claim is unpublished. Repeat the same measurements at the temperature and voltage corners the datasheet allows, because flash wait states, Stop leakage, crystal error, and brownout thresholds all move, and a 25 °C passing suite is not a 85 °C passing suite.

Bus recovery must be a tested state machine: nine clocks, STOP, bus-free wait of 50 µs, then retry once with a new transaction id. Infinite retry on NACK at 400 kHz with a missing slave is 100% CPU in the driver and a watchdog event. UART idle-line 9-bit addressing needs a 1-character idle gap at 115200 of about 95 µs; tighter gaps merge frames. Own those numbers in the contract.

Worked example: 4.7 kOhm on 400 pF is not Fast-mode

I2C Fast-mode wants rise time at most 300 ns.

pull-up and loadRC400 kHz SCL
4.7 kOhm, 400 pF cable1.88 usslaves stretch or NACK
1.0 kOhm, same cable400 nsstill over the spec
330 Ohm, 80 pF stub26 nsbus ACKs

The driver returning success only proved the master's state machine. The wire decides whether Fast-mode exists.