The direct answer to what SPI phase (CPHA) means: it dictates whether the receiving device samples the data line on the leading (first) or trailing (second) edge of the clock pulse. Combined with Clock Polarity (CPOL), which sets the idle state of the clock, these two parameters create the four distinct SPI modes (Mode 0 through 3). If your microcontroller and peripheral disagree on the SPI phase, you will read garbage data or zeros, regardless of how perfectly your wiring is routed.
This primer breaks down the physical layer, maps the exact clock phase matrices, and provides a bench-tested debugging framework for when the bus refuses to talk.
SPI Bus Mechanics and Physical Layer Rules
Serial Peripheral Interface (SPI) is a synchronous, full-duplex, master-slave bus. Unlike multi-master buses, SPI relies on dedicated hardware lines for every transaction. Below is the foundational mechanics table for standard 4-wire SPI.
| Parameter | SPI Specification | Practical Bench Limits |
|---|---|---|
| Wires | 4 (MOSI, MISO, SCK, CS/SS) | Adds 1 CS wire per target device |
| Speed (Baud) | 1 MHz to 50+ MHz | Reliably 1-10 MHz on breadboards; >20 MHz requires PCB trace routing |
| Addressing | None (Hardware Chip Select) | No software address clashes; limited by available GPIO pins for CS |
| Distance | Intra-board (< 1 meter) | Keep traces under 10cm for >10 MHz to avoid signal ringing |
| Duplex | Full-Duplex | MISO and MOSI shift simultaneously on every clock edge |
The Four SPI Modes: Clock Phase and Polarity Matrix
To successfully exchange data, the master and slave must agree on the clock's idle state (CPOL) and the sampling edge (CPHA). According to the Analog Devices SPI Guide, misinterpreting the datasheet's timing diagram is the number one cause of SPI integration failure.
| SPI Mode | CPOL (Polarity) | CPHA (Phase) | Clock Idle State | Data Sampled On | Data Shifted On |
|---|---|---|---|---|---|
| Mode 0 | 0 | 0 | Low | Rising (Leading) | Falling (Trailing) |
| Mode 1 | 0 | 1 | Low | Falling (Trailing) | Rising (Leading) |
| Mode 2 | 1 | 0 | High | Falling (Leading) | Rising (Trailing) |
| Mode 3 | 1 | 1 | High | Rising (Trailing) | Falling (Leading) |
Why does CPHA=1 exist? When CPHA is 0, the slave must place the first bit on MISO before the first clock edge, meaning the slave prepares data as soon as CS goes low. When CPHA is 1, the first clock edge is used to shift (prepare) the data, and the second edge is used to sample it. This gives the slave peripheral a full half-clock cycle to fetch the first byte from its internal registers before the master reads it.
Protocol Selection: When to Choose SPI Over I2C or UART
Not every sensor needs the speed of SPI. Here is how the bus mechanics dictate your protocol choice based on distance, speed, and device count.
| Criterion | SPI | I2C | UART |
|---|---|---|---|
| Best For | High-speed data (SD cards, TFT displays, ADCs) | Many low-speed sensors on limited GPIO | Point-to-point off-board comms (GPS, Cellular) |
| Max Speed | 50+ MHz | 3.4 MHz (High-Speed Mode) | ~5 Mbps (practical limit on MCUs) |
| Distance Limit | < 30 cm (without differential transceivers) | < 1 meter (at 100 kHz) | 15 meters (RS-232) / 1200m (RS-485) |
| Device Count | 1 Master, N Slaves (Requires N Chip Select wires) | 1 Master, up to 127 Slaves (2 shared wires) | Strictly 1-to-1 (without multiplexing) |
Classic Failures: Debugging Phase Mismatches and Bus Errors
When your logic analyzer shows a blank MISO line or your serial monitor prints 0xFF repeatedly, you are likely facing one of three classic SPI failures.
- The Phase/Polarity Mismatch: You configured Mode 0, but the sensor requires Mode 1. Fix: Check the sensor datasheet's timing diagram. If the data changes on the falling edge but is read on the rising edge, you need CPHA=1. Toggle through Modes 0-3 in your
SPISettingsuntil valid data appears. - Baud Rate Mismatch & Signal Ringing: You set the clock to 20 MHz on a breadboard with 10cm jumper wires. The wire inductance causes the square wave to ring, crossing the logic threshold multiple times per edge. The slave reads this as a dozen extra clock pulses. Fix: Drop the baud rate to 1 MHz to verify communication. For high-speed PCB designs, add 33Ω series termination resistors near the master's MOSI and SCK pins to dampen reflections.
- Address Clash vs. CS Clash (Missing Pull-Up): I2C suffers from software address clashes; SPI suffers from hardware CS clashes. If two SPI devices share the MISO line and one lacks a pull-up on its CS pin, it may wake up during MCU boot and permanently drive MISO low, blocking all other devices. Fix: Measure the CS pins with a multimeter during boot. Ensure every CS line has a 10kΩ pull-up to VCC.
How to Sniff and Debug the Bus: Do not guess; use a logic analyzer. Tools like the Saleae SPI Protocol Analyzer or the open-source Sigrok/PulseView software allow you to decode the hex bytes in real-time. When debugging, look closely at the setup and hold times—the microseconds between the CS line going low and the first SCK edge. If your MCU toggles CS and immediately fires the clock, you may violate the peripheral's required setup time (often 50ns to 100ns). Insert a 1-microsecond delay in your code after pulling CS low before initiating the SPI transfer.
Minimal Working Exchange: ESP32 to MAX31865 (Mode 1)
The MAX31865 RTD-to-Digital converter is a classic example of a device that strictly requires SPI Mode 1 (CPOL=0, CPHA=1) or Mode 3. Attempting to read it in Mode 0 will yield corrupted temperature registers. Below is a minimal, bench-verified exchange using the ESP32.
Physical Wiring Table
| ESP32 DevKit v1 GPIO | MAX31865 Breakout Pin | Notes |
|---|---|---|
| GPIO 23 (MOSI) | SDI | Master Out, Slave In |
| GPIO 19 (MISO) | SDO | Master In, Slave Out |
| GPIO 18 (SCK) | SCK | Clock (Max 5MHz for MAX31865) |
| GPIO 5 (CS) | CS | Requires 10kΩ pull-up to 3.3V |
| 3V3 | Vin / VDD | MAX31865 is 3.3V logic |
| GND | GND | Common ground required |
ESP32 Arduino Core Code
This code initializes the bus with the correct SPI phase (Mode 1), reads the configuration register (0x00), and writes a Vbias enable command. For deeper ESP32 hardware abstraction details, refer to the Espressif ESP32 SPI Master Documentation.
#include <SPI.h>
// Pin Definitions
#define MAX_CS 5
// MAX31865 Registers
#define REG_CONFIG 0x00
#define CONFIG_BIAS 0x80 // Vbias enable bit
void setup() {
Serial.begin(115200);
pinMode(MAX_CS, OUTPUT);
digitalWrite(MAX_CS, HIGH); // Deselect immediately
// Initialize SPI bus
SPI.begin();
// Delay to allow peripheral power stabilization
delay(100);
// Write to Config Register to enable Vbias
// SPI Mode 1: CPOL=0, CPHA=1. Max clock 5MHz.
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
digitalWrite(MAX_CS, LOW);
delayMicroseconds(1); // Respect CS setup time
// Send Write command (Address | 0x80) and Data
SPI.transfer(REG_CONFIG | 0x80);
SPI.transfer(CONFIG_BIAS);
digitalWrite(MAX_CS, HIGH);
SPI.endTransaction();
Serial.println("MAX31865 Vbias enabled via SPI Mode 1.");
}
void loop() {
// Read Config Register back to verify phase alignment
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
digitalWrite(MAX_CS, LOW);
delayMicroseconds(1);
SPI.transfer(REG_CONFIG); // Read command (Address & 0x7F)
byte configVal = SPI.transfer(0x00); // Clock out the data
digitalWrite(MAX_CS, HIGH);
SPI.endTransaction();
Serial.print("Config Register: 0x");
Serial.println(configVal, HEX);
delay(2000);
}
If your serial monitor outputs Config Register: 0x80 (or 0x82 depending on 2/3/4-wire RTD configuration), your SPI phase, polarity, and wiring are perfectly aligned. If you read 0x00 or 0xFF, drop your baud rate to 250kHz and verify your CS pull-up resistor.






