SPI is the protocol you reach for when you need speed. SD cards, TFT displays, ADC chips, flash memory, and shift registers all use SPI. It is synchronous (has a shared clock), full-duplex (sends and receives at the same time), and far faster than UART or I²C.
The Core Idea — Master Controls the Clock
In UART, both sides have their own clocks and agree on speed in advance. SPI is different — there is one master device that generates the clock, and one or more slave devices that follow it.
The master decides exactly when each bit is sent. Slaves have no say — they just respond when the master clocks them.
flowchart LR
classDef master fill:#dbeafe,stroke:#2563eb,color:#1e3a5f
classDef slave fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef wire fill:#fef9c3,stroke:#ca8a04,color:#713f12
M["Master\n(your microcontroller)\ngenerates the clock\ndecides when to talk"]:::master
S["Slave\n(sensor, SD card, display...)\nfollows the clock\nresponds when selected"]:::slave
M -- "SCK → (clock pulses)" --> S
M -- "MOSI → (data out)" --> S
S -- "MISO ← (data back)" --> M
M -- "SS̄ → (select this slave)" --> S
Four wires:
Wire
Full name
Direction
What it does
SCK
Serial Clock
Master → Slave
Clock pulses from master
MOSI
Master Out Slave In
Master → Slave
Data from master to slave
MISO
Master In Slave Out
Slave → Master
Data from slave back to master
SS̄
Slave Select (active LOW)
Master → Slave
Tells one specific slave it is being talked to
Full-Duplex — Sending and Receiving at the Same Time
SPI is full-duplex: on every clock pulse, the master sends one bit on MOSI and the slave sends one bit back on MISO — simultaneously. Both sides are shifting their data out at the same time.
Think of it like two people sliding pieces of paper across a table at the same moment — one goes left, one goes right, both happen at the same time.
sequenceDiagram
participant M as Master
participant S as Slave
Note over M,S: SS̄ pulled LOW — slave is selected
M->>S: Clock pulse 1 — MOSI bit 7 (MSB)
S->>M: Clock pulse 1 — MISO bit 7 (MSB)
M->>S: Clock pulse 2 — MOSI bit 6
S->>M: Clock pulse 2 — MISO bit 6
M->>S: Clock pulse 3 — MOSI bit 5
S->>M: Clock pulse 3 — MISO bit 5
M->>S: Clock pulses 4–7 — remaining bits
S->>M: Clock pulses 4–7 — remaining bits
M->>S: Clock pulse 8 — MOSI bit 0 (LSB)
S->>M: Clock pulse 8 — MISO bit 0 (LSB)
Note over M,S: SS̄ pulled HIGH — slave deselected
Note over M,S: Master has sent 1 byte. Slave has sent 1 byte. At the same time.
If you only want to write (e.g. sending a command to a display), ignore whatever comes back on MISO.
If you only want to read (e.g. reading a sensor value), send a dummy byte (all zeros) on MOSI just to generate the clock pulses.
Slave Select — Choosing Who to Talk To
SS̄ is active LOW — pull it LOW to activate a slave, HIGH to deactivate it. Each slave needs its own SS̄ line going back to the master.
flowchart TD
classDef master fill:#dbeafe,stroke:#2563eb,color:#1e3a5f
classDef slave fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef bus fill:#fef9c3,stroke:#ca8a04,color:#713f12
M["Master MCU"]:::master
BUS["Shared bus\nSCK, MOSI, MISO\n(all slaves connected to same 3 wires)"]:::bus
S0["Slave 0\nSD Card\nSS̄0 from master"]:::slave
S1["Slave 1\nDisplay\nSS̄1 from master"]:::slave
S2["Slave 2\nADC Chip\nSS̄2 from master"]:::slave
M --> BUS
BUS --> S0
BUS --> S1
BUS --> S2
M -- "SS̄0 (LOW = talking to SD card)" --> S0
M -- "SS̄1 (LOW = talking to display)" --> S1
M -- "SS̄2 (LOW = talking to ADC)" --> S2
Only one SS̄ can be LOW at a time. If you pull two slaves LOW simultaneously, both will try to drive MISO and you will get corrupted data. Always bring SS̄ HIGH before selecting a different slave.
How a SPI Transaction Works — Step by Step
flowchart TD
classDef step fill:#dbeafe,stroke:#2563eb,color:#1e3a5f
classDef warn fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
A["Pull SS̄ LOW\n→ Slave wakes up and listens"]:::step
B["Master starts generating clock pulses on SCK"]:::step
C["On each clock pulse:\n• Master shifts next bit out on MOSI\n• Slave shifts its bit out on MISO\n• Both happen at the same moment"]:::step
D["After 8 clock pulses: 1 full byte exchanged\nRepeat for more bytes if needed"]:::step
E["Pull SS̄ HIGH\n→ Transaction complete, slave goes idle"]:::step
F["Never leave SS̄ LOW between transactions\nor the slave stays in a confused state"]:::warn
A --> B --> C --> D --> E --> F
Clock Modes — CPOL and CPHA
SPI has 4 modes that define exactly which edge of the clock the data is sampled on. Every SPI device has a specific mode it needs — you must match it.
flowchart TD
classDef m0 fill:#dbeafe,stroke:#2563eb,color:#1e3a5f
classDef m1 fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef m2 fill:#fef9c3,stroke:#ca8a04,color:#713f12
classDef m3 fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
M0["Mode 0 (most common)\n──────────────────────\nClock starts LOW\nData captured on the RISING edge\nSD cards, most sensors use this"]:::m0
M1["Mode 1\n──────────────────────\nClock starts LOW\nData captured on the FALLING edge"]:::m1
M2["Mode 2\n──────────────────────\nClock starts HIGH\nData captured on the FALLING edge"]:::m2
M3["Mode 3\n──────────────────────\nClock starts HIGH\nData captured on the RISING edge\nSome thermocouple chips, some displays"]:::m3
M0 --> M1 --> M2 --> M3
[!TIP] When a datasheet does not mention which mode, try Mode 0 first. It works for the majority of SPI devices.
Reading from a Sensor — The Full Picture
Here is what actually happens when you read a temperature value from an SPI sensor:
sequenceDiagram
participant M as Master (MCU)
participant S as Slave (Sensor)
M->>S: Pull SS̄ LOW (select this sensor)
M->>S: Send command byte over MOSI (e.g. "read temperature register")
S->>M: Slave receives command (MISO sends back dummy data — ignore it)
Note over M,S: Sensor now knows what you want
M->>S: Send dummy byte (0x00) on MOSI just to generate 8 clock pulses
S->>M: Slave sends temperature value back on MISO
M->>S: Pull SS̄ HIGH (done — release the slave)
Note over M: Master now has the temperature value
SPI vs UART vs I²C
UART
SPI
I²C
Wires
3
4 + 1 per slave
2 + GND
Speed
Up to ~1 Mbps
Up to 20+ Mbps
100 kHz – 1 MHz
Devices on bus
1-to-1 only
1 master, many slaves
Up to 127
Duplex
Full
Full (both directions at once)
Half
Best for
Debug, GPS, GSM
SD cards, displays, ADCs
Sensors, RTC, EEPROM
Common SPI Devices
Device
What it does
SPI Mode
SD / microSD card
Mass storage
Mode 0
ILI9341 / ST7789
TFT colour display
Mode 0
MCP3204
4-channel 12-bit ADC
Mode 0
MAX31855
Thermocouple sensor
Mode 1 or 3
W25Q32
External flash memory
Mode 0
74HC595
Shift register (output expander)
Mode 0
Practical Tips
Always pull SS̄ LOW before sending, HIGH after — never leave a transaction open
Wrong mode = wrong data — if you read garbage from a sensor, try changing the clock mode
MISO needs a pull-up resistor if the slave might tri-state its MISO (leaves it floating) — add a 10kΩ resistor to VCC
One SS̄ pin per slave — SPI does not have addresses like I²C, so the SS̄ wire is the only way to select a device