Zbotic Logo Zbotic Logo
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

  • Shop
  • About Us
  • Contact Us
  • Reseller
  • Blogs
020 69134444
1800 209 0998
[email protected]
Help Desk
Facebook Twitter Instagram Linkedin YouTube
Zbotic Logo Zbotic Logo
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

All departments
  • 3D Print Service
  • 3D Printer
  • Batteries & Chargers
  • Development Boards
  • Drone Parts
  • EBike parts
  • Sensor Modules
  • Electronic Components
  • Electronic Modules
  • IoT and Wireless
  • Mechanical Parts and Workbench Tools
  • Motors & Drivers & Pumps & Actuators
  • DIY and Robot Kits
  • Show more
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
Return to previous page
Home Development Boards & SBCs

STM32 SPI Flash External Memory: W25Q128 Read-Write Guide

STM32 SPI Flash External Memory: W25Q128 Read-Write Guide

March 11, 2026 /Posted byJayesh Jain / 0

Adding a W25Q128 SPI flash memory to your STM32 project gives you an additional 16MB of non-volatile external storage — perfect for storing audio samples, firmware OTA updates, configuration data, file systems, and data logs. The W25Q128 uses SPI communication, making it easy to interface with any STM32 that has SPI peripheral. This comprehensive guide covers STM32 SPI Flash W25Q128 read-write operations using HAL and includes a custom driver implementation.

Table of Contents

  • W25Q128 Overview
  • Hardware Connection to STM32
  • STM32CubeMX SPI Configuration
  • W25Q128 HAL Driver Implementation
  • Read and Write Operations
  • Sector Erase Operations
  • LittleFS/FatFS on W25Q128
  • Frequently Asked Questions

W25Q128 Overview

The W25Q128 (Winbond) is a 128Mbit (16MB) SPI NOR Flash memory chip widely available in India for ₹40–₹80 per chip (SOIC-8 or DIP-8 adapter). Key characteristics:

  • Capacity: 128Mb = 16MB = 16,777,216 bytes
  • Interface: SPI (also supports Dual-SPI and Quad-SPI/QSPI)
  • Erase units: 4KB sector, 32KB block, 64KB block, chip erase
  • Page write: 256 bytes per page write operation
  • Voltage: 2.7–3.6V (3.3V nominal — compatible with STM32)
  • Speed: Up to 104 MHz SPI clock (standard SPI)
  • Endurance: 100,000 write cycles per sector
  • Data retention: 20 years

W25Q64 (8MB), W25Q32 (4MB), and W25Q256 (32MB) are pin-compatible alternatives — same driver works for all.

Hardware Connection to STM32

W25Q128 in SOIC-8 or via a DIP-8 adapter (for breadboard use):

W25Q128 Pin → STM32 Pin
CS  (pin 1) → GPIO output (e.g., PA4) — active low chip select
DO  (pin 2) → SPI MISO (Master In, Slave Out)
WP  (pin 3) → 3.3V (Write Protect disabled)
GND (pin 4) → GND
DI  (pin 5) → SPI MOSI (Master Out, Slave In)
CLK (pin 6) → SPI SCK (Clock)
HOLD(pin 7) → 3.3V (Hold function disabled)
VCC (pin 8) → 3.3V

// Example for STM32F4 (SPI1):
CS  → PA4
SCK → PA5
MISO→ PA6
MOSI→ PA7

// Decoupling: 100nF capacitor close to VCC pin
// SPI mode: Mode 0 (CPOL=0, CPHA=0)
Recommended: Arduino UNO R3 CH340G Development Board — Test W25Q128 driver concepts on Arduino with SPI library before implementing in STM32 HAL.

STM32CubeMX SPI Configuration

  1. Open .ioc file → Connectivity → SPI1
  2. Mode: Full-Duplex Master
  3. NSS Signal: Disable (we control CS manually via GPIO)
  4. Data Size: 8 Bits
  5. CPOL: Low (Mode 0)
  6. CPHA: 1 Edge (Mode 0)
  7. Prescaler: Set for ≤10 MHz to start (safe for most W25Q128 operations)
  8. Configure CS pin as GPIO Output, push-pull, initial HIGH
  9. Generate Code

W25Q128 HAL Driver Implementation

/* w25q128.h */
#ifndef W25Q128_H
#define W25Q128_H

#include "stm32f4xx_hal.h"

#define W25Q128_CS_LOW()  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)
#define W25Q128_CS_HIGH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)

/* W25Q128 Commands */
#define W25Q_CMD_WRITE_ENABLE     0x06
#define W25Q_CMD_WRITE_DISABLE    0x04
#define W25Q_CMD_READ_STATUS1     0x05
#define W25Q_CMD_READ_DATA        0x03
#define W25Q_CMD_PAGE_PROGRAM     0x02
#define W25Q_CMD_SECTOR_ERASE_4K  0x20
#define W25Q_CMD_BLOCK_ERASE_64K  0xD8
#define W25Q_CMD_CHIP_ERASE       0xC7
#define W25Q_CMD_JEDEC_ID         0x9F

/* Status Register bits */
#define W25Q_STATUS_BUSY  0x01
#define W25Q_STATUS_WEL   0x02

extern SPI_HandleTypeDef hspi1;

/* Function prototypes */
uint32_t W25Q_ReadJEDECID(void);
void W25Q_Read(uint32_t address, uint8_t *data, uint32_t length);
void W25Q_Write(uint32_t address, uint8_t *data, uint32_t length);
void W25Q_EraseSector(uint32_t sectorAddr);
void W25Q_EraseChip(void);
void W25Q_WaitBusy(void);

#endif
/* w25q128.c */
#include "w25q128.h"

static void W25Q_SendCommand(uint8_t cmd) {
    W25Q128_CS_LOW();
    HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY);
    W25Q128_CS_HIGH();
}

void W25Q_WaitBusy(void) {
    uint8_t status;
    uint8_t cmd = W25Q_CMD_READ_STATUS1;
    
    do {
        W25Q128_CS_LOW();
        HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY);
        HAL_SPI_Receive(&hspi1, &status, 1, HAL_MAX_DELAY);
        W25Q128_CS_HIGH();
    } while (status & W25Q_STATUS_BUSY);
}

uint32_t W25Q_ReadJEDECID(void) {
    uint8_t cmd = W25Q_CMD_JEDEC_ID;
    uint8_t id[3];
    
    W25Q128_CS_LOW();
    HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY);
    HAL_SPI_Receive(&hspi1, id, 3, HAL_MAX_DELAY);
    W25Q128_CS_HIGH();
    
    // W25Q128 returns: 0xEF 0x40 0x18
    return (id[0] << 16) | (id[1] <> 16) & 0xFF;  // Address byte 2 (MSB)
    header[2] = (address >> 8) & 0xFF;   // Address byte 1
    header[3] = address & 0xFF;          // Address byte 0 (LSB)
    
    W25Q128_CS_LOW();
    HAL_SPI_Transmit(&hspi1, header, 4, HAL_MAX_DELAY);
    HAL_SPI_Receive(&hspi1, data, length, HAL_MAX_DELAY);
    W25Q128_CS_HIGH();
}

void W25Q_WritePage(uint32_t address, uint8_t *data, uint32_t length) {
    uint8_t header[4];
    
    // Must erase sector before writing!
    W25Q_SendCommand(W25Q_CMD_WRITE_ENABLE);
    W25Q_WaitBusy();
    
    header[0] = W25Q_CMD_PAGE_PROGRAM;
    header[1] = (address >> 16) & 0xFF;
    header[2] = (address >> 8) & 0xFF;
    header[3] = address & 0xFF;
    
    W25Q128_CS_LOW();
    HAL_SPI_Transmit(&hspi1, header, 4, HAL_MAX_DELAY);
    HAL_SPI_Transmit(&hspi1, data, length, HAL_MAX_DELAY);
    W25Q128_CS_HIGH();
    
    W25Q_WaitBusy();
}

void W25Q_EraseSector(uint32_t sectorAddr) {
    uint8_t header[4];
    
    W25Q_SendCommand(W25Q_CMD_WRITE_ENABLE);
    W25Q_WaitBusy();
    
    // Align to 4KB sector boundary
    sectorAddr &= 0xFFFFF000;
    
    header[0] = W25Q_CMD_SECTOR_ERASE_4K;
    header[1] = (sectorAddr >> 16) & 0xFF;
    header[2] = (sectorAddr >> 8) & 0xFF;
    header[3] = sectorAddr & 0xFF;
    
    W25Q128_CS_LOW();
    HAL_SPI_Transmit(&hspi1, header, 4, HAL_MAX_DELAY);
    W25Q128_CS_HIGH();
    
    W25Q_WaitBusy();  // Sector erase takes up to 400ms!
}

Read and Write Operations

/* Usage in main.c */
#include "w25q128.h"
#include <string.h>

int main(void) {
    // Init peripherals...
    
    // Verify chip is present
    uint32_t jedec = W25Q_ReadJEDECID();
    if (jedec != 0xEF4018) {
        // W25Q128 not found! Check connections.
        Error_Handler();
    }
    
    // Erase sector before writing (mandatory for NOR flash)
    W25Q_EraseSector(0x000000);  // Erase first 4KB sector
    
    // Write data (must be within same page = 256 bytes for single write)
    char message[] = "Hello from STM32! Stored in W25Q128 Flash.";
    W25Q_WritePage(0x000000, (uint8_t*)message, strlen(message));
    
    // Read back and verify
    uint8_t readBuf[64];
    W25Q_Read(0x000000, readBuf, 64);
    readBuf[63] = 0;  // Null-terminate for print
    
    // Should print original message
    printf("Read: %s
", readBuf);
    
    while(1) {}
}

Sector Erase Operations

NOR flash can only erase bits (0→1), not set them. You must erase before writing:

  • Sector erase (4KB): Fastest, ~100–400ms. Use for frequently updated data.
  • Block erase (32KB or 64KB): Erases larger region, ~200–2000ms. For OTA firmware updates.
  • Chip erase: Erases entire 16MB, ~25–60 seconds. Rare — only on full initialisation.
// Safe write function that handles multi-page and erase
void W25Q_Write(uint32_t address, uint8_t *data, uint32_t length) {
    uint32_t currentAddr = address;
    uint8_t *currentData = data;
    uint32_t remaining = length;
    
    while (remaining > 0) {
        uint32_t pageOffset = currentAddr % 256;
        uint32_t writeLen = 256 - pageOffset;
        if (writeLen > remaining) writeLen = remaining;
        
        W25Q_WritePage(currentAddr, currentData, writeLen);
        
        currentAddr += writeLen;
        currentData += writeLen;
        remaining -= writeLen;
    }
}

LittleFS/FatFS on W25Q128

Mount a filesystem on W25Q128 for organised file storage:

// LittleFS (recommended for MCU flash) - provides wear levelling
// Add littlefs library to your project

#include "lfs.h"

// LittleFS configuration pointing to W25Q128 functions
int lfs_read(const struct lfs_config *c, lfs_block_t block,
             lfs_off_t off, void *buffer, lfs_size_t size) {
    W25Q_Read(block * 4096 + off, buffer, size);
    return 0;
}

int lfs_prog(const struct lfs_config *c, lfs_block_t block,
             lfs_off_t off, const void *buffer, lfs_size_t size) {
    W25Q_WritePage(block * 4096 + off, (uint8_t*)buffer, size);
    return 0;
}

int lfs_erase(const struct lfs_config *c, lfs_block_t block) {
    W25Q_EraseSector(block * 4096);
    return 0;
}

const struct lfs_config cfg = {
    .read = lfs_read,
    .prog = lfs_prog,
    .erase = lfs_erase,
    .sync = lfs_sync,
    .read_size = 256,
    .prog_size = 256,
    .block_size = 4096,  // 4KB sectors
    .block_count = 4096, // 16MB / 4KB = 4096 sectors
    .lookahead_size = 16,
};
Recommended: Waveshare ESP32-S3-Nano Development Board — ESP32-S3 includes QSPI interface for even faster W25Q128 access at up to 80 MHz quad-speed.

Frequently Asked Questions

What is the W25Q128 read speed compared to STM32’s internal flash?

W25Q128 via SPI at 10 MHz reads at ~1.25 MB/s. Via QSPI (Quad-SPI) at 80 MHz, it reaches ~40 MB/s. STM32F4’s internal flash at 168 MHz reads at up to 21 MB/s without wait states (slower with wait states at high speeds). For large data storage, external flash is far more cost-effective despite the slower interface.

Is W25Q128 safe for storing firmware updates (OTA)?

Yes — W25Q128 is commonly used for OTA firmware storage. The typical approach: download new firmware via WiFi (on ESP32 or STM32 with WiFi module) and write it to W25Q128, then have a bootloader copy it to internal flash on next boot. The STM32 bootloader must be in internal flash and run before the application starts.

Can W25Q128 operate at 3.3V or 5V?

W25Q128 operates at 2.7–3.6V — it’s a 3.3V device. Do not connect 5V to VCC or signal pins. STM32 GPIO outputs 3.3V maximum, which is fully compatible. If using with 5V Arduino (Uno, Mega), use a voltage divider on MOSI and SCK lines (MISO is safe as-is since it’s W25Q → Arduino direction).

How do I know if my W25Q128 is genuine or a counterfeit?

Read the JEDEC ID: genuine W25Q128 returns 0xEF4018. Counterfeits often return incorrect IDs. Also check the Unique ID (64-bit): genuine chips have a proper unique ID. Counterfeit flash is less reliable and may have smaller actual capacity than labelled — use vendor-sourced components for production designs.

How long does chip erase take on W25Q128?

W25Q128 chip erase takes 25–60 seconds (typical 50 seconds). This is too long for most applications — use sector erase (4KB, ~400ms) or block erase (64KB, ~2000ms) instead. Plan your memory layout to use the smallest erase unit that contains your data.

Shop Development Boards at Zbotic →

Tags: external memory, NOR flash embedded, SPI flash, STM32, W25Q128
Share Post
  • Facebook
  • Linkedin
  • Whatsapp
Industrial Ethernet Switch: Ma...
blog industrial ethernet switch managed vs unmanaged guide 598445
blog smart home appliance scheduling with cron and home assistant 598451
Smart Home Appliance Schedulin...

Related posts

Svg%3E
Read more

Battery Charger Module TP4056: LiPo and 18650 Charging Guide

April 1, 2026 0
The TP4056 battery charger module is one of the most essential components for any battery-powered electronics project. Costing under ₹30,... Continue reading
Svg%3E
Read more

Buck Converter vs Boost Converter: Voltage Regulation Guide

April 1, 2026 0
Understanding buck converters vs boost converters is essential for every electronics project involving power management. Whether you are stepping down... Continue reading
Svg%3E
Read more

Google Coral TPU: Accelerating AI Projects on Raspberry Pi

April 1, 2026 0
The Google Coral TPU (Tensor Processing Unit) transforms a Raspberry Pi from a sluggish AI hobbyist tool into a real-time... Continue reading
Svg%3E
Read more

NVIDIA Jetson Nano Projects India: Getting Started Guide

April 1, 2026 0
The NVIDIA Jetson Nano is the most accessible GPU-accelerated AI computer for developers in India. With 128 CUDA cores, a... Continue reading
Svg%3E
Read more

ATtiny85 Projects: Tiny Microcontroller for Space-Constrained Builds

April 1, 2026 0
The ATtiny85 is the Swiss Army knife of tiny microcontrollers — just 8 pins, 8 KB of flash, and a... Continue reading

Add comment Cancel reply

Your email address will not be published. Required fields are marked

Facebook Twitter Instagram Pinterest Linkedin Youtube

Get the latest deals and more.

Download on Google Play Download on the App Store

Call us: 020 69134444 / 1800 209 0998

Monday - Saturday 09:30 AM - 06:00 PM
For Technical Supports Email: [email protected]
For Sales / Enquiries Email: [email protected]

  • My Account

    • Cart

    • Wishlist

    • Checkout

    • My Orders

    • Track Order

    • My Account

  • Information

    • FAQs

    • Blogs

    • Career

    • About Us

    • Contact Us

    • Payment Options

  • Policies

    • Privacy Policy

    • Terms & Conditions

    • GST Input Tax Credit

    • Shipping Return Policy

    • E-Waste Collection Points

    • Our Sitemap

© Zbotic.in is registered trademark of Moxie Supply Pvt Ltd – All Rights Reserved
Login
Use Phone Number
Use Email Address
Not a member yet? Register Now
Reset Password
Use Phone Number
Use Email Address
Register
Already a member? Login Now