serial-bench / SPI / msg #2
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.
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».
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.
| Reference (I²C) | Over SPI, CPHA = 0 | Ratio |
|---|---|---|
| 0x3C7A | 0x1E3D | 0.5000 |
| 0x3C88 | 0x1E44 | 0.5000 |
| 0x3D02 | 0x1E81 | 0.5000 |
| 0x7FFE | 0x3FFF | 0.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 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.
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.