ALearning Material
Microcontrollers rarely work alone: they talk to sensors, displays, memory, and other chips. They do so over serial communication protocols, where data is sent one bit at a time over a few wires. The three you'll use constantly (UART, SPI, and I2C) each suit different situations, and knowing which to use (and how each works) is essential to interfacing any peripheral.
UART (Universal Asynchronous Receiver/Transmitter): asynchronous point-to-point serial: two wires (TX, RX), no shared clock (both sides agree on a baud rate). Simple, common for device-to-device or to a PC (e.g. debug output, a GPS module). Two devices only.
SPI (Serial Peripheral Interface): synchronous (a shared clock), fast, full-duplex, master/ slave with usually 4 wires (clock SCLK, MOSI, MISO, and a chip-select per slave). Fast and simple per-device, but needs a separate chip-select line for each slave. Good for high-speed peripherals (displays, SD cards, fast ADCs).
UART: TX <-> RX (2 wires, async, baud rate, 2 devices)
SPI: SCLK, MOSI, MISO, + CS per slave (sync, fast, full-duplex, a wire per slave)
I2C: SDA, SCL (2 wires shared bus, addressed devices, many on one bus)
I2C (Inter-Integrated Circuit): synchronous, only two wires (data SDA + clock SCL) shared by many devices, each with a unique address. Slower than SPI but very wire-efficient: many sensors on one two-wire bus. Great for connecting lots of low-speed sensors. The choice: UART for simple two-device async links; SPI for speed (few devices, but a wire per slave); I2C for many devices on few wires (addressed bus, slower). The disciplines: know the three protocols and their wires/speed/topology, and choose by the situation (simple link / speed / many-devices-few-wires). These are how an MCU connects to the world.
Why it exists. A microcontroller must communicate with many peripherals (sensors, displays, memory, other MCUs), and serial protocols send data over a few wires. UART, SPI, and I2C are the three standard choices, each with different speed, wiring, and topology trade-offs, so knowing how each works and when to use it is essential to interfacing any peripheral, the core of connecting an embedded system to the world.
Mental model. The three protocols are like three ways to have a conversation. UART is two people talking directly with no clock, just an agreed pace (baud rate), simple but only two of them. SPI is a fast conversation led by a conductor's beat (the clock), but you need a separate 'attention' line to address each listener. I2C is a group on a party line (two shared wires) where you call each device by name (address) before talking. Many can share, but it's slower.
Common misunderstandings.
- "All serial protocols are interchangeable." They differ in speed, wires, and topology: UART (2 devices, async), SPI (fast, a wire per slave), I2C (many devices, 2 shared wires, addressed, slower), choose by the situation.
- "SPI vs I2C is just speed." It's also wiring/topology: SPI needs a chip-select per slave (more wires for many devices), while I2C shares two wires among many addressed devices. I2C wins on wire count, SPI on speed.
- "UART needs a clock line." UART is asynchronous, no shared clock; both sides just agree on the baud rate, which is why a mismatched baud rate garbles the data.
Connections. Serial protocols build on the GPIO/peripherals of the MCU (the GPIO lesson and the Turn-2 STM32 lesson: the MCU has UART/SPI/I2C peripherals), are how the projects read sensors and report data (the UART reporter project, the data loggers), use DMA for efficiency (the Turn-2 STM32-peripherals lesson), and connect to the C++/Python serial lessons (the host side). The standard way an embedded system talks to peripherals.
BImmediate Active Recall
QUERYWhat is UART, and what are its key characteristics?
REVEAL
UART (Universal Asynchronous Receiver/Transmitter) is asynchronous, point-to-point serial: two wires (TX, RX), no shared clock. Both sides agree on a baud rate. It's simple and common for device-to-device or MCU-to-PC links (debug output, a GPS module), but connects only two devices. A mismatched baud rate garbles the data (since there's no clock to sync to).
QUERYWhat is SPI, and when is it the right choice?
REVEAL
SPI (Serial Peripheral Interface) is synchronous (a shared clock SCLK), fast, full-duplex, master/slave, usually 4 wires (SCLK, MOSI, MISO, + a chip-select per slave). It's the right choice for high-speed peripherals (displays, SD cards, fast ADCs) and few devices. It's fast and simple per device, but needs a separate chip-select line for each slave (more wires as devices grow).
QUERYWhat is I2C, and what is its main advantage?
REVEAL
I2C (Inter-Integrated Circuit) is synchronous, using only two wires (data SDA + clock SCL) shared by many devices, each with a unique address. Its main advantage is wire efficiency (many devices on one two-wire bus (addressed)) so it's great for connecting lots of low-speed sensors. The trade-off is it's slower than SPI.
QUERYHow do you choose between UART, SPI, and I2C?
REVEAL
By the situation: UART for a simple two-device asynchronous link (e.g. to a PC/GPS); SPI for speed with few devices (accepting a chip-select wire per slave: displays, SD cards, fast ADCs); I2C for many devices on few wires (an addressed two-wire bus, slower, lots of sensors). Match the protocol to the speed, wire count, and number of devices you need.
CConceptual Questions
Answer each in your own words in the box, then reveal the model answer to compare. These ask why, not how, and your answers are saved.
Why do three different serial protocols (UART, SPI, I2C) coexist rather than one being universally best, and how do their trade-offs map to different situations?
REVEAL MODEL ANSWER
Three protocols coexist because serial communication has competing demands (speed, wire count, number of devices, and simplicity) and no single design optimizes all of them, so each protocol makes a different trade-off that's best in a different situation. UART optimizes simplicity for a point-to-point link: just two wires and no shared clock (both sides agree on a baud rate), which is dead simple and perfect for connecting two devices (an MCU to a PC for debug, to a GPS module), but it fundamentally only links two devices and its asynchronous nature means a baud-rate mismatch corrupts data. SPI optimizes speed: a shared clock makes it synchronous and fast, full-duplex (simultaneous send and receive), and simple per device: ideal for high-speed peripherals like displays, SD cards, and fast ADCs; its cost is wiring, because each additional slave needs its own chip-select line, so wiring grows with the number of devices and it doesn't scale gracefully to many peripherals. I2C optimizes wire efficiency for many devices: only two shared wires (data and clock) carry a whole bus of devices, each addressed by a unique address, so you can hang many low-speed sensors on the same two wires. Its cost is speed (slower than SPI) and a bit more protocol complexity (addressing, acknowledgement). So the trade-offs map cleanly: need a simple link between two devices? UART. Need raw speed to a few peripherals and can afford a wire per device? SPI. Need to connect many devices over as few wires as possible and can tolerate lower speed? I2C. This is the recurring engineering theme of fit-to-purpose: rather than one universal protocol that's mediocre everywhere, you have a small toolkit where you pick the one whose trade-off matches your situation's priorities (speed vs wires vs device count vs simplicity). Knowing all three and their trade-offs is what lets an embedded developer interface any peripheral well, which is exactly why they all persist and why the skill is choosing among them, not memorizing one.
Why does UART being asynchronous (no shared clock, just an agreed baud rate) make it both simple and fragile, and what does this reveal about the role of a clock in serial communication?
REVEAL MODEL ANSWER
UART's asynchronicity is the source of both its simplicity and its fragility, and contrasting it with the clocked protocols reveals what a shared clock actually does. In any serial link, bits are sent one after another on a wire, and the receiver must know when to sample the wire to read each bit correctly. It has to align its sampling to the sender's bit boundaries. Synchronous protocols (SPI, I2C) solve this by sending an explicit clock line alongside the data: the clock signal tells the receiver exactly when each bit is valid, so the two sides are inherently synchronized and the data rate can be whatever the clock runs at, even varying, without confusion. UART omits that clock line (that's what 'asynchronous' means) which makes it simple (only two wires, TX and RX, no clock to route) and is why it's so convenient for basic point-to-point links. But without a shared clock, the receiver has no signal telling it when bits are valid; instead, both sides must independently agree in advance on the bit rate (the baud rate) and the receiver uses that agreed timing (plus start bits to mark the beginning of each byte) to guess when to sample. This is the fragility: if the two sides' baud rates don't match (or a side's clock drifts too far), the receiver samples at the wrong moments and reads garbage. The data is corrupted with no clock to fall back on. So UART trades the robustness and rate-flexibility a shared clock provides for the simplicity of fewer wires, at the cost of requiring exact pre-agreement on speed and being sensitive to timing mismatch. The deeper lesson is that a clock line is the mechanism that keeps sender and receiver synchronized bit-by-bit: synchronous protocols carry it explicitly (robust, rate-flexible, but an extra wire), while asynchronous UART replaces it with a pre-shared agreed rate (simpler wiring, but fragile to mismatch). Understanding this is why 'check the baud rate' is the first thing you do when a UART link garbles data, and why SPI/I2C don't have that particular failure mode.
DPractice Problems
P1 (easy). Match each scenario to UART, SPI, or I2C: (a) connecting many low-speed sensors over as few wires as possible, (b) a fast display needing high speed, (c) a simple link from the MCU to a PC for debug output.
P2 (medium). Compare SPI and I2C for connecting peripherals, when would you choose each, and what's the trade-off?
P3 (harder). A UART link between two devices is producing garbled data. Given how UART works, what's the most likely cause and why?
Solutionsclick to reveal
P1. (a) Many low-speed sensors, fewest wires -> I2C: only two shared wires (SDA+SCL) carry many addressed devices, so lots of sensors share one bus. Wire-efficient (slower, but fine for low-speed sensors). (b) Fast display, high speed -> SPI: synchronous, fast, full-duplex: ideal for high-speed peripherals (displays, SD cards, fast ADCs); it needs a chip-select per device, but that's fine for one display. (c) Simple MCU-to-PC debug link -> UART: asynchronous, two wires (TX/RX), point-to-point: the standard simple device-to-device/PC link (e.g. debug output, GPS). The choice follows the situation: I2C for many-devices-few-wires, SPI for speed, UART for a simple two-device link.
P2. SPI: synchronous, fast, full-duplex, with a shared clock and usually 4 wires (SCLK, MOSI, MISO, + a chip-select per slave). Choose it for speed with few devices: high-speed peripherals like displays, SD cards, fast ADCs. I2C: synchronous, only two shared wires (SDA+SCL) for many addressed devices. Choose it for many devices on few wires. Lots of low-speed sensors on one bus. The trade-off (speed vs wiring/topology): SPI is faster but needs a separate chip-select line for each slave, so the wire count grows with the number of devices (it doesn't scale gracefully to many peripherals). I2C is slower but shares just two wires among many devices (each with a unique address), so it's far more wire-efficient for connecting lots of devices. So: SPI for speed (few devices, a wire per slave); I2C for many devices over few wires (addressed, slower). Match the choice to whether you prioritize speed (SPI) or wire-efficient many-device connection (I2C).
P3. Most likely cause: a baud-rate mismatch (the two sides aren't set to the same bit rate), or a clock drift/wiring issue affecting timing. Why (from how UART works): UART is asynchronous. There is no shared clock line telling the receiver when each bit is valid. Instead, both sides must independently agree in advance on the baud rate (the bit rate), and the receiver uses that agreed timing (plus a start bit marking each byte) to decide when to sample the line for each bit. If the two sides' baud rates don't match (one set to 9600, the other to 115200, say), the receiver samples at the wrong moments relative to the sender's bits and reads garbage. There's no clock to fall back on to re-synchronize. So a mismatch (or excessive clock drift) corrupts the data exactly as described. Why this is the first thing to check: it's UART's characteristic failure mode: 'check the baud rate' is the standard first diagnosis when a UART link garbles data: precisely because UART replaces a shared clock with a pre-agreed rate, making it fragile to timing mismatch (a failure mode SPI/I2C don't have, since they carry an explicit clock). Fix: set both sides to the same baud rate (and verify the data format, bits/parity/stop). (Less likely but possible: wrong wiring/TX-RX swap, voltage levels, or a noisy/long line.)
EFeynman Exercise
Explain to a beginner, using three ways to have a conversation: (1) two people talking directly at an agreed pace with no conductor (UART): simple but only two of them, (2) a fast conversation led by a conductor's beat where you need a separate 'attention' signal for each listener (SPI), and (3) a group on a shared party line where you call each person by name before talking (I2C). Many can share but it's slower.
REVEAL MODEL ANSWER
The three serial protocols an MCU uses are like three ways to have a conversation. UART is like two people talking directly at an agreed pace, with no conductor: they just both agree beforehand how fast they'll speak (the baud rate) and then talk back and forth on two lines: dead simple, but it only works between two people, and if they disagree on the pace, it comes out as gibberish (a baud-rate mismatch garbles the data). SPI is like a fast conversation led by a conductor's beat: there's a shared clock (the conductor) keeping everyone perfectly in time, so it's quick and you can talk and listen at once, but to address each listener you need a separate 'attention' signal for each one (a chip-select per device), so the more listeners, the more 'attention' wires you string up. Great when you need speed with just a few devices (a fast display, an SD card). I2C is like a group on a shared party line: everyone shares just two wires, and to talk to someone you call them by name first (each device has an address) so the right one responds: many people can share the same two wires, which is wonderfully wire-efficient for hooking up lots of sensors, but the shared, take-turns nature makes it slower than SPI. So you pick the conversation style to fit: two people, simple, agreed pace -> UART; fast, few listeners, with attention signals -> SPI; many sharing two wires, called by name -> I2C. Knowing all three and which fits is how an MCU talks to everything around it.
FError Analysis Framework
- Treating UART, SPI, and I2C as interchangeable. Why: they're all serial. Recognise: they differ in speed/wires/topology; the wrong one fits poorly. Avoid: choose by the situation (simple link / speed / many devices on few wires).
- Thinking SPI vs I2C is only about speed. Why: SPI is faster, so use it. Recognise: you ignore wiring. SPI needs a chip-select per device; I2C shares 2 wires. Avoid: weigh speed (SPI) vs wire-efficient many-device bus (I2C).
- Expecting UART to have a clock line. Why: synchronous serial has a clock. Recognise: UART is async, no clock; a baud mismatch garbles data. Avoid: set both sides to the same baud rate (no shared clock to resync).
- Hanging many SPI devices off one chip-select. Why: fewer wires is simpler. Recognise: SPI addresses slaves by separate chip-selects; sharing one breaks it. Avoid: give each SPI slave its own chip-select (or use I2C for many devices).
GMini Challenge
Design the peripheral communication for an embedded sensor node: it must connect to a PC for debug, a fast display, and several low-speed environmental sensors. Choose the protocol for each, specify the wiring, and explain how matching each protocol to the situation interfaces everything efficiently.
REVEAL MODEL ANSWER
Protocol choices (match to situation):
| Connection | Protocol | Wiring | Why |
|---|---|---|---|
| MCU <-> PC (debug output) | UART | TX, RX (2 wires) | Simple asynchronous point-to-point link to a PC (agreed baud rate) |
| MCU <-> fast display | SPI | SCLK, MOSI, MISO, + CS (chip-select) | High speed, full-duplex, ideal for a fast display; one CS for the one device |
| MCU <-> several low-speed sensors | I2C | SDA, SCL (2 shared wires) | Many addressed devices on two wires. Wire-efficient for several low-speed sensors |
Wiring summary: UART uses 2 wires to the PC; SPI uses ~4 wires to the display (clock, two data, chip-select); all the I2C sensors share the same 2 wires (SDA+SCL), each responding to its unique address, so adding more sensors adds no extra wires, just more addresses on the bus.
How matching each protocol interfaces everything efficiently: - UART for the PC link: the situation is a simple two-device async link: UART is exactly that (TX/RX, baud rate), minimal and standard for debug. - SPI for the display: the situation needs speed. SPI's shared clock gives fast, full-duplex transfer ideal for a display; the single chip-select cost is trivial for one device. - I2C for the many sensors: the situation is many low-speed devices, few wires: I2C shares two wires among all the sensors (addressed), so you connect many without a forest of wires (accepting lower speed, fine for environmental sensors).
By choosing each protocol to fit its situation (simple link / speed / many-devices-few-wires), the node interfaces the PC, display, and all the sensors efficiently. The right speed and minimal wiring for each. This fit-to-purpose selection is the core skill of interfacing peripherals, and these protocols (using the MCU's UART/SPI/I2C peripherals, often with DMA) are how the embedded system talks to everything around it.
Quiz Check
A quick auto-graded check, separate from the recall cards above. Your score is pooled with the recall cards into this module's Mastery score, and completing this lesson requires the quiz submitted with pooled mastery at 80% or above.