...

Adding QSPI Flash to STM32F4: W25Q128 Driver and Example Code

A complete guide to interfacing the W25Q128JV 16MB QSPI NOR Flash with the STM32F4. Covers wiring, CubeMX configuration, HAL SPI driver, read/write/erase operations, and data logging example — all tested on the NVX-F4 Flash board.

Introduction

The STM32F4CEU6 microcontroller provides 512KB of internal flash, which is sufficient for firmware but limited for data-heavy applications. When storing sensor logs, configuration files, audio data, or display assets, external flash becomes essential.

The W25Q128JV is a 16MB SPI NOR flash chip from Winbond, widely used in embedded systems due to its reliability, speed, and low cost. The NVX-F4 Flash board integrates this chip pre-wired and ready for immediate use.

This guide explains how to interface the W25Q128JV with STM32F4 using SPI, configure it in STM32CubeMX, and build a complete driver for read, write, and erase operations.


W25Q128JV Specifications

  • Capacity: 128Mbit (16MB)
  • Interface: SPI / Dual SPI / Quad SPI
  • Max Clock: 133MHz
  • Page Size: 256 bytes
  • Sector Size: 4KB
  • Block Size: 64KB
  • Voltage: 2.7V – 3.6V
  • JEDEC ID: 0xEF4018

STM32F4 SPI Flash Wiring

The NVX-F4 board already connects the flash to SPI1. For custom setups:

Flash Pin STM32 Pin Function
CS PA4 Chip Select
DO PA6 MISO
DI PA7 MOSI
CLK PA5 SCK
WP 3.3V Write Protect (disabled)
HOLD 3.3V Hold (disabled)
VCC 3.3V Power
GND GND Ground

CubeMX Configuration

Configure SPI1 as follows:

  • Mode: Full-Duplex Master
  • Data Size: 8-bit
  • Clock Polarity: Low
  • Clock Phase: 1 Edge (SPI Mode 0)
  • Baud Rate: Start around 10MHz
  • GPIO: PA4 as Output (FLASH_CS)

Generate project code after configuration.



Full SPI Flash Driver

Initialization

 
void W25Q128_Init(SPI_HandleTypeDef *hspi) {
_hspi = hspi;
FLASH_CS_HIGH();
}
 

Read Device ID

 
uint32_t W25Q128_ReadID(void) {
uint32_t id = 0;

FLASH_CS_LOW();
SPI_Transfer(0x9F);

id = (uint32_t)SPI_Transfer(0xFF) << 16;
id |= (uint32_t)SPI_Transfer(0xFF) << 8;
id |= SPI_Transfer(0xFF);

FLASH_CS_HIGH();
return id;
}

 

Read Data

 
void W25Q128_Read(uint32_t addr, uint8_t *buf, uint16_t len) {
FLASH_CS_LOW();
SPI_Transfer(0x03);

SPI_Transfer((addr >> 16) & 0xFF);
SPI_Transfer((addr >> 8) & 0xFF);
SPI_Transfer(addr & 0xFF);

for (uint16_t i = 0; i < len; i++)
buf[i] = SPI_Transfer(0xFF);

FLASH_CS_HIGH();
}

 

Page Write

 
void W25Q128_WritePage(uint32_t addr, uint8_t *buf, uint16_t len) {
FLASH_CS_LOW();
SPI_Transfer(0x06); // Write enable
FLASH_CS_HIGH();

FLASH_CS_LOW();
SPI_Transfer(0x02);

SPI_Transfer((addr >> 16) & 0xFF);
SPI_Transfer((addr >> 8) & 0xFF);
SPI_Transfer(addr & 0xFF);

for (uint16_t i = 0; i < len; i++)
SPI_Transfer(buf[i]);

FLASH_CS_HIGH();
W25Q128_WaitBusy();
}

 

Sector Erase

 
void W25Q128_EraseSector(uint32_t addr) {
FLASH_CS_LOW();
SPI_Transfer(0x06);
FLASH_CS_HIGH();

FLASH_CS_LOW();
SPI_Transfer(0x20);

SPI_Transfer((addr >> 16) & 0xFF);
SPI_Transfer((addr >> 8) & 0xFF);
SPI_Transfer(addr & 0xFF);

FLASH_CS_HIGH();
W25Q128_WaitBusy();
}

 

Example Usage

 
W25Q128_Init(&hspi1);

uint32_t id = W25Q128_ReadID();

W25Q128_EraseSector(0x000000);

uint8_t write_buf[16] = "NVX-F4 TEST DATA";
W25Q128_WritePage(0x000000, write_buf, 16);

uint8_t read_buf[16] = {0};
W25Q128_Read(0x000000, read_buf, 16);

 

Important Notes

  • Flash must be erased before writing
  • Writes cannot cross 256-byte page boundaries
  • Each sector supports ~100,000 erase cycles
  • Always wait for BUSY flag after write/erase operations

Conclusion

The W25Q128JV makes it easy to extend STM32F4 storage far beyond internal flash limits. With SPI1 integration on the NVX-F4 board, no external wiring is required, making it ideal for data logging, firmware storage, and embedded applications.

Seraphinite AcceleratorOptimized by Seraphinite Accelerator
Turns on site high speed to be attractive for people and search engines.