Reading the same sensor register may involve sending a vendor command first via UART, locating the register and switching to read mode via I²C, or continuously sending commands, addresses, and dummy clocks via SPI. Protocol names only describe part of the transmission rules; actually reading data depends on device command formats, timing, and electrical connections. This article traces three interfaces through a single transmission and then calculates effective throughput.
Interface
Synchronization & Common Signals
Who Defines Data Boundaries
Often Overlooked Issues
UART
Asynchronous, TX/RX
Start, data, parity, and stop bits form a character; messages are defined separately
Clock error, frame boundaries, receive overflow
I²C
Synchronous, SCL/SDA
Address, acknowledgment, START/STOP
Address representation, repeated START, stretching, and rise time
SPI
Synchronous, SCK, data lines, CS
Device-defined chip select and clock sequences
Mode, setup time for the first bit, dummy cycles, MISO release
The table does not include power, reference ground, and additional interrupt/flow control pins. Serial describes transmission bit by bit over time, which does not mean all interfaces have only one wire; SPI has multiple signals in different directions, QSPI has multiple data lines, and high-speed serial can use multiple lanes. Parallel interfaces are still useful; it is incorrect to say that all modern high-speed interfaces are serial.
UART: Each side times itself, realigning at every start bit
In common non-inverted UART, the idle state is high, the start bit pulls low, and data bits are sent at the agreed baud rate. 8N1 means 8 data bits, no parity, and 1 stop bit; the stop bit is high. Transmission and reception can operate independently, but implementations with a single wire or external half-duplex transceivers may not support simultaneous transmission and reception.
The binary representation of 0xA5 written from MSB to LSB is 10100101. UART typically sends D0 first, so the data bit order is 1, 0, 1, 0, 0, 1, 0, 1. The complete character includes start and stop bits, totaling 10 bits. At 115200 bit/s continuous transmission, the theoretical effective data rate is 115200/10 = 11520 byte/s; 100 bytes take approximately 8.681 ms, not yet accounting for message headers, parity, inter-byte gaps, and device wait times.
Baud rate error accumulates; don't just look at identical settings
After the receiver identifies the start bit, it uses its local clock to estimate each bit center. If the receive and transmit clocks differ by 2%, the idealized cumulative offset is about 0.19 bits after approximately 9.5 bit times from the start. Adding start detection error, jitter, and edge distortion further narrows the usable window. Configuring both ends to 115200 does not guarantee that crystal oscillators and dividers produce identical results.
Allowable error depends on data/parity length, oversampling method, and specific receiver; it is inappropriate to write a fixed ±2% that applies to all UARTs. Combine the clock accuracy, temperature drift, and divider error of both ends, and consult the tolerance table of the target hardware. Microchip: Asynchronous Receiver Operating Range
Correct waveforms can still lose data: if the receive FIFO is full and software/DMA does not read in time, an overrun occurs; if the stop bit position is not a valid high level, a framing error is generated; parity can only detect some errors and cannot replace message CRC. UART itself has no unified application message format; you can adopt explicit length, delimiters, escaping, parity, and timeouts; do not assume one read corresponds to one complete command.
RTS/CTS can provide hardware flow control between devices, but you must consider characters still in transit before the other party stops sending and the FIFO capacity. An empty TX FIFO does not necessarily mean the final stop bit has been fully transmitted from the pin; disabling the transmitter or deasserting RS-485 DE should use a true transmission complete condition. UART can participate in multi-point systems via electrical layers like RS-485; it cannot be broadly limited to "one-to-one, below 1 Mbit/s".
How do samples drift away from bit centers?
Positive error means a faster receiver and samples drifting left; negative error drifts right. Change the byte: displaced samples may still decode correctly when neighboring bits agree. One successful character does not validate a timing budget.
Δtk=(k+1.5)Ttx(1+ε1−1)
Preparing the visual
How do samples drift away from bit centers?
Clock mismatch accumulates across a character; aligned starts do not ensure centered later samples.
I²C: Bus control, byte acknowledgment, and device registers are three layers of concepts
In common Standard/Fast/Fast-mode Plus bidirectional modes, SDA and SCL use open-drain/open-collector form with pull-ups. The controller schedules the clock, but the final bus level is determined by all connected devices; if a target device supports clock stretching, it can continue to pull SCL low after the controller releases it. The controller must observe the actual SCL, and both the driver and device must support the features used.
Normal data bits remain stable during the SCL high period. SDA falling during SCL high defines START, and rising defines STOP. After each 8-bit byte, there is a 9th acknowledgment clock: the transmitter releases SDA, and the receiver pulling it low indicates ACK, while keeping it high indicates NACK. NACK is not necessarily a fault; after reading the last byte, the controller usually actively sends NACK to indicate no further bytes will be received. NXP: I²C Bus Specification
For a continuous read, first clarify who drives SDA
Assume a teaching device uses a 7-bit address 0x50, 8-bit register address, and register 0x10 currently holds 0x5A. The requirement is to read it using a repeated START:
The controller first sends the address byte (0x50<<1)|0=0xA0, and the target ACKs; then it sends the register pointer 0x10, and the target ACKs. The controller does not release the bus, sends a repeated START, and then sends the read address byte 0xA1. After the target ACKs, it becomes the data transmitter. The controller reads 0x5A, sends NACK on the 9th clock, and finally sends STOP.
There are four address/data bytes here, each occupying 9 clocks, totaling 36 SCL cycles. At 400 kHz, these clocks alone take 90 μs, plus START/STOP, bus idle time, possible stretching, and software gaps. When continuously reading N bytes and the device supports automatic address increment, the leading overhead can be amortized over 9(N+3) clocks; the first N−1 data bytes are ACKed by the controller, and the last one is NACKed.
The 7-bit device address 0x50 and the 0xA0/0xA1 on the wire are two different representations. Most APIs accept 7-bit addresses, but some libraries require the left-shifted value; check the API and do not left-shift twice. 10-bit addressing has a specific header format; do not stuff a 10-bit value directly into a single byte. Addresses are usually fixed by the device or configured via address pins, and are not naturally globally unique; address conflicts require changing the address, segmentation, or using multiplexers.
ACK is not business success, arbitration is not a software lock
Address NACK may indicate no device, wrong address, power loss, or the device is temporarily unresponsive; data ACK only indicates that this byte was acknowledged, and does not necessarily mean the EEPROM has completed internal programming. Write-wait, busy state, or ACK polling must be arranged according to the device protocol; do not assert non-volatile data is written just based on bus ACK.
In multi-controller scenarios, open-drain bus bit-by-bit arbitration is used: sending a logical 1 means releasing SDA. If you actually read 0, it means another party is sending a prioritized low level, and the current controller should exit according to the specification. Whether hardware fully supports multi-controller, stretching, and arbitration depends on the specific controller and driver; "bus specification allows" does not mean every peripheral implementation allows it.
When SDA is stuck low, the system should not block indefinitely. First, timeout to confirm the fault, then execute recovery according to controller/device and bus status; the specification provides a bus clear method that attempts clock pulses when SDA is stuck low, but if SCL is continuously pulled low, the device is powered off, or there is a hardware short circuit, blindly sending nine clocks cannot guarantee recovery. After recovery, you should also confirm the device's internal transaction status to avoid treating an interrupted write as a complete success.
Rise time depends on the mode; don't turn frequency into a fixed resistor
Common Mode
Max SCL Frequency
Max Rise Time, 30%–70%
Standard-mode
100 kHz
1000 ns
Fast-mode
400 kHz
300 ns
Fast-mode Plus
1 MHz
120 ns
Higher speed modes have different entry procedures and electrical constraints; the table above is not the complete set of all I²C modes. Match the rise-time limit to the operating mode; it cannot be inferred from the clock period alone. Rp,max ≈ tr/(0.8473Cb), and you must also use Rp,min ≈ (Vpullup−VOL,max)/IOL to limit sink current. NXP: UM10204 Timing Table and Pull-up Design
For example, a 200 pF bus with 4.7 kΩ has a rise of about 796 ns, which fits within the Standard-mode rise time requirement but does not satisfy Fast-mode. At 400 kHz, calculating the upper limit based solely on rise time gives about 1.77 kΩ; it must also form a feasible range with the sink current lower limit. When multiple modules on the board each have their own pull-ups, the equivalent value is parallel, and capacitance also increases; do not just look at the resistance of one board.
SPI: Shared clock, but you must still allow setup time for data
In typical four-wire SPI, the controller outputs SCK, MOSI, and CS_n, and the peripheral outputs MISO. When sharing a bus, independent CS is usually used to select peripherals; peripherals that are deselected must release MISO according to the manual. There are also variants such as three-wire half-duplex, write-only, no independent chip select, Dual/Quad, etc., so "four wires" is just a common structure.
Mode
CPOL: Idle Level
CPHA
Sampling Edge
Typical Edge for Updating Data
0
0
0
Rising
Falling
1
0
1
Falling
Rising
2
1
0
Falling
Rising
3
1
1
Rising
Falling
CPHA=0 samples at the first active edge, so the first bit must be prepared in advance; CPHA=1 samples at the second edge, and the first edge is used to push out the first bit. CPOL determines whether the first active edge is rising or falling. Do not just memorize "read on rising edge" and miss the idle polarity and chip select timing. Analog Devices: SPI Interface
Assume the half-period for Mode 0 provides a 20 ns budget for the peripheral to output, while the peripheral clock-to-MISO delay is 12 ns, line and on-board propagation is 4 ns, and the controller setup requirement is 5 ns. The total requirement is 21 ns, which is still insufficient. Total clock frequency, sampling delay options, load, and wiring must all participate in the budget; do not ignore specific modes and timing conditions just because both sides claim to support the same MHz.
There is a clock for shifting, but what is shifted in may not be valid data
Assume the device defines a read operation as 8-bit command, 24-bit address, 8 dummy clocks, and then returns 16 bytes. The entire transaction requires 8+24+8+16×8=168 clocks. At 10 MHz, this takes 16.8 μs, with an effective payload of about 7.62 Mbit/s; even without an I²C-style address phase, the SPI device's own commands, addresses, and waits are still overhead.
When reading data, the controller usually still needs to send placeholder bytes to generate clocks; the MISO bits received during the command phase may be invalid. You must decide when to discard and when to store in the receive buffer according to the device definition. Whether CS_n needs to stay low across all bytes, whether pauses are allowed between bytes, and how long to hold at the end are all part of the transaction format; do not simply treat each byte as an independent access.
Daisy-chaining only works if the devices support serial shift semantics; do not connect the MISO of any arbitrary SPI peripheral to the MOSI of the next one. Basic SPI has no unified ACK or flow control, but this does not mean devices cannot provide busy bits, READY pins, CRC, or retry rules. Ethernet chips also need to distinguish between controllers accessed via SPI and PHYs managed using MDIO/MDC; do not collectively refer to "all Ethernet PHYs using SPI".
How to read an abnormal transmission
Observation
Priority Check
Further Localization
UART characters stable but overall garbled
Baud rate, data bits, parity, inversion
Raw bytes and encoding, message format
UART sporadic loss of segments
Overrun, DMA/FIFO, flow control
Blocking time and message checksum
NACK after I²C address
7-bit/8-bit address, power, address pins
Device busy, rising edge, bus segment
I²C permanent wait
SCL stretching or line stuck low
Timeout, recovery, device status
SPI data shifted by one bit overall
CPOL/CPHA, first bit setup
CS timing, sampling edge, and propagation delay
SPI fails only when multiple devices are connected
MISO high-impedance and chip select
Pull-ups, load, conflicting drivers
A logic analyzer can explain bit order and transactions, but slow edges near thresholds, overshoot, ground bounce, and contention still require analog waveforms. A decoder showing ACK or a hexadecimal value does not prove sufficient electrical margin.
Interface selection ultimately depends on device support, throughput, pin budget, topology, and power state. I²C idle pull-ups usually do not consume continuous DC, but bus low level, device static power consumption, and communication duty cycle will change energy consumption; do not directly list I²C as the lowest power solution for all battery-powered devices. Long-line differential choices can select appropriate physical layers, but RS-485 does not limit itself to carrying only UART character formats.
Recalculate frames and throughput
frommathimportisclosevalue=0xA5bits=[(value>>i)&1foriinrange(8)]assertbits==[1,0,1,0,0,1,0,1]assertsum(bit<<ifori,bitinenumerate(bits))==valueassertisclose(100*10/115200,0.008680555555555556)assert(0x50<<1,(0x50<<1)|1)==(0xA0,0xA1)assertisclose(36/400000,90e-6)assert0.8473*4700*200e-12<1000e-9assert0.8473*4700*200e-12>300e-9assert12+4+5>20cycles=8+24+8+16*8assertcycles==168assertisclose(cycles/10e6,16.8e-6)assertisclose(128/(cycles/10e6),7.619047619e6,rel_tol=1e-9)print('UART bit order, I²C address/clocks, and SPI transaction throughput checks passed')
These checks recalculate teaching transactions and do not represent connected real UART, sensors, or SPI Flash.