The puzzle
A flash memory, a display and an accelerometer share three wires to the microcontroller. The accelerometer returns 0x00 for every register, or values that look shifted by one bit. The display works only at half the speed the datasheet promises. SPI has no addresses, no acknowledgements and hardly any rules. What does it take to get it right?
STEP 1
Four wires, one clock
SPI has a controller that generates the clock and one or more devices (targets):
- SCK, the clock, always driven by the controller;
- MOSI (controller out, device in, also called COPI or SDO on the controller);
- MISO (controller in, device out, CIPO);
- CS (chip select, usually active low, one per device).
Each clock cycle shifts one bit out on MOSI and one bit in on MISO at the same time: SPI is inherently full duplex. To read, the controller must send something, often dummy 0x00 or 0xFF bytes, because only its clock makes the device shift data out. The clock runs only while there are bits to move, so the rate can be anything up to the device’s limit, and the link never needs the matched clocks a UART does.
STEP 2
Clock polarity and phase
Devices differ in which clock edge they use. CPOL sets the idle level of SCK; CPHA sets whether data is sampled on the first (leading) or second (trailing) edge after CS falls, with data changing on the other edge. The mode number is CPOL × 2 + CPHA; modes 0 and 3 are the most common.
↑ This step uses the figure at the top of the page.
With the wrong CPHA, the controller typically samples at the moment the device changes its output. The result is not random: often every byte appears shifted by one bit, or reads correctly at low speed and fails at high speed. A wrong CPOL alone (mode 0 against 3, or 1 against 2) samples on the same edge direction and may seem to work, except for the first bit after CS falls. The device datasheet’s timing diagram shows the idle clock level and the edge on which it samples.
STEP 3
Several devices
All devices share SCK, MOSI and MISO; each has its own CS. Only the selected device may drive MISO: a device whose CS is high must release it (a high-impedance output), or two devices fight over the line. CS also frames the transaction: many devices reset their command decoder when CS goes high, so a multi-byte command must keep CS low throughout. Some controllers raise their hardware CS between words, so firmware often drives CS as an ordinary GPIO to control exactly when it moves.
STEP 4
Clock rate
The RP2040’s SPI divides clk_peri by an even prescaler (2–254) and a post-divider (1–256). For requests down to clk_peri/512, the pico-sdk’s spi_set_baudrate() picks the pair that gives the fastest rate not above the request; for slower requests (1.5.1) it returns a rate above the request. Only clk_peri divided by an even integer is possible, so a request can come out well away from what was asked. Always use the returned value.
The controller divides its clock by the ratios its hardware supports. On the RP2040, clk_peri is divided by an even prescaler and an integer post-divider, and for requests down to clk_peri/512 spi_set_baudrate() returns the fastest rate that does not exceed the request. Below that, pico-sdk 1.5.1’s prescaler search stops one step early and the result is faster than requested: 100 kHz at 125 MHz gives 122 kHz. Ask for 10 MHz with a 125 MHz clk_peri and you get 125 / 14 = 8.93 MHz; ask for 20 MHz and you get 15.6 MHz.
A transfer of N bytes needs
of clocking. Real transfers add gaps between bytes while software feeds the transmit FIFO, CS setup and hold times, and command bytes; at high clock rates these, not the clock, often limit throughput, and DMA (unit 12) keeps the FIFO fed.
STEP 5
Beyond the clock limit
Long wires and several devices load SCK and MISO, slowing their edges. The data from the device arrives later than it leaves: its clock-to-output delay plus the trace delay both ways must fit within the half clock period before the controller samples. When a design works at 1 MHz and fails at 20 MHz, lower the clock or shorten the wiring before suspecting the code.
STEP 6
Worked example: reading an accelerometer
A device in mode 3 returns six bytes of X, Y and Z data after a one-byte command whose top bit means “read”. The controller:
- drives CS low;
- sends the command byte (register address with the read bit set), ignoring the byte received at the same time;
- sends six dummy bytes, receiving the six data bytes;
- drives CS high.
Seven bytes are 56 bits; at 8.93 MHz that is
of clocking, plus whatever gaps the software leaves between bytes.
MYTHS AND FACTS
Common misconceptions
Reading needs no transmit
The controller must clock the device, and every clock also sends a bit.
The mode is just a detail
A wrong mode gives shifted or marginal data that may pass at low speed and fail at high speed.
spi_set_baudrate() gives the rate I asked for
It gives the nearest rate its search finds, usually just below the request (above it for very slow requests); use its return value.
SPI speed is set by the clock alone
Gaps between bytes, CS handling and wiring delays often dominate.
Check yourself
Answer in your head, then open the card.
A device datasheet shows the clock idling high and data being latched on the rising edge. Which mode is that?
Idle high means CPOL = 1; the first edge after CS falls is falling, so the rising edge is the trailing edge: CPHA = 1. Mode 3.
With clk_peri at 48 MHz, what does spi_set_baudrate(spi0, 10000000) return?
Prescale 2; the largest post-divider keeping the rate at or below 10 MHz is 3: 48 / 6 = 8 MHz.
Two SPI devices share MISO. Reading device A returns garbage only when device B is fitted. What is the likely fault?
Device B drives MISO while not selected: its CS is not high (for example, a floating or wrongly wired CS), or it does not release MISO. Make sure every unselected device has CS inactive.
How long does it take to read a 4096-byte page from a flash chip at 15.625 MHz, ignoring gaps and the command bytes?
8 × 4096 / 15.625 MHz ≈ 2.1 ms.
Sources (3)
- Linux kernel, Documentation/spi/spi-summary.rst — “CPOL=0 means the clock starts low, so the first (leading) edge is rising … CPHA=0 says sample on the leading edge, CPHA=1 means the trailing edge”; “In the SPI mode number, CPOL is the high order bit and CPHA is the low order bit”; “mode-0 and mode-3 are most commonly used”; chip selects “are usually active low”
- Raspberry Pi Ltd, pico-sdk 1.5.1, hardware_spi/spi.c — spi_set_baudrate: “Prescale is an even number from 2 to 254 inclusive” (the smallest with freq_in < (prescale + 2) × 256 × baudrate), “Post-divide is an integer in the range 1 to 256 inclusive” (“Find largest post-divide which makes output <= baudrate”); returns freq_in / (prescale × postdiv)
- Raspberry Pi Ltd, pico-sdk 1.5.1, hardware_spi/spi.h — spi_set_format(spi, data_bits 4..16, cpol, cpha, order): “order Must be SPI_MSB_FIRST, no other values supported on the PL022”; spi_write_read_blocking writes and reads the same number of bytes