STM32 QSPI驱动GD25Q80E SPI NOR Flash实战指南

发布时间:2026/9/19 12:20:02
STM32 QSPI驱动GD25Q80E SPI NOR Flash实战指南 1. 搞懂 SPI NOR Flash 到底在系统里扮演什么角色1.1 为什么嵌入式项目绕不开 SPI NOR Flash做过 STM32 项目的人迟早会碰到一个尴尬局面芯片内部 Flash 不够用了。可能是要存字库、图片资源、音频片段也可能是要做 OTA 升级需要一块外部存储来暂存固件包又或者是数据采集项目需要掉电保存大量日志。这时候 SPI NOR Flash 几乎是默认选项而 GD25Q80E 这颗 8Mbit1MB的芯片因为价格便宜、供货稳定、驱动简单成了很多方案里的常客。它和 EEPROM、SD 卡、NAND Flash 的区别用生活场景类比一下就清楚了。EEPROM 像一个小笔记本容量小但可以按字节擦写适合存配置参数SD 卡像一个大仓库容量大但管理复杂需要文件系统NAND Flash 像集装箱码头单位成本极低但坏块管理麻烦适合大容量存储。SPI NOR Flash 则介于中间——容量适中常见 1MB 到 16MB支持随机读取可以直接跑代码XIP接口简单到只需要 SPI 四根线是嵌入式系统里最省心的外部存储方案。GD25Q80E 具体参数值得先摆出来8Mbit 容量也就是 1M 字节支持标准 SPI、Dual SPI、Quad SPI 三种模式最高时钟频率在 Quad 模式下可以到 120MHz扇区大小 4KB块大小 64KB整片擦除一次工作电压 2.7V 到 3.6V和 STM32 的 3.3V 电平天然匹配。这些参数决定了它在 STM32 项目里的使用方式后面会逐一展开。1.2 STM32 QSPI 外设带来的性能跃升如果只是用普通 SPI 去读写 GD25Q80ESTM32F1 系列跑 18MHz 已经算快了读 1MB 数据要花不少时间。但 STM32F4、F7、H7、L4 等系列内置了 Quad SPIQSPI外设情况就完全不同了。QSPI 不是简单的四线 SPI它把时钟、片选、四根数据线整合成一个专用外设支持命令阶段、地址阶段、数据阶段分别配置线宽还能配合 DMA 实现零 CPU 占用的批量传输。更关键的是 QSPI 支持内存映射模式Memory-Mapped Mode。配置好之后外部 Flash 的地址空间会映射到 STM32 的 0x90000000 起始区域CPU 可以直接用指针读取就像访问内部 Flash 一样。这个特性对于跑 LVGL 图形库、存放字库或者执行外部代码来说价值巨大。我实测过用 QSPI 内存映射模式驱动一块 240x320 的 TFT 屏刷图片帧率比普通 SPI 模式高出三倍以上。不过 QSPI 的配置复杂度也上了一个台阶。命令阶段的指令码、地址阶段的字节数、 dummy cycles 的数量、交替字节的配置每一项都要和 Flash 手册严格对应。GD25Q80E 的 datasheet 里有一张命令表QSPI 初始化本质上就是把这张表翻译成 STM32 的寄存器配置。这也是很多人卡住的地方——不是不会写代码而是不知道每个参数该填什么。1.3 这篇内容适合谁来参考如果你正在做 STM32 项目需要外挂一颗 SPI NOR Flash 来存资源或做 OTA那这篇内容基本可以当操作手册用。我会从 GD25Q80E 的命令时序讲起把每个关键命令的波形含义拆开然后落到 STM32 QSPI 的 HAL 库配置上给出可以直接编译运行的代码框架。中间会穿插我实际调试时踩过的坑比如 dummy cycles 配错导致读出来全是 0xFF、片选信号被其他外设干扰、DMA 传输长度不对导致数据错位这些问题。如果你用的是普通 SPI 模式而不是 QSPI前半部分的命令时序分析同样适用后半部分只需要把 QSPI 配置替换成 SPI 配置即可。如果你用的是 W25Q80、W25Q128 等其他 NOR Flash命令集大同小异重点看时序分析和 QSPI 配置思路具体指令码查对应手册就行。2. GD25Q80E 命令时序深度拆解2.1 标准 SPI 命令格式与波形解读GD25Q80E 的所有操作都通过命令来驱动每条命令的格式在 datasheet 里都有明确的时序图。标准 SPI 模式下命令由三部分组成指令码1 字节、地址0 到 4 字节视命令而定、数据0 到 N 字节。片选信号 CS# 拉低表示命令开始拉高表示命令结束。时钟极性 CPOL0、时钟相位 CPHA0 是模式 0这是 GD25Q80E 最常用的配置。以读状态寄存器命令 0x05 为例时序是这样的CS# 拉低主机在时钟上升沿依次发送 0x05然后在后续时钟下降沿读取 Flash 返回的 1 字节状态数据最后 CS# 拉高。整个过程没有地址阶段。这个命令用来查询 Flash 的忙状态bit0 为 WIP和写使能状态bit1 为 WEL是所有写操作前必须调用的。再看读数据命令 0x03格式是指令码 0x03 3 字节地址 N 字节数据。地址是 24 位因为 GD25Q80E 容量 1MB需要 20 位地址线但标准命令统一用 3 字节地址。数据阶段可以连续读取任意长度Flash 内部地址会自动递增读到末尾会回卷到 0。这个特性在连续读取大块数据时很有用不需要手动处理地址边界。注意0x03 命令在标准 SPI 模式下最高时钟频率是 50MHz 左右具体值查 datasheet 的 AC 特性表。如果超频使用读出来的数据可能不稳定表现为偶发位翻转。2.2 写使能、擦除与编程的完整流程NOR Flash 的写入规则和 RAM 完全不同只能把 1 写成 0不能把 0 写成 1。所以每次写入前必须先擦除擦除操作会把整个扇区或块恢复成全 0xFF。这个特性决定了写操作的完整流程写使能 - 擦除 - 等待擦除完成 - 写使能 - 编程 - 等待编程完成。写使能命令是 0x06没有地址和数据阶段CS# 拉低发送 0x06 再拉高即可。执行后状态寄存器的 WEL 位会置 1表示允许下一次写操作。注意 WEL 位在每次擦除或编程完成后会自动清零所以每次写操作前都要重新发送 0x06。扇区擦除命令是 0x20格式为 0x20 3 字节地址。擦除的最小单位是 4KB 扇区地址可以是扇区内任意位置Flash 会自动对齐到扇区起始。块擦除命令 0xD8 擦除 64KB整片擦除命令 0xC7 擦除全部。擦除时间在 datasheet 里有典型值扇区擦除约 45ms块擦除约 150ms整片擦除约 3s。实际使用中要留足余量尤其是低温环境下擦除时间会变长。页编程命令是 0x02格式为 0x02 3 字节地址 N 字节数据。一页是 256 字节如果写入数据超过页边界地址会回卷到页首覆盖之前写入的数据。这是新手最容易踩的坑想连续写 512 字节结果后 256 字节覆盖了前 256 字节。正确做法是分两次页编程或者用 QSPI 的自动地址递增模式配合正确的长度控制。2.3 Quad 模式下的命令差异与 dummy cycles切换到 Quad 模式后命令格式发生变化。数据线从 1 根变成 4 根指令码和地址可以选择用 1 线或 4 线传输。GD25Q80E 支持的命令包括0xEBQuad 快速读取、0x32Quad 页编程、0x38Quad 扇区擦除等。其中 0xEB 是最常用的 Quad 读命令格式为指令码 0xEB1 线 3 字节地址4 线 模式字节4 线通常发 0x00 dummy cycles N 字节数据4 线。dummy cycles 是 Quad 模式特有的概念。因为 Flash 收到地址后需要时间准备数据主机必须在这段时间内发送空时钟不传输有效数据。GD25Q80E 在 0xEB 命令下需要 6 个 dummy cycles具体值查 datasheet 的 AC 表。如果 dummy cycles 配少了读出来的数据会错位配多了数据会延迟几个周期出现。STM32 QSPI 外设里有专门的 DummyCycles 配置项填错这个值是最常见的调试问题之一。提示不同厂商、不同型号的 NOR Flash 在 Quad 读命令下的 dummy cycles 可能不同。W25Q80 是 8 个GD25Q80E 是 6 个换芯片时一定要重新查表。2.4 状态寄存器与忙等待机制状态寄存器是 Flash 和主机之间的握手信号。GD25Q80E 有两个状态寄存器命令 0x05 读寄存器 10x35 读寄存器 2。寄存器 1 的 bit0 是 WIPWrite In Progressbit1 是 WELWrite Enable Latchbit2 到 bit6 是块保护位bit7 是状态寄存器保护位。每次擦除或编程后必须轮询 WIP 位直到它变成 0才能进行下一步操作。轮询的代码很简单但实际项目中容易出问题的地方在于超时处理。如果 Flash 因为某种原因一直处于忙状态死循环轮询会导致整个系统卡死。我的做法是加一个超时计数器比如轮询 100 万次还没完成就返回错误码让上层决定是重试还是报错。这个超时值要根据擦除时间来估算扇区擦除 45ms假设 SPI 时钟 10MHz读一次状态寄存器大概 1us那 45ms 需要轮询约 45000 次留 10 倍余量就是 45 万次。另外GD25Q80E 支持忙等待时的片选保持吗不支持。每次读状态寄存器都要完整地拉低 CS#、发命令、读数据、拉高 CS#。有些 Flash 支持在忙的时候保持 CS# 低电平然后连续读状态但 GD25Q80E 不在此列必须每次重新发起命令。3. STM32 QSPI 外设配置与代码实现3.1 QSPI 初始化参数逐项解析STM32 的 QSPI 外设配置集中在QSPI_InitTypeDef结构体里每个参数都直接影响和 Flash 的通信。先看时钟分频ClockPrescaler它决定 QSPI 时钟频率。假设 STM32 的 QSPI 时钟源是 200MHz分频系数设为 4则 QSPI 时钟为 50MHz。GD25Q80E 在 Quad 模式下最高支持 120MHz但实际布线质量、PCB 走线长度都会影响稳定性。我一般先用 50MHz 调通再逐步提高到 80MHz 或 100MHz 测试稳定性。FifoThreshold是 FIFO 阈值设为 4 表示 FIFO 中至少有 4 字节时才触发传输。这个值影响 DMA 请求的粒度一般设 4 或 8 即可。SampleShifting是采样偏移设为QSPI_SAMPLE_SHIFTING_HALFCYCLE表示在时钟半周期处采样这对高速信号完整性很重要。如果读数据不稳定可以尝试改这个参数。FlashSize填 Flash 容量对应的值GD25Q80E 是 1MB对应QSPI_FLASH_SIZE_82 的 8 次方等于 256不对这里要查 HAL 库定义。实际上 HAL 库的FlashSize是 log2(容量)1MB 2^20所以填 20。这个值影响内存映射模式下的地址范围。ChipSelectHighTime是片选拉高后的保持时间设为QSPI_CS_HIGH_TIME_1_CYCLE通常够用如果 Flash 反应慢可以加到 2 或 3 个周期。ClockMode设为QSPI_CLOCK_MODE_0对应 CPOL0、CPHA0。FlashID在单 Flash 模式下设为QSPI_FLASH_ID_1。这些参数配好之后调用HAL_QSPI_Init完成初始化。3.2 命令结构体与传输函数封装STM32 HAL 库用QSPI_CommandTypeDef结构体来描述一条 QSPI 命令。以读数据命令 0x03 为例配置如下Instruction填 0x03Address填目标地址AddressSize填QSPI_ADDRESS_24_BITSDataMode填QSPI_DATA_1_LINE标准 SPI 模式NbData填要读取的字节数DummyCycles填 00x03 命令不需要 dummy cyclesInstructionMode填QSPI_INSTRUCTION_1_LINE。配置好结构体后调用HAL_QSPI_Command发送命令然后调用HAL_QSPI_Receive接收数据。如果是写操作先发写使能命令 0x06再发擦除或编程命令最后轮询状态寄存器等待完成。这些操作可以封装成QSPI_WriteEnable、QSPI_SectorErase、QSPI_PageProgram、QSPI_WaitForWriteEnd等函数上层调用起来就清爽了。对于 Quad 读命令 0xEB配置略有不同InstructionMode填QSPI_INSTRUCTION_1_LINEAddressMode填QSPI_ADDRESS_4_LINESDataMode填QSPI_DATA_4_LINESDummyCycles填 6Instruction填 0xEB。这样配置后地址和数据都走 4 线读取速度是标准模式的 4 倍。3.3 内存映射模式的配置与使用内存映射模式是 QSPI 最强大的功能。配置步骤是先发一条 Quad 读命令 0xEB但NbData设为 0表示只发命令不读数据然后调用HAL_QSPI_MemoryMapped函数传入QSPI_CommandTypeDef和QSPI_MemoryMappedTypeDef结构体。后者包含TimeOutActivation和TimeOutPeriod两个参数用于超时管理。配置完成后外部 Flash 的内容就映射到了 0x90000000 地址。你可以直接用*(uint8_t*)(0x90000000 offset)读取任意位置的数据CPU 会自动生成 QSPI 时序。这个模式下读数据不需要 CPU 干预DMA 也可以直接从这个地址搬运数据到其他地方。注意内存映射模式下不能执行写操作。要写数据必须先退出内存映射模式发写命令再重新进入。退出方法是调用HAL_QSPI_Abort或者重新初始化 QSPI。我实际用这个模式跑 LVGL 的图片资源把图片数组放在外部 Flash 的固定地址LVGL 直接从映射地址读像素数据CPU 占用率几乎为零。唯一要注意的是映射地址的访问速度比内部 Flash 慢如果跑代码需要配置指令缓存I-Cache来弥补。3.4 DMA 传输配置与性能优化QSPI 配合 DMA 可以进一步降低 CPU 占用。以读取 4KB 数据为例配置 DMA 通道时外设地址填hqspi-Instance-DR内存地址填接收缓冲区传输方向为外设到内存数据宽度为字节。然后调用HAL_QSPI_Command发命令再调用HAL_QSPI_Receive_DMA启动 DMA 接收。DMA 传输的长度要和NbData一致否则会出现数据错位或传输不完整。我遇到过一个问题NbData设了 4096但 DMA 缓冲区只开了 2048 字节结果 DMA 传输越界把相邻变量的内存覆盖了。这种问题调试起来很隐蔽因为现象是其他变量莫名其妙被改而不是 QSPI 本身报错。性能优化方面有几个实测有效的技巧把 QSPI 时钟从 50MHz 提到 100MHz读取 1MB 数据的时间从约 200ms 降到约 100ms开启 QSPI 的 prefetch 功能连续读取时命中率很高对于频繁读取的小块数据放在内存映射模式下的固定地址避免每次发命令的开销。4. 调试实录常见问题与排查技巧4.1 读出来全是 0xFF 或 0x00 怎么查这是最经典的问题。读出来全是 0xFF通常意味着 Flash 没有正确响应可能原因有片选信号没接对、时钟极性相位配错、Flash 供电不足、dummy cycles 配错。排查顺序建议从硬件开始用示波器看 CS#、CLK、MOSI 三根线确认命令码 0x03 或 0xEB 正确发出地址也正确。如果波形没问题再查软件配置。读出来全是 0x00可能是 Flash 处于掉电模式或者复位状态。GD25Q80E 有一个深度掉电命令 0xB9执行后电流降到 1uA 以下但需要发 0xAB 命令唤醒。如果代码里不小心发了 0xB9后续所有读取都会返回 0x00。另外如果 Flash 的 WP# 引脚被拉低写保护生效擦除和编程会失败但读取应该正常。还有一种情况是读出来的数据有规律地错位比如每 256 字节重复一次。这通常是页边界处理问题检查地址递增逻辑和NbData设置。4.2 擦除和编程失败的典型原因擦除失败最常见的原因是忘记发写使能命令 0x06。每次擦除或编程前都必须发 0x06因为 WEL 位在操作完成后会自动清零。我见过有人在初始化时发了一次 0x06然后连续做多次擦除结果只有第一次成功。正确做法是把写使能封装成独立函数每次写操作前调用。另一个原因是块保护位被设置。GD25Q80E 的状态寄存器 1 的 bit2 到 bit6 是块保护位如果这些位被置 1对应区域的擦除和编程会被拒绝。上电默认值通常是 0但如果之前有人写过保护设置就会一直生效。解决方法是在初始化时发 0x01 命令写状态寄存器把保护位清零。编程失败还可能是数据长度超过页边界。前面说过页编程超过 256 字节会回卷覆盖。如果写入 300 字节后 44 字节会覆盖页首的 44 字节。正确做法是分页写入每页不超过 256 字节且地址要对齐到页起始。4.3 QSPI 初始化失败的排查清单QSPI 初始化失败的表现是HAL_QSPI_Init返回错误或者后续命令全部超时。排查清单如下检查项可能问题解决方法时钟源QSPI 时钟未使能检查 RCC 配置使能 QSPI 时钟GPIO 复用引脚未配为 QSPI 功能检查 GPIO 初始化代码确认 AF 编号正确片选引脚CS 未连接或接错用万用表测通断确认接到 Flash 的 CS#供电Flash VCC 不足 2.7V测量 Flash 供电引脚电压复位状态Flash 处于掉电模式发 0xAB 唤醒命令参数配置FlashSize 或分频系数错误对照 datasheet 重新计算我遇到过一次 QSPI 初始化失败查了半天发现是 GPIO 的复用功能编号填错了。STM32H7 的 QSPI 引脚 AF 编号是 10我填成了 9结果引脚一直处于普通 GPIO 模式QSPI 根本发不出波形。这种问题用示波器一看便知但如果没有示波器就只能靠仔细核对手册。4.4 高速模式下的信号完整性优化当 QSPI 时钟超过 80MHz 时信号完整性问题开始显现。表现是偶发数据错误读出来的数据大部分正确但个别字节随机翻转。这时候要从 PCB 层面找原因QSPI 的四根数据线是否等长走线是否远离高频干扰源有没有加匹配电阻我的经验是QSPI 走线尽量短最好控制在 5cm 以内四根数据线长度差异不超过 5mm在 Flash 端加 22 欧姆到 33 欧姆的串联匹配电阻电源引脚加 0.1uF 和 1uF 去耦电容越靠近 Flash 越好。如果这些措施都做了还是不稳定就降低时钟频率50MHz 通常能满足大部分应用需求。软件层面也可以优化调整SampleShifting参数从半周期采样改成全周期采样试试开启 QSPI 的 DDR 模式如果 Flash 支持在时钟上下沿都采样等效速率翻倍。不过 GD25Q80E 不支持 DDR这个选项用不上。5. 从裸机驱动到项目实战的进阶思路5.1 把 Flash 驱动封装成可复用模块裸机调通之后下一步是把驱动封装成独立模块方便在不同项目里复用。我的做法是定义一个nor_flash_t结构体包含 QSPI 句柄、容量、扇区大小、页大小等属性然后提供nor_flash_init、nor_flash_read、nor_flash_write、nor_flash_erase四个核心接口。上层应用只调这四个接口不关心底层是 QSPI 还是普通 SPI。写接口内部要处理擦除和分页。比如nor_flash_write收到 1000 字节数据先计算跨越了几个扇区逐个扇区擦除然后按页写入。擦除前要检查该扇区是否已经是全 0xFF如果是就跳过擦除节省时间。这个优化在频繁写入小数据的场景下效果明显。读接口可以支持内存映射模式。如果 QSPI 已经配置为内存映射读操作直接返回映射地址的指针零拷贝如果没有配置就走标准读命令。这样上层代码不用改底层根据配置自动选择最优路径。5.2 OTA 升级中的 Flash 分区设计用 GD25Q80E 做 OTA 升级是常见需求。1MB 容量怎么分区我的方案是前 512KB 存固件 A后 512KB 存固件 B再加一个 4KB 的配置扇区记录当前运行的是 A 还是 B。升级时把新固件写到非运行区校验通过后更新配置扇区重启后 Bootloader 根据配置跳转到新固件。这个方案的关键是 Bootloader 要能读写 QSPI Flash。Bootloader 本身放在 STM32 内部 Flash 里启动时先读配置扇区确定运行区然后跳转。跳转前要配置好 QSPI 内存映射模式因为固件是存在外部 Flash 里的CPU 需要能直接取指令。STM32H7 支持从 QSPI 内存映射地址执行代码但需要配置 MPU 和 I-Cache否则性能很差。注意从外部 Flash 执行代码时中断向量表要重映射到外部 Flash 的地址。STM32 的SCB-VTOR寄存器要设为 0x90000000 加上固件偏移。这个步骤漏掉的话中断会跳转到错误地址系统直接跑飞。5.3 数据日志场景的磨损均衡考虑GD25Q80E 的擦写寿命是 10 万次听起来很多但如果每秒写一次日志一天就是 86400 次一年多就写废了。做数据日志场景必须考虑磨损均衡。简单做法是轮流使用不同的扇区把 Flash 分成 N 个日志扇区每次写新数据时切换到下一个扇区写满一圈后再擦除最旧的扇区。这样每个扇区的擦写次数降到 1/N。更复杂的做法是记录每个扇区的擦写次数优先使用次数少的扇区。但 GD25Q80E 容量小存元数据本身也占空间。我的经验是对于 1MB 的 Flash分 16 个日志扇区每个扇区 4KB能存 64KB 日志数据擦写寿命等效提升 16 倍够用很久了。另外日志数据最好带时间戳和 CRC 校验。时间戳可以用 STM32 的 RTC 提供CRC 用来检测数据是否完整。读取时如果 CRC 不匹配说明该条日志写入过程中掉电了跳过即可。这个机制在意外断电时特别有用能保证已写入的日志不丢失。5.4 性能实测数据与选型建议最后分享一组实测数据供选型参考。测试平台是 STM32H743QSPI 时钟 100MHzGD25Q80E。标准 SPI 模式读 1MB 数据耗时约 800msQuad SPI 模式耗时约 200msQuad SPI 加 DMA 耗时约 180ms内存映射模式读 1MB 耗时约 150ms。写入方面扇区擦除约 50ms页编程 256 字节约 0.7ms写满 1MB 约 3.5s含擦除时间。如果项目对读取速度要求高优先用 QSPI 内存映射模式如果对写入速度要求高考虑换用支持更小擦除单位的 Flash或者用 FRAM 这类不需要擦除的存储器。GD25Q80E 的定位是性价比1MB 容量、SPI 接口、便宜好买适合大多数中小规模嵌入式项目。如果容量不够可以选 GD25Q12816MB或 GD25Q25632MB命令集兼容驱动改一下容量参数就能用。我在多个量产项目里用过 GD25Q80E最深的体会是硬件设计阶段就要把 QSPI 走线当高速信号处理别等到调试阶段才发现信号完整性问题。软件层面把擦除和写入的超时处理做好别让 Flash 的忙状态卡死整个系统。这两点做到位这颗芯片基本不会给你添麻烦。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询