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.
