Electronics Design AU
CommunicationsSolved

SPI reads all returning 0xFF — logic analyser shows MISO activity, W25Q32 not responding to commands

5 min read3 replies
Original Question

Asked by stale_biscuit_03 ·

Been staring at this one for a day and a half. I'm trying to read the JEDEC ID from a W25Q32JV SPI flash chip on a custom STM32L432 board. The read command (0x9F) should return 0xEF 0x40 0x16, but instead I'm getting 0xFF 0xFF 0xFF every single time.

The confusing part is that my Saleae actually shows activity on MISO during the transaction — the line is doing something. So the flash chip isn't completely ignoring me?

What I've confirmed:

  • 3.3V on VCC, WP and HOLD tied high through 10kΩ pull-ups
  • CS goes low before the first SCK edge (scope confirms it, spent half an hour on this)
  • SPI at 1 MHz, also tried 500 kHz and 250 kHz, same result
  • MOSI/MISO/SCK continuity all good, measured to the pads directly
  • Swapped to a second flash chip from a different reel, exact same result
  • Using HAL_SPI_TransmitReceive with a 4-byte buffer: 0x9F followed by three 0x00 dummy bytes, CS held low manually (not hardware NSS) for the whole call

CubeMX config: Full-Duplex Master, 8-bit data size, MSB First. I didn't touch the clock polarity or phase settings from the defaults.

Does the MISO activity mean the chip is at least seeing the CS go low and responding in some way? Or is this just noise?

From the knowledge baseWhat Is SPI (Serial Peripheral Interface)?

3 Replies

spi_speed_demon
Accepted Answer

The symptom — MISO active on the analyser but all reads returning 0xFF — is the textbook sign of a CPOL/CPHA mismatch. The flash is clocking data out. Your MCU is just sampling that data on the wrong clock edge, so it captures the MISO line while it's idle-high instead of when a bit is valid. Idle high across all eight bits → 0xFF.

To answer your question: yes, MISO activity means the chip is alive, seeing the CS toggle, and attempting to respond. The problem is on the sampling side, not the wiring.

The mode issue

The W25Q32JV only supports Mode 0 (CPOL=0, CPHA=0) and Mode 3 (CPOL=1, CPHA=1). Both work because both sample on the rising edge of SCK — they just differ in what SCK looks like at rest. Mode 0 idles low, Mode 3 idles high. The flash outputs a bit after the falling edge and holds it through the next rising edge, which is where you need to sample.

Mode 1 (CPOL=0, CPHA=1) samples on the falling edge — exactly when the flash is changing its output. You'll capture the wrong state or a transition, which across a full byte looks like 0xFF at typical SPI speeds because you're reading the line between valid windows.

What to check in CubeMX

The CubeMX clock phase and polarity dropdowns are separate from the "SPI Mode" selector. Look for Clock Polarity and Clock Phase explicitly. For Mode 0: Clock Polarity = Low, Clock Phase = 1 Edge. "2 Edge" is CPHA=1 (Mode 1). Verify the generated init code too — the graphic and the generated struct can get out of sync if you've edited things by hand.

CS timing

Your setup with HAL_SPI_TransmitReceive holding CS for all four bytes is correct. If you were toggling CS between the command byte and the response bytes, the flash would reset its internal state machine and the response would be garbage. Sounds like you've got this right, but worth confirming the CS GPIO is only released after HAL_SPI_TransmitReceive returns.

Fix the mode first. The MISO activity you're seeing already confirms the chip is functional and responding — this is purely a timing configuration issue. The SPI protocol explainer has the full mode table with sampling-edge diagrams if you want to cross-reference the four combinations.

glitch_getter

You can confirm exactly which mode is correct using the existing Saleae capture, without touching the firmware.

Open the SPI async decoder on your capture and try changing only the Clock Phase setting (CPHA in the decoder). When the decoded MISO bytes change from 0xFF 0xFF 0xFF to 0xEF 0x40 0x16, that's the mode the flash is actually talking. Then you match the firmware to what the decoder confirmed, not the other way around.

Two things to look at in the raw waveform to orient yourself:

SCK idle state: Is SCK low when CS is high? That's CPOL=0. SCK idle high means CPOL=1. W25Q32 supports both, so either is valid — but your STM32 init must match.

MISO transition timing: Zoom in on a single bit period. The W25Q32 drives MISO after the falling edge of SCK and holds it through the next rising edge. In a correct Mode 0 capture, MISO should be rock-solid when SCK goes high — that's your sampling moment. If MISO is visibly transitioning right at the SCK rising edge instead of being stable there, the flash is outputting for Mode 0 but your MCU is set to Mode 1 and sampling half a cycle off. That's when you get 0xFF.

Adjust the decoder first. Match the firmware to the confirmed mode. Saves you another firmware iteration in the dark. If you're new to logic analyser capture setups, logic analyser vs oscilloscope covers SPI multi-channel trigger configuration and what to look for in the capture.

soggy_waffle42

Even if CubeMX looks right, check the generated init code directly. The STM32 HAL and CubeMX peripheral guide covers what each field in the SPI init struct does — but specifically, find MX_SPI1_Init (or whichever peripheral) and verify it actually says:

hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase    = SPI_PHASE_1EDGE;

CubeMX regenerates this on every .ioc save, so if your source and .ioc got out of step, the screen and the binary can disagree. SPI_PHASE_1EDGE is Mode 0. SPI_PHASE_2EDGE is Mode 1. That's the field to find.

Also check hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB. The W25Q32 is MSB-first. If this is set to LSB, the command byte 0x9F gets transmitted as 0xF9, which the flash doesn't recognise. It would still clock something out on MISO — probably zeros or garbage from an unrecognised command — which would explain the MISO activity without getting a valid JEDEC ID back.

Last thing worth checking: HAL_SPI_GetState. If the peripheral is sitting in an error state (MODF, for example), every HAL_SPI_TransmitReceive call silently fails and returns the transmit buffer contents on the receive side. MODF fires when hardware NSS detects a low level while the peripheral thinks it's master. If you've got the hardware NSS pin floating or accidentally shorted, that can corrupt every transaction without logging anything obvious. Confirm HAL_SPI_GetState returns HAL_SPI_STATE_READY before the call.

Related Discussions