Loading header...

SPI: Serial Peripheral Interface

SPI is a synchronous board-level serial bus used when an MCU needs fast, simple communication with peripherals such as ADCs, DACs, displays, flash memories, shift registers, radios, and SD cards. One controller provides the clock. Selected peripherals shift data in and out at the same time.

SPI is fast because it is simple. The tradeoff is that SPI has no universal addressing, no universal command format, and no built-in acknowledgement. The wiring and timing are generic; the transaction meaning comes from each device data sheet.

Learning Objectives

By the end of this lesson, you should be able to:

  • identify SCK, MOSI, MISO, chip select, supply, and ground connections;
  • explain full-duplex shifting and dummy bytes;
  • choose the correct clock polarity and phase from a data sheet;
  • calculate transfer time from SPI clock frequency and bits transferred;
  • design a shared SPI bus with one chip-select line per peripheral;
  • debug mode errors, bus contention, floating MISO, and signal-integrity problems.

Core Idea: The Controller Owns The Clock

SPI is synchronous. The controller starts a transaction by asserting a chip-select signal, then toggles SCK. On each clock cycle, one bit moves from controller to peripheral on MOSI and one bit moves back on MISO.

flowchart LR M["Controller MCU\nSCK source\nMOSI out\nMISO in\nCS outputs"] S["SPI peripheral\nfollows SCK\nsamples MOSI\ndrives MISO when selected"] M -- "SCK" --> S M -- "MOSI" --> S S -- "MISO" --> M M -- "CS active low" --> S

Many older data sheets say master and slave. Newer documents often use controller and peripheral. The signals are the same.

SPI Signals

Signal Direction Purpose
SCK controller to peripheral serial clock
MOSI controller to peripheral controller data output
MISO peripheral to controller peripheral data output
CS, SS, or nCS controller to peripheral selects one device, usually active LOW
GND shared reference required for logic-level signaling

Some vendors use alternate names: COPI for controller-out peripheral-in, CIPO for controller-in peripheral-out, and SDO/SDI from the device point of view. Always map names from the data sheet before wiring.

Full-Duplex Shifting

SPI shift registers exchange bits simultaneously. Even a read operation requires the controller to transmit something, often a dummy byte, because clock pulses are created only while the controller sends.

sequenceDiagram participant C as Controller participant P as Peripheral C->>P: CS LOW loop 8 SCK pulses C->>P: MOSI bit shifts out P->>C: MISO bit shifts back end C->>P: CS HIGH

For many sensors, the first byte is a command or register address. The meaningful response appears in the next byte or next several bytes. The first returned byte may be dummy data.

Clock Polarity And Phase

SPI has four common modes. They are defined by clock polarity CPOL and clock phase CPHA.

Mode CPOL CPHA Idle SCK Capture edge, common wording
0 0 0 LOW first edge, rising
1 0 1 LOW second edge, falling
2 1 0 HIGH first edge, falling
3 1 1 HIGH second edge, rising
flowchart TD M0["Mode 0\nCPOL 0 CPHA 0\nidle low\nsample rising"] M1["Mode 1\nCPOL 0 CPHA 1\nidle low\nsample falling"] M2["Mode 2\nCPOL 1 CPHA 0\nidle high\nsample falling"] M3["Mode 3\nCPOL 1 CPHA 1\nidle high\nsample rising"] M0 --> M1 --> M2 --> M3

The mode must match the peripheral data sheet. If the mode is wrong, data may look shifted by one bit, unstable, or completely wrong. Do not rely on "mode 0 usually works" when the data sheet specifies another mode.

Transfer Time

SPI throughput is set by SCK and by protocol overhead.

$$
t_{bits} = \frac{N_{bits}}{f_{SCK}}
$$

For a 16-bit register read that sends an 8-bit command and receives 16 data bits, many devices require 24 clock cycles:

$$
t = \frac{24}{8,MHz} = 3,us
$$

Real transaction time also includes chip-select setup time, chip-select hold time, firmware overhead, and any required delay between command and data.

Multiple Devices On One Bus

SCK, MOSI, and often MISO are shared. Each peripheral gets its own chip-select line.

flowchart TD MCU["MCU controller"] BUS["Shared SPI bus\nSCK MOSI MISO"] ADC["ADC\nCS_ADC"] FLASH["Flash\nCS_FLASH"] DISP["Display\nCS_DISP"] MCU --> BUS BUS --> ADC BUS --> FLASH BUS --> DISP MCU -- "CS_ADC" --> ADC MCU -- "CS_FLASH" --> FLASH MCU -- "CS_DISP" --> DISP

Only one chip-select should be active at a time unless the hardware is intentionally designed for a special daisy-chain or broadcast case. If two peripherals drive MISO at once, the bus is electrically contended and data is corrupted.

Electrical Design Rules

  • Keep SPI traces short on a PCB, especially at high SCK rates.
  • Route SCK cleanly; it is the signal most likely to cause timing and ringing issues.
  • Use series damping resistors near the driver when edges are fast and traces are long enough to ring.
  • Confirm that all devices use compatible I/O voltage.
  • Keep chip-select HIGH during reset until firmware configures the peripheral.
  • Check whether MISO is high impedance when CS is inactive.
  • Avoid long off-board SPI cables; use a robust physical layer when distance or noise is significant.

Firmware Transaction Pattern

uint8_t spi_transfer(uint8_t out);
void cs_low(void);
void cs_high(void);

uint16_t read_register16(uint8_t reg) {
    uint8_t hi;
    uint8_t lo;

    cs_low();
    (void)spi_transfer(0x80u | reg);  /* command byte: read selected register */
    hi = spi_transfer(0x00u);         /* dummy output clocks in high byte */
    lo = spi_transfer(0x00u);         /* dummy output clocks in low byte */
    cs_high();

    return ((uint16_t)hi << 8) | lo;
}

This code is a generic pattern, not a universal command format. Real devices define read/write bits, address size, dummy cycles, byte order, and CS timing in their own data sheets.

Worked Example: Reading A 12-Bit ADC

Suppose an SPI ADC requires 3 command bits, 1 null bit, and 12 data bits. The controller clocks 16 bits total at 2 MHz.

$$
t = \frac{16}{2,MHz} = 8,us
$$

If the ADC must be sampled at 10 kS/s, the sample period is:

$$
T_s = \frac{1}{10000} = 100,us
$$

An 8 us transfer fits easily, but firmware must still leave time for chip-select timing, interrupt latency, processing, and other bus users.

Practical Debug Checklist

  1. Confirm supply voltage and I/O level compatibility.
  2. Verify SCK, MOSI, MISO, CS, and GND mapping from the device data sheet.
  3. Keep all chip-selects HIGH except the target device.
  4. Confirm CPOL, CPHA, bit order, word length, and maximum SCK.
  5. Start with a slow SCK, then increase after the transaction is correct.
  6. Use a logic analyzer to inspect CS, SCK, MOSI, and MISO together.
  7. Check whether the first returned byte is dummy data.
  8. Confirm that inactive peripherals release MISO.

Common Mistakes

  • Swapping MISO and MOSI because names are read from opposite device perspectives.
  • Leaving a chip-select LOW between transactions.
  • Selecting two peripherals at the same time.
  • Using the wrong SPI mode.
  • Reading the first dummy byte as real data.
  • Clocking faster than the peripheral or board layout can support.
  • Forgetting pull-ups or default states for chip-select during reset.
  • Trying to run SPI over a long cable in a noisy machine.

Summary

SPI is a fast synchronous bus where the controller selects one peripheral, generates SCK, and exchanges one bit in each direction per clock cycle. Correct SPI design depends on matching the data sheet mode and timing, controlling chip-select lines carefully, avoiding MISO contention, and validating signal quality at the actual clock rate.

Further Reading

  • Motorola SPI Block Guide for the original SPI concepts.
  • Microchip and STMicroelectronics SPI peripheral reference manual chapters.
  • Texas Instruments application notes on SPI timing and signal integrity.
  • Device data sheets for MCP320x ADCs, W25Q flash memories, and common display controllers.

Mind Map

mindmap root((SPI)) Core concept Synchronous bus Controller clock Full duplex Device data sheet rules Signals SCK clock MOSI to peripheral MISO to controller CS active low Shared ground Timing CPOL and CPHA Four modes t equals bits over fSCK Setup and hold Dummy cycles Design rules One CS per device One MISO driver Match I O voltage Short clean clock CS high at reset Practical checks Slow first Logic analyzer Mode match Bit order Max SCK Common mistakes MISO MOSI swap Wrong mode Two CS low Dummy byte used Long cable

Previous: UART
Next: I2C: Inter-Integrated Circuit