ESP32/Buses/32. SPI
Your chip
Your language
Buses · 32 of 81

SPI

Four wires instead of two, tens of megahertz instead of hundreds of kilohertz, and no addresses - each device gets its own select line. Faster, simpler, and it fails silently because nothing in SPI ever acknowledges anything.

/esp32/spi · arduino · S3

The mode question

SPI_MODE0 — CPOL 0, CPHA 0
device agrees
Mode you set in SPISettings
Clock rests
low
Data latched on
the first edge
Bytes received
correct
Mode 0: clock idles low, data latched on the rising edge. This is what almost everything uses — displays, SD cards, most sensors — which is why so much example code never mentions modes at all. The device's datasheet states it in one line; that line is the only place worth looking.

Four wires and what each does

WireDirectionWhat it is
SCKoutThe clock. The board decides when a bit happens
MOSIoutBoard to device
MISOinDevice to board
CSoutOne per device. Low means "this one"

There are no addresses. Adding a device means adding a CS pin, which is why SPI runs out of pins long before it runs out of speed.

On the ESP32 specifically

Two buses are available to you — HSPI and VSPI on the classic chip — and the pins are freely assignable, because the signals go through a routing matrix on their way to the pads. The default pin numbers in the examples are only defaults.

One consequence worth knowing: routed pins top out around 40 MHz. The dedicated IO-MUX pins go higher, and that is the difference between a display that refreshes smoothly and one that does not.

On your S3
ChipXtensa LX7 · 2 × 240 MHz
Board settingESP32S3 Dev Module
Default I2CSDA 8 · SCL 9
Watch out forThe port vanishes after upload

The code

The ESP32 lets you assign all four SPI pins freely. SPISettings carries the three facts a device needs - speed, bit order and mode - and beginTransaction applies them per device, which is what makes sharing a bus safe.

spi_device.ino
#include <SPI.h>

const int CS = 5;

void setup() {
  Serial.begin(115200);
  pinMode(CS, OUTPUT);
  digitalWrite(CS, HIGH);           // idle high - not selected
  SPI.begin(18, 19, 23, CS);        // SCK, MISO, MOSI, SS

  SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
  digitalWrite(CS, LOW);
  uint8_t whoami = SPI.transfer(0x8F);   // read register 0x0F
  whoami = SPI.transfer(0x00);
  digitalWrite(CS, HIGH);
  SPI.endTransaction();

  Serial.printf("id 0x%02X\n", whoami);
}

void loop() {}

Chip select is yours to drive. The library does not touch it, and forgetting to raise it again is why the next device on the bus starts reading your traffic.

When it does not work

The display shows snow, or every byte comes back shifted

Wrong SPI mode. There are four, the device's datasheet names one, and there is no error of any kind when they disagree. Trying all four takes a minute.

Reads always return 0x00 or 0xFF

Chip select is not reaching the device, MISO is not connected, or the device is a write-only one. Check CS with a meter while the sketch runs - it should sit high and dip low.

It works at 1 MHz and fails at 20

Wire length. SPI at 20 MHz wants short, direct connections - jumper wires on a breadboard are not that. Drop the clock until it works and leave it there.

Two devices on one bus interfere

One of them is not releasing MISO when deselected, or its CS is idling low. Each device needs its own CS pin, idle high, and only one low at a time.

Where this goes next

I2C and SPI both need a master. The next page is the bus where both ends just talk, and nobody is in charge.

UART and baud rates

Edit this page — content/esp32/spi.mdx

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse ESP32 on the forum