SPI reads all returning 0xFF — logic analyser shows MISO activity, W25Q32 not responding to commands
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?
3 Replies
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.
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.
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
I2C bus scan finding nothing — NACK on every address despite pull-ups
Working through my first proper I2C project — hooking up a BME280 temp/humidity sensor to an ESP32 devkit. Wired SDA to GPIO21 and SCL to GP
STM32F401 UART printing garbage after switching to 84 MHz PLL — same 115200 baud in CubeMX and PuTTY
Got a WeAct Black Pill (STM32F401CCU6) project that's been running happily on the default HSI clock at 16 MHz. Using USART1 on PA9/PA10 thro
STM32 USB not detected by Windows after jumping to bootloader mode
Working on a custom STM32F411 board, trying to jump into the built-in USB DFU bootloader from application code instead of holding BOOT0 on p