Fading Coder

An Old Coder’s Final Dance

Home > Tech > Content

Configuring and Using QUADSPI on STM32H7 for External Serial NOR Flash

Tech 2

QSPI essentials

Quad-SPI (QSPI) is an SPI extension that boosts throughput by transferring data over up to four data pins in parallel. STM32 MCUs expose this as the QUADSPI peripheral. Copmared to classic SPI:

  • Pins: CS/NSS, SCK, IO0, IO1, IO2, IO3 (six lines total).
  • Command/address phases are typically sent over one line; payload can be sent over 1/2/4 lines.
  • External memories like serial NOR/NAND flash and PSRAM commonly support QSPI.

SPI snapshot across STM32 generations

  • STM32F1 SPI
    • Full-duplex 3‑wire, simplex 2/3‑wire
    • 8‑ or 16‑bit frames, master/slave
    • HW/SW SS management, CRC, DMA, interrupts
    • Prescaler up to PCLK/2
  • STM32F4 SPI
    • All F1 features + optional TI frame format
    • RX/TX FIFOs, HAL driver coverage
  • STM32F7 SPI
    • All F4 features
    • 4–16‑bit configurable data size
    • 32‑bit TX/RX FIFO with DMA
  • STM32H7 SPI
    • All F7 features
    • 4–32‑bit frame size
    • Separate kernel clock domain, higher achievable SCK
    • Programmable delays (SS to first bit, inter-frame), SS polarity, MISO/MOSI swap
    • RX sampling point control, Stop/low-power wakeup, programmable FIFO thresholds and transfer sizes
    • Larger FIFOs (up to 16×8‑bit)
    • Slave underflow behavier configurable

QUADSPI on STM32F7/H7

QUADSPI adds hardware support for common serial flash workflows:

  • Modes of operation
    • Indirect: CPU issues commands and moves data via registers/FIFO
    • Auto‑polling: hardware repeatedly reads a status register and stops on match/mask
    • Memory‑mapped: read-only window of external flash on the AHB bus (e.g., 0x90000000), enabling XIP-style fetch or memcpy reads
  • Features
    • Single/dual/quad I/O, SDR and DDR
    • Optional dual‑flash (two devices in parallel) for doubled bus width/capacity
    • Integrated RX/TX FIFO, DMA/MDMA support
    • 8/16/32‑bit data accesses in indirect mode

Hardware-wise, the QUADSPI FIFO and registers sit on the AHB bus. In dual‑flash, one or two CS lines can be used depending on package/pinout.

Typical HAL flow for QUADSPI indirect transfers

  1. Configure pins and clocks (GPIO AF, QUADSPI kernel clock)
  2. Build a QSPI_CommandTypeDef with instruction/address/data modes
  3. HAL_QSPI_Command()
  4. HAL_QSPI_Transmit()/HAL_QSPI_Receive() or DMA variants
  5. Optionally HAL_QSPI_AutoPolling() to wait for BUSY/WEL bits
  6. Repeat for next operation

Reference driver: Winbond W25Q64 (8 MBytes) on STM32H7

Below is a compact, refactored HAL-based driver for a W25Q64 device. It demonstrates indirect read/write/erase, status polling, and memory-mapped fast read. Naming and structure differ from common examples, but the sequence mirrors the device datasheet.

qspi_w25x.h

#ifndef QSPI_W25X_H
#define QSPI_W25X_H

#include "stm32h7xx_hal.h"

#ifdef __cplusplus
extern "C" {
#endif

/* JEDEC and geometry */
#define W25_TOTAL_SIZE_BYTES      (0x800000U)  /* 8 MBytes */
#define W25_PAGE_SIZE_BYTES       (256U)
#define W25_JEDEC_ID_EXPECTED     (0xEF4017U)  /* W25Q64 */

/* Memory-mapped base (H7 default mapping) */
#define W25_MEM_BASE              (0x90000000UL)

/* Command set */
#define W25_CMD_RESET_EN          0x66
#define W25_CMD_RESET_DEV         0x99
#define W25_CMD_READ_JEDEC_ID     0x9F
#define W25_CMD_WRITE_ENABLE      0x06

#define W25_CMD_READ_SR1          0x05
#define W25_SR1_BUSY              0x01
#define W25_SR1_WEL               0x02

#define W25_CMD_SECTOR_ERASE_4K   0x20
#define W25_CMD_BLOCK_ERASE_32K   0x52
#define W25_CMD_BLOCK_ERASE_64K   0xD8
#define W25_CMD_CHIP_ERASE        0xC7

#define W25_CMD_PP_QUAD           0x32        /* 1-1-4 program */
#define W25_CMD_FAST_READ_QIO     0xEB        /* 1-4-4 fast read */

/* Return codes */
typedef enum {
    QSPI_W25X_OK = 0,
    QSPI_W25X_ERR_INIT = -1,
    QSPI_W25X_ERR_TXRX = -2,
    QSPI_W25X_ERR_POLL = -3,
    QSPI_W25X_ERR_ERASE = -4,
    QSPI_W25X_ERR_MAP = -5,
    QSPI_W25X_ERR_WREN = -6,
} qspi_w25x_status_t;

/* Public API */
qspi_w25x_status_t qspi_w25x_init(void);
qspi_w25x_status_t qspi_w25x_reset(void);
uint32_t            qspi_w25x_read_jedec_id(void);

qspi_w25x_status_t qspi_w25x_read(uint8_t *dst, uint32_t addr, uint32_t len);
qspi_w25x_status_t qspi_w25x_write(const uint8_t *src, uint32_t addr, uint32_t len);

qspi_w25x_status_t qspi_w25x_erase_sector_4k(uint32_t addr);
qspi_w25x_status_t qspi_w25x_erase_block_32k(uint32_t addr);
qspi_w25x_status_t qspi_w25x_erase_block_64k(uint32_t addr);
qspi_w25x_status_t qspi_w25x_erase_chip(void);

qspi_w25x_status_t qspi_w25x_enter_memmap(void); /* read-only window at W25_MEM_BASE */

/* Export QSPI handle for BSP integration if needed */
extern QSPI_HandleTypeDef g_qspi;

#ifdef __cplusplus
}
#endif

#endif /* QSPI_W25X_H */

qspi_w25x.c

#include "qspi_w25x.h"

QSPI_HandleTypeDef g_qspi;

/* --- Low-level MSP — adjust pins for your board --- */
static void qspi_gpio_clock_enable(void)
{
    __HAL_RCC_QSPI_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    __HAL_RCC_GPIOE_CLK_ENABLE();
}

void HAL_QSPI_MspInit(QSPI_HandleTypeDef *hqspi)
{
    GPIO_InitTypeDef io = {0};
    if (hqspi->Instance != QUADSPI) return;

    qspi_gpio_clock_enable();

    /* Example mapping:
       PB2  -> QSPI_CLK      (AF9)
       PB6  -> QSPI_BK1_NCS  (AF10)
       PD11 -> QSPI_BK1_IO0  (AF9)
       PE2  -> QSPI_BK1_IO1  (AF9)
       PD12 -> QSPI_BK1_IO2  (AF9)
       PD13 -> QSPI_BK1_IO3  (AF9)
     */
    io.Mode  = GPIO_MODE_AF_PP;
    io.Pull  = GPIO_NOPULL;
    io.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

    io.Alternate = GPIO_AF9_QUADSPI; io.Pin = GPIO_PIN_2;  HAL_GPIO_Init(GPIOB, &io); /* CLK */
    io.Alternate = GPIO_AF10_QUADSPI;io.Pin = GPIO_PIN_6;  HAL_GPIO_Init(GPIOB, &io); /* NCS */
    io.Alternate = GPIO_AF9_QUADSPI; io.Pin = GPIO_PIN_11; HAL_GPIO_Init(GPIOD, &io); /* IO0 */
    io.Alternate = GPIO_AF9_QUADSPI; io.Pin = GPIO_PIN_12; HAL_GPIO_Init(GPIOD, &io); /* IO2 */
    io.Alternate = GPIO_AF9_QUADSPI; io.Pin = GPIO_PIN_13; HAL_GPIO_Init(GPIOD, &io); /* IO3 */
    io.Alternate = GPIO_AF9_QUADSPI; io.Pin = GPIO_PIN_2;  HAL_GPIO_Init(GPIOE, &io); /* IO1 */
}

/* Core init: choose a kernel clock in CubeMX/clock tree (e.g., PLL2), then set prescaler here.
 * Note: in memory-mapped mode prescaler must not be 0 (driver clock must be valid). */
static qspi_w25x_status_t qspi_core_init(void)
{
    g_qspi.Instance = QUADSPI;

    g_qspi.Init.ClockPrescaler       = 1;        /* f_qspi = f_kernel / (Prescaler+1) */
    g_qspi.Init.FifoThreshold        = 32;
    g_qspi.Init.SampleShifting       = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
    g_qspi.Init.FlashSize            = 22;       /* 2^(FSIZE+1) bytes => 8 MBytes => FSIZE=22 */
    g_qspi.Init.ChipSelectHighTime   = QSPI_CS_HIGH_TIME_1_CYCLE;
    g_qspi.Init.ClockMode            = QSPI_CLOCK_MODE_3; /* Many Winbond parts accept Mode 0 or 3 */
    g_qspi.Init.FlashID              = QSPI_FLASH_ID_1;
    g_qspi.Init.DualFlash            = QSPI_DUALFLASH_DISABLE;

    if (HAL_QSPI_Init(&g_qspi) != HAL_OK) return QSPI_W25X_ERR_INIT;
    return QSPI_W25X_OK;
}

static qspi_w25x_status_t qspi_wait_ready(void)
{
    QSPI_CommandTypeDef cmd = {0};
    QSPI_AutoPollingTypeDef ap = {0};

    cmd.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    cmd.Instruction       = W25_CMD_READ_SR1;
    cmd.AddressMode       = QSPI_ADDRESS_NONE;
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    cmd.DataMode          = QSPI_DATA_1_LINE;
    cmd.DummyCycles       = 0;

    ap.Match               = 0x00;
    ap.Mask                = W25_SR1_BUSY;
    ap.MatchMode           = QSPI_MATCH_MODE_AND;
    ap.StatusBytesSize     = 1;
    ap.Interval            = 0x10;
    ap.AutomaticStop       = QSPI_AUTOMATIC_STOP_ENABLE;

    if (HAL_QSPI_AutoPolling(&g_qspi, &cmd, &ap, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
        return QSPI_W25X_ERR_POLL;

    return QSPI_W25X_OK;
}

static qspi_w25x_status_t qspi_write_enable(void)
{
    QSPI_CommandTypeDef cmd = {0};
    QSPI_AutoPollingTypeDef ap = {0};

    cmd.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    cmd.Instruction       = W25_CMD_WRITE_ENABLE;
    cmd.AddressMode       = QSPI_ADDRESS_NONE;
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    cmd.DataMode          = QSPI_DATA_NONE;

    if (HAL_QSPI_Command(&g_qspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
        return QSPI_W25X_ERR_WREN;

    /* poll WEL */
    cmd.Instruction = W25_CMD_READ_SR1;
    cmd.DataMode    = QSPI_DATA_1_LINE;

    ap.Match           = W25_SR1_WEL;
    ap.Mask            = W25_SR1_WEL;
    ap.MatchMode       = QSPI_MATCH_MODE_AND;
    ap.StatusBytesSize = 1;
    ap.Interval        = 0x10;
    ap.AutomaticStop   = QSPI_AUTOMATIC_STOP_ENABLE;

    if (HAL_QSPI_AutoPolling(&g_qspi, &cmd, &ap, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
        return QSPI_W25X_ERR_POLL;

    return QSPI_W25X_OK;
}

qspi_w25x_status_t qspi_w25x_reset(void)
{
    QSPI_CommandTypeDef cmd = {0};

    cmd.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    cmd.AddressMode       = QSPI_ADDRESS_NONE;
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    cmd.DataMode          = QSPI_DATA_NONE;
    cmd.DummyCycles       = 0;

    cmd.Instruction = W25_CMD_RESET_EN;
    if (HAL_QSPI_Command(&g_qspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
        return QSPI_W25X_ERR_INIT;

    if (qspi_wait_ready() != QSPI_W25X_OK) return QSPI_W25X_ERR_POLL;

    cmd.Instruction = W25_CMD_RESET_DEV;
    if (HAL_QSPI_Command(&g_qspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
        return QSPI_W25X_ERR_INIT;

    if (qspi_wait_ready() != QSPI_W25X_OK) return QSPI_W25X_ERR_POLL;

    return QSPI_W25X_OK;
}

uint32_t qspi_w25x_read_jedec_id(void)
{
    QSPI_CommandTypeDef cmd = {0};
    uint8_t id[3] = {0};

    cmd.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    cmd.Instruction       = W25_CMD_READ_JEDEC_ID;
    cmd.AddressMode       = QSPI_ADDRESS_NONE;
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    cmd.DataMode          = QSPI_DATA_1_LINE;
    cmd.NbData            = 3;

    if (HAL_QSPI_Command(&g_qspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
        return 0;
    if (HAL_QSPI_Receive(&g_qspi, id, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
        return 0;

    return ((uint32_t)id[0] << 16) | ((uint32_t)id[1] << 8) | id[2];
}

static qspi_w25x_status_t qspi_page_program_quad(const uint8_t *src, uint32_t addr, uint32_t len)
{
    QSPI_CommandTypeDef cmd = {0};
    if (len == 0 || len > W25_PAGE_SIZE_BYTES) return QSPI_W25X_OK;

    if (qspi_write_enable() != QSPI_W25X_OK) return QSPI_W25X_ERR_WREN;

    cmd.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    cmd.Instruction       = W25_CMD_PP_QUAD;   /* 1-1-4 */
    cmd.AddressMode       = QSPI_ADDRESS_1_LINE;
    cmd.AddressSize       = QSPI_ADDRESS_24_BITS;
    cmd.Address           = addr;
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    cmd.DataMode          = QSPI_DATA_4_LINES;
    cmd.NbData            = len;

    if (HAL_QSPI_Command(&g_qspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
        return QSPI_W25X_ERR_TXRX;
    if (HAL_QSPI_Transmit(&g_qspi, (uint8_t *)src, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
        return QSPI_W25X_ERR_TXRX;

    return qspi_wait_ready();
}

qspi_w25x_status_t qspi_w25x_read(uint8_t *dst, uint32_t addr, uint32_t len)
{
    if (len == 0) return QSPI_W25X_OK;

    QSPI_CommandTypeDef cmd = {0};
    cmd.InstructionMode   = QSPI_INSTRUCTION_1_LINE;   /* 1-4-4 */
    cmd.Instruction       = W25_CMD_FAST_READ_QIO;
    cmd.AddressMode       = QSPI_ADDRESS_4_LINES;
    cmd.AddressSize       = QSPI_ADDRESS_24_BITS;
    cmd.Address           = addr;
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    cmd.DataMode          = QSPI_DATA_4_LINES;
    cmd.DummyCycles       = 6;
    cmd.NbData            = len;

    if (HAL_QSPI_Command(&g_qspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
        return QSPI_W25X_ERR_TXRX;
    if (HAL_QSPI_Receive(&g_qspi, dst, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
        return QSPI_W25X_ERR_TXRX;

    return qspi_wait_ready();
}

qspi_w25x_status_t qspi_w25x_write(const uint8_t *src, uint32_t addr, uint32_t len)
{
    if (len == 0) return QSPI_W25X_OK;

    uint32_t cur = addr;
    uint32_t remaining = len;
    const uint8_t *p = src;

    while (remaining > 0)
    {
        uint32_t page_off = cur % W25_PAGE_SIZE_BYTES;
        uint32_t space    = W25_PAGE_SIZE_BYTES - page_off;
        uint32_t chunk    = (remaining < space) ? remaining : space;

        qspi_w25x_status_t st = qspi_page_program_quad(p, cur, chunk);
        if (st != QSPI_W25X_OK) return st;

        cur       += chunk;
        p         += chunk;
        remaining -= chunk;
    }
    return QSPI_W25X_OK;
}

static qspi_w25x_status_t qspi_erase_common(uint8_t opcode, uint32_t addr)
{
    QSPI_CommandTypeDef cmd = {0};

    if (qspi_write_enable() != QSPI_W25X_OK) return QSPI_W25X_ERR_WREN;

    cmd.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    cmd.Instruction       = opcode;
    cmd.AddressMode       = (opcode == W25_CMD_CHIP_ERASE) ? QSPI_ADDRESS_NONE : QSPI_ADDRESS_1_LINE;
    cmd.AddressSize       = QSPI_ADDRESS_24_BITS;
    cmd.Address           = addr;
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    cmd.DataMode          = QSPI_DATA_NONE;

    if (HAL_QSPI_Command(&g_qspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
        return QSPI_W25X_ERR_ERASE;

    /* Long operations (especially chip erase) rely on status polling */
    return qspi_wait_ready();
}

qspi_w25x_status_t qspi_w25x_erase_sector_4k(uint32_t addr)
{
    return qspi_erase_common(W25_CMD_SECTOR_ERASE_4K, addr);
}

qspi_w25x_status_t qspi_w25x_erase_block_32k(uint32_t addr)
{
    return qspi_erase_common(W25_CMD_BLOCK_ERASE_32K, addr);
}

qspi_w25x_status_t qspi_w25x_erase_block_64k(uint32_t addr)
{
    return qspi_erase_common(W25_CMD_BLOCK_ERASE_64K, addr);
}

qspi_w25x_status_t qspi_w25x_erase_chip(void)
{
    return qspi_erase_common(W25_CMD_CHIP_ERASE, 0);
}

qspi_w25x_status_t qspi_w25x_enter_memmap(void)
{
    QSPI_CommandTypeDef cmd = {0};
    QSPI_MemoryMappedTypeDef cfg = {0};

    /* 1-4-4 fast read mapping */
    cmd.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    cmd.Instruction       = W25_CMD_FAST_READ_QIO;
    cmd.AddressMode       = QSPI_ADDRESS_4_LINES;
    cmd.AddressSize       = QSPI_ADDRESS_24_BITS;
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    cmd.DataMode          = QSPI_DATA_4_LINES;
    cmd.DummyCycles       = 6;

    cfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;
    cfg.TimeOutPeriod     = 0;

    if (qspi_w25x_reset() != QSPI_W25X_OK) return QSPI_W25X_ERR_INIT;
    if (HAL_QSPI_MemoryMapped(&g_qspi, &cmd, &cfg) != HAL_OK) return QSPI_W25X_ERR_MAP;

    return QSPI_W25X_OK;
}

qspi_w25x_status_t qspi_w25x_init(void)
{
    if (qspi_core_init() != QSPI_W25X_OK) return QSPI_W25X_ERR_INIT;
    if (qspi_w25x_reset() != QSPI_W25X_OK) return QSPI_W25X_ERR_INIT;

    uint32_t id = qspi_w25x_read_jedec_id();
    if (id != W25_JEDEC_ID_EXPECTED) return QSPI_W25X_ERR_INIT;

    return QSPI_W25X_OK;
}

Example: basic use in indirect mode

#include "qspi_w25x.h"

static uint8_t tx_buf[1024];
static uint8_t rx_buf[1024];

void qspi_demo(void)
{
    if (qspi_w25x_init() != QSPI_W25X_OK) {
        /* handle error */
        return;
    }

    /* Erase, program, read back */
    uint32_t test_addr = 0x00010000; /* aligned as needed */

    qspi_w25x_erase_block_64k(test_addr);

    for (size_t i = 0; i < sizeof(tx_buf); ++i) tx_buf[i] = (uint8_t)i;

    qspi_w25x_write(tx_buf, test_addr, sizeof(tx_buf));
    qspi_w25x_read(rx_buf,  test_addr, sizeof(rx_buf));
}

Memory-mapped read window (read-only)

/***** one-time setup *****/
if (qspi_w25x_enter_memmap() == QSPI_W25X_OK) {
    const uint8_t *flash = (const uint8_t *)(W25_MEM_BASE);
    uint8_t buffer[256];
    uint32_t offset = 0x00020000;
    memcpy(buffer, flash + offset, sizeof(buffer));
    /* process buffer */
}

Optional: MDMA integration for QUADSPI RX/TX

To reduce CPU overhead for long reads/writes, attach an MDMA channel to QUADSPI. Example setup:

static MDMA_HandleTypeDef g_mdma_qspi;

static void mdma_qspi_init(void)
{
    __HAL_RCC_MDMA_CLK_ENABLE();

    g_mdma_qspi.Instance = MDMA_Channel1;
    g_mdma_qspi.Init.TransferTriggerMode   = MDMA_BUFFER_TRANSFER;
    g_mdma_qspi.Init.Priority              = MDMA_PRIORITY_VERY_HIGH;
    g_mdma_qspi.Init.Request               = MDMA_REQUEST_QUADSPI_FIFO_TH; /* FIFO threshold */

    g_mdma_qspi.Init.Endianness            = MDMA_LITTLE_ENDIANNESS_PRESERVE;
    g_mdma_qspi.Init.DataAlignment         = MDMA_DATAALIGN_PACKENABLE;

    g_mdma_qspi.Init.SourceInc             = MDMA_SRC_INC_BYTE;
    g_mdma_qspi.Init.SourceDataSize        = MDMA_SRC_DATASIZE_BYTE;
    g_mdma_qspi.Init.SourceBurst           = MDMA_SOURCE_BURST_SINGLE;

    g_mdma_qspi.Init.DestinationInc        = MDMA_DEST_INC_DISABLE;
    g_mdma_qspi.Init.DestDataSize          = MDMA_DEST_DATASIZE_BYTE;
    g_mdma_qspi.Init.DestBurst             = MDMA_DEST_BURST_SINGLE;

    __HAL_LINKDMA(&g_qspi, hmdma, g_mdma_qspi);

    HAL_MDMA_Init(&g_mdma_qspi);

    HAL_NVIC_SetPriority(MDMA_IRQn, 14, 0);
    HAL_NVIC_EnableIRQ(MDMA_IRQn);
}

void HAL_QSPI_RxCpltCallback(QSPI_HandleTypeDef *hqspi)
{
    /* RX complete flag or notify task */
}

/* In your read path, switch to HAL_QSPI_Receive_DMA() once the command is issued. */

The user-level API can remain unchanged if the internals select DMA for longer transfers, falling back to blocking for short bursts.

Quick reference: W25Qxx instruction codes

/* Protection and status */
#define W25X_WriteEnable         0x06
#define W25X_WriteDisable        0x04
#define W25X_ReadStatusReg1      0x05
#define W25X_ReadStatusReg2      0x35
#define W25X_ReadStatusReg3      0x15
#define W25X_WriteStatusReg1     0x01
#define W25X_WriteStatusReg2     0x31
#define W25X_WriteStatusReg3     0x11

/* Read */
#define W25X_ReadData            0x03
#define W25X_FastRead            0x0B
#define W25X_FastReadDual        0x3B
#define W25X_FastReadQuadIO      0xEB

/* Program */
#define W25X_PageProgram         0x02
#define W25X_QuadPageProgram     0x32

/* Erase */
#define W25X_SectorErase_4K      0x20
#define W25X_BlockErase_32K      0x52
#define W25X_BlockErase_64K      0xD8
#define W25X_ChipErase           0xC7

/* Power */
#define W25X_PowerDown           0xB9
#define W25X_ReleasePowerDown    0xAB

/* Identification */
#define W25X_DeviceID            0xAB
#define W25X_ManufactDeviceID    0x90
#define W25X_JEDECID             0x9F

/* Addressing mode */
#define W25X_Enable4ByteAddr     0xB7
#define W25X_Exit4ByteAddr       0xE9

Note: 4‑byte addressing is required for devices larger than 16 MBytes (e.g., W25Q256 and above). For 8 MByte devices like W25Q64, 24‑bit addressing is sufficient.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.