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.
The mode question
Four wires and what each does
| Wire | Direction | What it is |
|---|---|---|
| SCK | out | The clock. The board decides when a bit happens |
| MOSI | out | Board to device |
| MISO | in | Device to board |
| CS | out | One 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.
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.
#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.
Same three settings, same manual chip select. polarity and phase are CPOL and CPHA spelled out, which is clearer than a mode number once you know what they mean.
from machine import SPI, Pin
cs = Pin(5, Pin.OUT, value=1)
spi = SPI(2, baudrate=10_000_000, polarity=0, phase=0,
sck=Pin(18), mosi=Pin(23), miso=Pin(19))
cs.value(0)
spi.write(bytes([0x8F]))
data = spi.read(1)
cs.value(1)
print(hex(data[0]))SPI(1) is HSPI and SPI(2) is VSPI on the classic ESP32. Passing 1 or 2 as the first argument gets you the hardware peripheral rather than a bit-banged one.
When it does not work
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.
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.
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.
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.
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.