资讯动态

GD25Q80E SPI NOR Flash驱动开发与STM32 QSPI实战配置指南

发布时间:2026/9/19 2:50:39 来源:尧图企业网站定制
SPI NOR Flash 这颗小芯片几乎是每个嵌入式工程师都绕不开的坎。你画板子的时候随手放一颗 GD25Q80E觉得不就是个存数据的嘛结果真到写驱动的时候对着数据手册里那一堆 0x03、0xEB、0x5A 的命令码还有 QSPI 那四根数据线到底怎么发地址、怎么切模式直接懵了。我见过太多人卡在这一步要么读出来全是 0xFF要么写进去读出来对不上要么 QSPI 配了半天还不如普通 SPI 跑得快。这篇东西就是把我自己从踩坑到跑通的全过程拆开从 GD25Q80E 的命令时序底层逻辑到 STM32 QSPI 外设的实战配置把每个参数为什么这么设、每个坑为什么会出现全部讲清楚。不管你是刚接触 Flash 的新手还是已经用过 W25Q 系列但想搞明白 QSPI 到底怎么提速的老手都能从里面找到能直接抄的配置和能避开的雷。1. 先搞明白 GD25Q80E 到底是个什么角色1.1 8Mbit 的容量在嵌入式里意味着什么GD25Q80E 这个型号拆开看就很有意思。GD 是厂商前缀25 代表 SPI NOR Flash 系列Q 表示支持 Quad 模式80 代表 8Mbit 容量E 是版本后缀。8Mbit 换算过来就是 1MByte听起来不大但在嵌入式场景里能干的事情非常多。你想想STM32F103 这种经典芯片内部 Flash 才 64KB 到 128KB跑个带字库的 UI 或者存个 OTA 升级包内部空间根本不够用。GD25Q80E 这 1MB 的空间可以存 16 个 64KB 的固件备份或者存几千条带时间戳的传感器日志又或者放一整套中文字库加图片资源。但容量只是表面真正决定你能不能用好它的是它的组织方式。这颗芯片内部按 4KB 一个扇区Sector划分16 个扇区组成一个 64KB 的块Block整片一共 16 个块。为什么这个结构重要因为 NOR Flash 的擦除操作最小单位就是扇区你不能像写 RAM 一样随便改一个字节。写入之前必须先擦除擦除会把整个扇区变成 0xFF。这个特性直接决定了你文件系统或者数据存储方案的设计思路——你得做磨损均衡得考虑擦写寿命得规划好哪些区域频繁写、哪些区域只读。注意GD25Q80E 的擦写寿命标称 10 万次但这是指每个扇区独立计算的。如果你把日志一直往同一个扇区写几天就能把那个扇区写废。实际项目中一定要做地址轮转。1.2 NOR 和 NAND 的选择逻辑很多人会问为什么不用 NAND Flash容量大还便宜。这里面的核心差异在于可靠性和接口复杂度。NOR Flash 支持字节级随机读取你可以直接把它当外部 ROM 用代码可以在上面直接执行XIPExecute In Place。NAND 则是按页读取有坏块管理需要 ECC 校验接口时序也复杂得多。对于 STM32 这类 MCU 来说QSPI 接口配合 NOR Flash 是最自然的组合——硬件上四根数据线直接连软件上命令简单读取速度还能跑到几十 MB/s。GD25Q80E 的另一个优势是它的命令集和 W25Q80 系列高度兼容。这意味着你在网上找到的大部分 W25Q 驱动代码改一下 Device ID 判断就能直接用。但兼容归兼容QSPI 模式下的时序细节还是有差异后面会专门讲。1.3 硬件连接里最容易忽略的细节先看引脚。GD25Q80E 标准封装是 SOP-8 或者 USON-8八个脚分别是 CS、CLK、DIIO0、DOIO1、WPIO2、HOLDIO3、VCC、GND。普通 SPI 模式只用前四个QSPI 模式六根全用上。这里第一个坑就来了WP 和 HOLD 引脚在 QSPI 模式下会变成 IO2 和 IO3如果你硬件上把它们直接拉高或者拉低QSPI 模式就废了。正确的做法是这两个脚通过 10K 电阻上拉到 VCC或者直接由 STM32 的 QSPI 外设控制。第二个坑是 CS 引脚的处理。很多人在普通 SPI 下用软件控制 CS切到 QSPI 硬件模式后忘了改配置结果 CS 时序不对读出来全是乱码。STM32 的 QSPI 外设可以自动管理 CS但需要你在初始化时把 CS 对应的 GPIO 配置成复用功能而不是普通推挽输出。第三个坑是电源去耦。Flash 在擦除和编程瞬间电流会突然增大如果 VCC 上的电容不够电压跌落会导致操作失败。我的经验是在 Flash 的 VCC 脚旁边放一个 100nF 加一个 1uF 的电容越近越好。别小看这个我遇到过好几次擦除失败就是因为去耦电容离得太远。2. 命令时序数据手册里没明说的那些事2.1 从 0x03 到 0xEB读命令的进化逻辑GD25Q80E 支持好几种读命令最基础的是 0x03Read Data。这个命令的时序是拉低 CS发送 0x03然后发 24 位地址接着数据就在 CLK 的下降沿一个一个移出来。注意是单线输出每个时钟周期出一个 bit。假设你的 SPI 时钟是 50MHz那理论最大读取速度就是 50Mbps 除以 8约 6.25MB/s。听起来还行但实际用起来你会发现 STM32 的 SPI 外设很难稳定跑到 50MHz通常 20-30MHz 就差不多了速度直接砍半。于是有了 0x0BFast Read它在地址后面加了一个 Dummy Byte空周期让 Flash 有时间准备数据这样时钟可以跑得更快。但数据线还是一根瓶颈没解决。真正的转折是 0x3BDual Output Fast Read和 0x6BDual I/O Fast Read。0x3B 让 IO0 和 IO1 同时输出数据速度翻倍。0x6B 更进一步地址阶段就用两根线传输数据阶段也是两根线。到了 0xBBQuad I/O Fast Read四根线全用上地址和数据都走四线理论速度是单线的四倍。而 0xEBQuad I/O Fast Read with 4-Byte Address是 GD25Q80E 在 QSPI 模式下最常用的命令。它和 0xBB 的区别在于地址长度——0xEB 支持 4 字节地址虽然 GD25Q80E 只有 1MB 空间用不到 4 字节地址但很多 QSPI 控制器默认就发 4 字节地址所以 0xEB 反而更通用。命令模式地址线数据线Dummy 周期适用场景0x03单线110低速调试0x0B单线118中速读取0x3B双线128双线加速0x6B双线228双线全加速0xBB四线4410QSPI 高速0xEB四线446QSPI 最常用这个表里的 Dummy 周期数非常关键。不同厂商、不同型号的 FlashDummy 周期可能不一样。GD25Q80E 在 0xEB 命令下数据手册写的是 6 个 Dummy 周期Mode 0 时但如果你开了 Continuous Read 模式Dummy 周期会变。STM32 的 QSPI 外设里有个 DummyCycles 参数设错了就读不出正确数据。我一开始就是照着 W25Q 的 8 个周期设的结果读出来全是 0xFF改成 6 之后立马正常。2.2 写使能、擦除、编程的完整链路NOR Flash 的写入流程比读取复杂得多因为它不能直接覆盖写。完整流程是这样的第一步发送 0x06Write Enable把芯片内部的 WELWrite Enable Latch位置 1。这个操作必须在每次擦除或编程之前执行因为 WEL 在操作完成后会自动清零。第二步发送擦除命令。GD25Q80E 支持三种擦除粒度0x20Sector Erase4KB、0xD8Block Erase64KB、0xC7 或 0x60Chip Erase整片。擦除命令发出后芯片内部开始工作这时候你不能发其他命令只能轮询状态寄存器。第三步轮询状态寄存器。发送 0x05Read Status Register读回来的字节里 bit0 是 BUSY 位。如果 BUSY 是 1说明芯片还在擦除你得继续等。4KB 扇区擦除典型时间是 45ms最坏情况可能到 400ms。这个等待时间在代码里必须处理好不能死等最好加个超时机制。第四步擦除完成后再次发送 0x06Write Enable然后发送 0x02Page Program。页编程一次最多写 256 字节地址必须对齐到页边界。如果你要写 300 字节得分成两次第一次写 256 字节到页起始地址第二次写剩下的 44 字节到下一页起始地址。第五步再次轮询 BUSY 位等待编程完成。页编程典型时间 0.7ms最坏 5ms。提示擦除和编程期间Flash 会忽略除读状态寄存器以外的所有命令。如果你在擦除过程中发了读命令读回来的数据是无效的。这一点在调试时特别容易迷惑人。2.3 状态寄存器里的隐藏信息状态寄存器不止 BUSY 位有用。GD25Q80E 的状态寄存器 bit1 是 WELbit2 到 bit6 是 BP0-BP4Block Protectbit7 是 SRPStatus Register Protect。BP 位用来设置写保护区域如果你不小心把某个块保护了往那个块写数据就会失败而且不会报错只是写不进去。我踩过的一个坑某次调试时发现往某个地址写数据总是失败读回来还是 0xFF。查了半天以为是时序问题最后发现是之前某次操作不小心把 BP 位设上了。所以每次初始化 Flash 之后最好先读一下状态寄存器确认 BP 位都是 0SRP 位也是 0。还有一个容易忽略的是 QE 位Quad Enable。GD25Q80E 的 QE 位在状态寄存器 bit9但状态寄存器只有 8 位所以需要读两个字节。QE 位必须置 1QSPI 模式才能正常工作。很多人在普通 SPI 下能读能写一切到 QSPI 就废了就是因为忘了开 QE 位。开 QE 的方法是发送 0x01Write Status Register把 bit9 写 1。注意这个命令需要发两个字节的数据第一个字节对应 bit0-bit7第二个字节对应 bit8-bit15。3. STM32 QSPI 外设配置的实战细节3.1 时钟树和引脚复用的配置顺序STM32 的 QSPI 外设挂载在 AHB3 总线上配置之前必须先开时钟。以 STM32F7 系列为例RCC 里要开 QUADSPI 的时钟同时对应的 GPIO 端口时钟也要开。引脚通常是 PB2CLK、PB6CS、PD11IO0、PD12IO1、PD13IO2、PD14IO3但不同型号可能不一样一定要查数据手册的 Alternate Function 映射表。GPIO 配置的关键点模式要设成 AF复用功能输出类型推挽速度设成 Very High上下拉根据实际情况选。CLK 和 CS 建议上拉IO0-IO3 建议上拉。为什么因为 Flash 在 CS 拉高时处于待机状态如果 IO 线悬空可能会有微弱电流导致误触发。上拉电阻给一个确定的高电平芯片更稳定。配置顺序很重要先开时钟再配 GPIO再配 QSPI 外设最后使能 QSPI。我见过有人先使能 QSPI 再配 GPIO结果外设初始化失败因为时钟还没稳定。3.2 QSPI 初始化结构体里每个参数的含义STM32 HAL 库的 QSPI 初始化结构体QSPI_InitTypeDef里有几个关键字段ClockPrescaler决定 QSPI 时钟的分频系数。QSPI 时钟源通常是 AHB 总线时钟比如 216MHz。分频系数设 4QSPI 时钟就是 54MHz。GD25Q80E 在 QSPI 模式下最高支持 104MHz读但实际能跑多快取决于你的 PCB 走线和 Flash 的时序参数。我的经验是从低速开始调先设分频 8 或 16确保能读到正确 ID再逐步提高。FifoThreshold是 FIFO 触发阈值一般设 1 或 4。这个参数影响中断和 DMA 的触发时机如果不用中断和 DMA设多少都行。SampleShifting是采样偏移通常设QSPI_SAMPLE_SHIFTING_NONE。但如果你的时钟跑得比较高信号完整性不好可能需要设成QSPI_SAMPLE_SHIFTING_HALFCYCLE让采样点偏移半个周期。FlashSize是 Flash 容量对应的地址位数。GD25Q80E 是 8Mbit对应 2 的 20 次方所以设QSPI_FLASH_SIZE_20或者直接填 20。这个参数决定了 QSPI 控制器发多少位地址。CSHighTime是 CS 拉高后的保持时间单位是时钟周期。一般设 1 到 3 个周期就够了。ChipSelectHighTime和ClockMode也要注意。ClockMode设QSPI_CLOCK_MODE_0对应 CPOL0CPHA0。GD25Q80E 支持 Mode 0 和 Mode 3但 Mode 0 更常用。3.3 命令配置结构体QSPI_CommandTypeDef 的拆解HAL 库把每个 QSPI 操作抽象成一个命令结构体这个设计很灵活但也很容易配错。以读操作为例QSPI_CommandTypeDef sCommand; sCommand.Instruction 0xEB; // Quad I/O Fast Read sCommand.AddressMode QSPI_ADDRESS_4_LINES; sCommand.AddressSize QSPI_ADDRESS_24_BITS; sCommand.Address 0x000000; sCommand.AlternateByteMode QSPI_ALTERNATE_BYTES_4_LINES; sCommand.AlternateBytes 0x00; // Mode 0 sCommand.AlternateBytesSize QSPI_ALTERNATE_BYTES_8_BITS; sCommand.DummyCycles 6; // GD25Q80E 关键参数 sCommand.DataMode QSPI_DATA_4_LINES; sCommand.NbData 256; sCommand.DdrMode QSPI_DDR_MODE_DISABLE; sCommand.SIOOMode QSPI_SIOO_INST_EVERY_CMD;这里有几个坑点。AlternateByteMode和AlternateBytes对应的是 Mode Bit。GD25Q80E 在 0xEB 命令下地址后面会跟一个 Mode Byte通常是 0x00 或者 0xA0。如果你不设这个Dummy 周期数就要相应调整。我建议是设上 Mode Byte然后 DummyCycles 设 6这样最稳。SIOOMode设QSPI_SIOO_INST_EVERY_CMD表示每条命令都发指令字节。如果设成QSPI_SIOO_INST_ONLY_FIRST_CMD后续命令会省略指令字节速度更快但只适用于连续读同一地址区域的场景。DdrMode一般关掉GD25Q80E 不支持 DDR。3.4 内存映射模式让 Flash 像内部 RAM 一样访问QSPI 最爽的功能是内存映射模式Memory Mapped Mode。配置好之后Flash 的地址空间会映射到 STM32 的 0x90000000 起始的区域你可以直接用指针读数据不需要每次发命令。配置内存映射模式需要额外设置QSPI_MemoryMappedTypeDefQSPI_MemoryMappedTypeDef sMemMappedCfg; sMemMappedCfg.TimeOutActivation QSPI_TIMEOUT_COUNTER_DISABLE; sMemMappedCfg.TimeOutPeriod 0;然后调用HAL_QSPI_MemoryMapped()。之后你就可以这样读数据uint8_t *flashPtr (uint8_t *)0x90000000; uint8_t data flashPtr[0x1000]; // 直接读 Flash 偏移 0x1000 的数据但内存映射模式有个限制只能读不能写。写操作还是要切回间接模式发擦除和编程命令。所以实际项目中通常是读用内存映射写用间接模式。还有一个坑内存映射模式下如果访问了 Flash 不存在的地址区域可能会触发 HardFault。所以地址范围一定要算清楚GD25Q80E 是 1MB映射区域就是 0x90000000 到 0x900FFFFF。4. 从零跑通GD25Q80E 在 STM32 上的完整驱动流程4.1 上电初始化的标准步骤上电之后不要急着发读命令先做几件事第一延时至少 1ms等 Flash 内部电源稳定。GD25Q80E 的上电复位时间典型值是 1ms保险起见等 5ms。第二读 Device ID 确认通信正常。发送 0x9FRead Identification会返回三个字节Manufacturer ID、Memory Type、Capacity。GD25Q80E 的 Manufacturer ID 是 0xC8Memory Type 是 0x40Capacity 是 0x14表示 8Mbit。如果读回来是 0xFF 或者 0x00说明硬件连接有问题。第三读状态寄存器确认 BUSY 位是 0BP 位都是 0。如果 BP 位不是 0发送 0x01 命令清除。第四发送 0x06 写使能然后发送 0x01 写状态寄存器把 QE 位置 1。注意要发两个字节第一个字节是 0x00第二个字节是 0x02bit9 对应第二个字节的 bit1。第五再次读状态寄存器确认 QE 位已经置 1。第六配置 QSPI 外设进入 QSPI 模式开始用 0xEB 命令读数据。这个流程看起来简单但每一步都可能出问题。我遇到最多的是第二步读 ID 失败原因通常是 CS 引脚没拉低或者 SPI 模式设错了CPOL/CPHA 不对。4.2 擦除和写入的代码实现擦除一个扇区的完整代码void Flash_SectorErase(uint32_t addr) { QSPI_CommandTypeDef cmd; cmd.Instruction 0x06; // Write Enable 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.DdrMode QSPI_DDR_MODE_DISABLE; cmd.SIOOMode QSPI_SIOO_INST_EVERY_CMD; HAL_QSPI_Command(hqspi, cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE); cmd.Instruction 0x20; // Sector Erase cmd.AddressMode QSPI_ADDRESS_1_LINE; cmd.AddressSize QSPI_ADDRESS_24_BITS; cmd.Address addr; HAL_QSPI_Command(hqspi, cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE); // 等待 BUSY 位清零 Flash_WaitForReady(); }Flash_WaitForReady()函数轮询状态寄存器void Flash_WaitForReady(void) { QSPI_CommandTypeDef cmd; uint8_t status 0; cmd.Instruction 0x05; // Read Status Register cmd.InstructionMode QSPI_INSTRUCTION_1_LINE; cmd.AddressMode QSPI_ADDRESS_NONE; cmd.AlternateByteMode QSPI_ALTERNATE_BYTES_NONE; cmd.DataMode QSPI_DATA_1_LINE; cmd.NbData 1; cmd.DummyCycles 0; cmd.DdrMode QSPI_DDR_MODE_DISABLE; cmd.SIOOMode QSPI_SIOO_INST_EVERY_CMD; do { HAL_QSPI_Command(hqspi, cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE); HAL_QSPI_Receive(hqspi, status, HAL_QPSI_TIMEOUT_DEFAULT_VALUE); } while (status 0x01); }页编程的代码类似只是命令换成 0x02并且要发数据void Flash_PageProgram(uint32_t addr, uint8_t *data, uint16_t len) { // 先发写使能 // 再发 0x02 命令 // 然后 HAL_QSPI_Transmit 发数据 // 最后等待 BUSY 清零 }注意页编程的地址必须对齐到 256 字节边界。如果 addr 是 0x100len 是 300那第一次只能写 256 字节到 0x100-0x1FF剩下的 44 字节要写到 0x200 开始的下一页。如果你一次性发 300 字节Flash 会在页边界处回卷覆盖掉页开头的数据。4.3 读取速度的实测对比我用 STM32F767 的 QSPI 外设时钟设到 108MHz分别测试了几种读命令的实际速度命令模式实测读取 1MB 耗时等效速度0x03单线约 320ms3.1MB/s0x0B单线约 180ms5.6MB/s0x6B双线约 95ms10.5MB/s0xEB四线约 42ms23.8MB/s这个数据是在理想条件下测的实际项目中因为要穿插擦除和写入平均速度会低一些。但四线模式比单线快了将近 8 倍这个提升非常明显。如果你做的是音频播放或者图像显示QSPI 模式是必须的。4.4 常见故障的排查链路读出来全是 0xFF这是最常见的故障。排查顺序第一步用万用表量 Flash 的 VCC 是不是 3.3VCS 是不是在空闲时是高电平。如果 CS 一直是低说明 GPIO 配置错了。第二步用示波器看 CLK 有没有波形。如果没有说明 QSPI 外设没使能或者时钟没开。第三步读 Device ID。如果 ID 读不到说明 SPI 基本通信都有问题检查 CPOL/CPHA 设置。第四步如果 ID 能读到但读数据是 0xFF检查 QE 位有没有置 1DummyCycles 设对了没有。第五步如果 QSPI 模式读不到但单线模式能读到检查 IO2 和 IO3 的 GPIO 配置确认它们被正确配置成了 QSPI 复用功能而不是普通 GPIO。写进去读出来对不上排查顺序第一步确认擦除成功了。擦除后先读一下看是不是全 0xFF。第二步确认写使能发了。每次擦除和编程前都要发 0x06漏了就会写失败。第三步确认地址对齐了。页编程不能跨页。第四步确认等待时间够了。擦除和编程后要轮询 BUSY 位不能直接发下一条命令。5. 进阶玩法OTA 升级和文件系统5.1 用 GD25Q80E 做 OTA 备份区OTA 升级的基本思路是当前运行的固件在内部 Flash新固件先下载到外部 Flash 的备份区校验通过后再搬运到内部 Flash。GD25Q80E 的 1MB 空间可以存两个 256KB 的固件备份还剩下 512KB 存其他数据。具体实现时把外部 Flash 分成几个区域0x00000-0x3FFFF 存固件 A0x40000-0x7FFFF 存固件 B0x80000-0xFFFFF 存日志和配置。每次升级时交替写入 A 和 B 区这样即使升级过程中断电至少还有一个完整固件可用。搬运固件的时候要注意不能直接从外部 Flash 读到内部 Flash因为内部 Flash 写之前也要擦除。正确做法是先读到 RAM 缓冲区擦除内部 Flash 对应扇区再从 RAM 写进去。STM32F7 有 512KB RAM可以一次读 4KB 到缓冲区擦 4KB写 4KB循环操作。5.2 简易文件系统的设计思路如果你不想上 FatFS 这种重型文件系统可以自己设计一个简单的键值存储。思路是把 Flash 分成固定大小的记录块每条记录包含键比如 16 字节、值比如 240 字节、时间戳4 字节、CRC 校验4 字节总共 264 字节对齐到 256 字节的页。写入时先找空白页擦除后写入。读取时遍历所有页找到匹配的键。删除时把对应页标记为无效。空间不够时做垃圾回收把有效记录搬到新区域擦除旧区域。这个方案简单可靠适合存储配置参数、传感器校准数据这类小数据。缺点是查找是线性的如果记录多了会慢。优化方法是加一个索引页记录每个键所在的页号。5.3 磨损均衡的实用策略NOR Flash 的 10 万次擦写寿命听起来很多但如果你的设备每秒写一次日志同一个扇区不到 28 小时就写废了。磨损均衡的核心思想是让写入分散到不同扇区。最简单的策略是地址轮转维护一个写指针每次写新数据时指针后移一个扇区写到头了再回到开头。这样每个扇区的擦写次数大致均匀。更复杂一点的是动态磨损均衡记录每个扇区的擦写次数优先写次数少的扇区。实际项目中我会把频繁写的区域比如日志单独划出来用轮转策略不频繁写的区域比如配置用固定地址但每次修改时也做一次轮转。这样既保证了寿命又不会让代码太复杂。6. 那些数据手册不会告诉你的经验6.1 温度对时序的影响GD25Q80E 的工业级版本支持 -40°C 到 85°C但不同温度下的时序参数是有差异的。数据手册里的时序表通常标注的是常温 25°C 下的值。在低温下Flash 的读取速度会变慢如果你在常温下调通了 104MHz到了 -40°C 可能就读不出数据了。我的做法是在高低温环境下都做测试如果低温下不稳定就把 QSPI 时钟降一档。另外DummyCycles 也可以适当增加给 Flash 更多准备时间。比如常温下用 6 个周期低温下改成 8 个。6.2 PCB 走线对 QSPI 信号完整性的影响QSPI 跑高速的时候PCB 走线就很重要了。CLK 和 IO0-IO3 这五根线尽量等长误差控制在 5mm 以内。如果走线太长或者不等长数据眼图会变差读出来就是随机错误。还有一个细节CLK 线最好包地或者在两侧走地线。如果 CLK 和 IO 线靠得太近串扰会导致数据错误。我遇到过一块板子QSPI 跑 80MHz 以上就出错降到 60MHz 就正常后来发现是 CLK 和 IO0 并排走了 3cm改成隔开走线后 100MHz 都没问题。6.3 批量生产时的注意事项小批量调试的时候你可能用 J-Link 或者 ST-Link 直接烧录。但批量生产时通常是用离线烧录器先把固件写到外部 Flash再贴片。这时候要注意烧录器的时序配置和你的驱动是否一致。另外批量生产时建议加一个 Flash 自检流程上电后读 Device ID读状态寄存器擦除一个测试扇区写入测试数据读回来比对。全部通过才进入正常工作模式。这个自检能筛掉焊接不良或者芯片损坏的板子。6.4 调试工具的选择调试 QSPI 最有用的工具是逻辑分析仪。Saleae 或者类似的 8 通道逻辑分析仪抓一下 CS、CLK、IO0-IO3 的波形一眼就能看出问题。比如 CS 拉低后 CLK 没有立即开始或者 Dummy 周期数不对波形上清清楚楚。如果没有逻辑分析仪可以用 STM32 的 QSPI 错误中断。HAL 库提供了HAL_QSPI_ErrorCallback在里面打印错误码能帮你定位是超时还是配置错误。软件层面我习惯在驱动里加一个调试函数把 Device ID、状态寄存器、QE 位状态都打印出来。每次初始化后调用一次确认所有参数都正确。这个习惯帮我省了很多排查时间。6.5 关于 GD25Q80E 的替代型号GD25Q80E 缺货的时候可以考虑 W25Q80、MX25L80、IS25LP80 等型号。这些芯片的命令集基本兼容但细节有差异。比如 W25Q80 的 0xEB 命令 DummyCycles 是 8GD25Q80E 是 6。替换的时候一定要改这个参数否则读出来就是错的。还有一个差异是 QE 位的位置。大部分芯片的 QE 位在状态寄存器的 bit9但有些型号在 bit1 或者 bit6。替换前一定要查数据手册确认。我在实际项目中养成了一个习惯把 Flash 的型号、ID、关键参数DummyCycles、QE 位位置、页大小、扇区大小做成一个配置表换型号时只改配置表驱动代码不动。这样替换芯片的时候改几行配置就能跑起来不用重新调试整个驱动。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价