serial-bench one bench, five serial buses - read-only archive

serial-bench / SPI / msg #2

A mode mismatch reads like a one-bit shift, not like noise

From
Ivo Kallas <ivo@rawsocket.cloud>
Date
Tue, 08 Jul 2025 09:00:00 +0300
Subject
A mode mismatch reads like a one-bit shift, not like noise
Message-ID
<20250708.0900.spicpha@rawsocket.cloud>
List-Id
serial-bench.rawsocket.cloud
X-Bus
SPI
X-Topic
SPI at 8 MHz, 16-bit register reads · CPHA = 0 against CPHA = 1

A pressure sensor on a four-wire SPI link returned numbers that were wrong in a very particular way: always close to half of what the same sensor reported over its I²C pins. Not jittery, not occasionally broken. Half.

That «half» is a fingerprint, and once you have seen it once you never spend an afternoon on it again. A right shift by one bit is division by two. Random corruption does not produce an exact factor; a phase error does.

What the four mode numbers actually say

SPI has two independent bits of configuration and four names for their combinations, which is one more name than the situation deserves.

Note what follows from that. Mode 0 idles low and samples on the leading edge, which is a rising edge. Mode 3 idles high, so its leading edge is falling and it samples on the trailing edge, which is also a rising edge. Modes 0 and 3 therefore sample on the same polarity of edge and differ only in the idle level and in where the data transitions sit. That is why parts and controllers so often work across those two and why the mode number alone is a poor way to describe what a device wants.

The pair that breaks is a CPHA mismatch. My master was set to CPHA = 0 and the sensor wanted CPHA = 1. Both agreed on the clock polarity, so the capture looks perfectly healthy - correct idle level, clean edges, right number of clocks, chip select framing the transfer properly. Nothing on the scope says «error».

SCK MISO, device wants CPHA = 1 what a CPHA = 0 master reads idle D7=1 D6=0 D5=1 D4=0 idleD7 D6D5 every sample lands one bit late: the first is the idle level, the last bit is never read
Fig. 3 - the master samples exactly where the device is changing its output. The first sample is the idle level of MISO and every real bit arrives one position too late, which assembles into the original value shifted right by one.

The evidence, in bytes

I clamped the sensor to a stable pressure and read the same register forty times over each interface. The I²C path is independent of the SPI configuration, so it served as the reference.

Same register, same conditions, read over both interfaces
Reference (I²C)Over SPI, CPHA = 0Ratio
0x3C7A0x1E3D0.5000
0x3C880x1E440.5000
0x3D020x1E810.5000
0x7FFE0x3FFF0.5000

Four digits of ratio and no scatter at all. The top bit of every SPI reading is zero because MISO idles low on this part, and the bottom bit of the true value is simply never sampled - the transfer ends before it would have been. If MISO had idled high, the readings would have come back as the true value shifted right with a one in the top bit, and the ratio would have looked random instead of clean. That is worth knowing, because the clean ratio is what made this quick.

The clock-rate trap

The person who handed me the board told me it «worked better slowly», and I believed that for half an hour. At 1 MHz the readings did look more plausible - but only because this sensor's output changes slowly, so a value shifted right by one bit still decodes to a pressure in the right part of the scale, and nobody was comparing against an independent reading.

It cannot be fixed by slowing down, and that is the useful diagnostic. A CPHA mismatch puts the sample and the data transition on the same edge, so stretching the clock period moves both of them and changes nothing about their relationship. A genuine setup or hold violation does get better when you slow down, because the period is exactly what it is short of.

Slowing the clock cures it
You had a timing-margin problem: propagation delay, a long MISO trace, a buffer in the path, or a device that needs more time between chip select and the first clock.
Slowing the clock changes nothing
You have a phase problem. Check CPHA on both sides, and check it against the device's timing diagram rather than against its mode number.

The fix was one register field on the master, from CPHA = 0 to CPHA = 1. The readings matched the I²C reference on all forty samples afterwards, bit for bit.