Engineering Guides · Module 04
Wiring and configuring UART, SPI and I2C
Most failures of simple serial links are a mismatch of settings: baud, mode, voltage level, address or pull-up.
Assumptions: Numbers in the examples are illustrative and the code is executed by the site’s tests. Facts taken from a cited source are marked; other statements are general engineering knowledge and must be confirmed on the datasheet of the part you use.
1. Engineering problem
UART, SPI and I²C are simple to wire and unforgiving about settings. Both ends must agree exactly on speed and format, and the electrical levels and wiring must suit the bus.
2. When is this needed?
- Connecting a GNSS receiver, IMU or small sensor board to a controller.
- When a link works on the bench and fails on the robot.
- When reading a datasheet’s timing and wiring diagrams.
3. Selection and design decisions
Sourced facts (Wikipedia): a UART frame sends a start bit (logic low), the data bits and one or two stop bits (logic high), so 8-N-1 is 10 line bits per byte; SPI has a clock polarity (CPOL) and phase (CPHA) that the controller must set to match the device, with an active-low slave-select line; I²C uses SDA and SCL, both bidirectional and pulled up with resistors, with a 7-bit address space and common speeds of 100 and 400 kbit/s.
| Bus | Wires | Settings that must match | Typical wiring faults |
|---|---|---|---|
| UART | TX, RX, GND (cross TX and RX) | Baud rate, data bits, parity, stop bits, voltage level | TX to TX; different grounds; 3.3 V and 5 V mixed |
| SPI | SCLK, MOSI, MISO, one select per device, GND | Clock polarity and phase (mode 0 to 3), clock rate, bit order | Wrong mode; long wires at high clock; floating select line |
| I²C | SDA, SCL, GND, pull-ups | Address, speed, voltage | Missing or too weak pull-ups; address clash; too much bus capacitance |
4. Advantages and disadvantages
- Higher speed shortens the cable you can use.
- Fewer wires (I²C) against slower speed and bus-sharing faults.
- Level shifters add parts and are needed when voltages differ.
5. Requirements and constraints
- All devices share a common ground.
- Voltage levels match, or a level shifter is used.
- Baud error within the receiver’s tolerance in the datasheet.
6. Architecture
controller TX ──▶ RX device SPI: SCLK, MOSI ──▶ device, MISO ◀── device, CS per device
controller RX ◀── TX device I²C: SDA, SCL (pull-ups to Vcc) shared by addressed devices7. Implementation
- Read the datasheet for baud, mode, address and voltage.
- Compute the actual baud rate from the clock divider (below) and its error.
- Set SPI CPOL and CPHA to the device mode; mode = 2·CPOL + CPHA in the usual numbering.
- Wire with a common ground, correct crossing and pull-ups.
- Verify with a logic analyser or oscilloscope before writing the driver.
def uart_error(clock_hz, baud, oversample=16):
div = round(clock_hz / (oversample * baud))
actual = clock_hz / (oversample * div)
return div, actual, (actual - baud) / baud * 100
for clk in (16e6, 12e6):
d, a, e = uart_error(clk, 115200)
print(f"{clk/1e6:.0f} MHz: divisor {d}, actual {a:.0f} baud, error {e:+.2f} %")
def spi_mode(cpol, cpha):
return 2 * cpol + cpha
print("CPOL=1 CPHA=0 -> SPI mode", spi_mode(1, 0))Printed (checked by the site’s tests):
16 MHz: divisor 9, actual 111111 baud, error -3.55 %
12 MHz: divisor 7, actual 107143 baud, error -6.99 %
CPOL=1 CPHA=0 -> SPI mode 2Reading it: a 16 MHz clock divided by 16 × 9 gives 111 111 baud, 3.55 % below 115 200; a 12 MHz clock gives 107 143 baud, 6.99 % low. Whether that error is acceptable depends on the receiver’s tolerance, which is in its datasheet; fractional dividers or a different clock reduce it. CPOL = 1 with CPHA = 0 is SPI mode 2. Clock values are illustrative.
8. Real examples
- GNSS on UART: the receiver’s default baud is set in its datasheet; set the controller to match and raise both together.
- IMU on SPI: the datasheet gives the mode; set it and check a known register (such as a device-ID register) first.
9. Failure scenarios
- Reading garbage because the baud rate error is too large or the receiver is set to another baud.
- SPI returns all zeros or ones due to wrong mode or a floating select line.
- I²C works with one device and fails with two (address clash or pull-ups).
10. Troubleshooting
- No data: swap TX and RX, check ground and voltage.
- Garbage: check baud and frame format.
- SPI wrong values: try the other modes and slower clock.
- I²C no acknowledge: check address, pull-ups and power.
11. Validation
- The example prints the lines shown (executed by the site tests).
- A known register or ID reads back correctly 1000 times in a row.
12. Engineering checklist
13. Further references
- Wikipedia, UART, SPI and I²C, read 2026-11-06 (secondary sources; the I²C specification itself was not read).
Related on FixTheRobot
- Nonlinear Least Squares SLAM · Lesson