我的STM32学习专栏[点击直达 | 持续更新中]本文基于江科大 STM32 入门教程整理面向零基础小白讲清楚 W25Q64 这款 SPI Flash 芯片。一、W25Q64 是什么先搞清楚它的身份1.1 芯片简介咱们在 STM32 开发中经常需要存储一些掉电不丢失的数据比如用户配置参数字库数据显示汉字用固件升级包传感器的历史记录这时候 EEPROM 容量太小比如 AT24C02 只有 256 字节SD 卡又太复杂W25Q64就是一个非常合适的中间方案。W25Q64是一款容量64 Mbit 8 MByte注意单位换算1 Byte 8 bit类型Nor Flash闪存的一种接口SPI 通信特点低成本、小型化、使用简单我的理解你可以把它想象成一个超级大的 EEPROM但是它和 EEPROM 有个本质区别——Flash 写入前必须先擦除而且擦除是按块来的这个后面会详细讲也是新手最容易踩坑的地方。1.2 关键参数速览参数数值说明存储容量8 MB64 Mbit ÷ 8 8 MByte存储介质Nor Flash非易失性掉电数据不丢失时钟频率80 / 160 / 320 MHz支持标准/快速/高速模式地址位数24 位最大寻址 224 16 MB但本芯片只有 8 MB所以实际用到0x000000 ~ 0x7FFFFF小知识为什么 24 位地址最大能到 16 MB但 W25Q64 只有 8 MB 呢因为厂商用同一套地址架构兼容不同容量的芯片比如 W25Q128 就是 16 MBW25Q64 只用到地址的最高位到0x7F即0x7FFFFF。二、硬件电路与引脚功能2.1 硬件电路图下图是江科大课程中的 W25Q64 硬件电路原理图W25Q64 硬件电路与引脚功能表从图中可以看到这是模块对外的接口引出 6 个引脚CS、DO、GND、CLK、DI、VCC2.2 引脚功能详解引脚别名类型功能说明VCC—电源供电 2.7V ~ 3.6V接 3.3V 即可GND—电源地CSSS输入SPI 片选低电平有效。拉低选中芯片拉高释放芯片CLKSCK输入SPI 时钟由主机STM32产生DIMOSI输入主机输出从机输入。STM32 通过这根线发数据给 W25Q64DOMISO输出主机输入从机输出。W25Q64 通过这根线发数据给 STM32WPIO₂输入写保护。低电平有效硬件层面防止误写入HOLDIO₃输入数据保持。低电平有效暂停当前操作我的理解CS片选就像教室点名老师叫到你名字CS 拉低你才能说话没叫到你CS 拉高你就乖乖坐着别吭声。DI 和 DO 的方向一定要搞清楚DI 是 W25Q64 的输入Data In对应 STM32 的 MOSIDO 是 W25Q64 的输出Data Out对应 STM32 的 MISO。很多新手容易搞反⚠️注意WP 和 HOLD 引脚在图中通过电阻上拉到 VCC平时处于无效状态高电平这样不会影响正常读写。如果你想用硬件写保护功能可以把 WP 接到 STM32 的 GPIO 上控制。三、W25Q64 内部结构——地址是怎么划分的3.1 内部框图下图是 W25Q64 的内部结构框图W25Q64 内部框图3.2 存储空间的层级划分从框图右边可以看到W25Q64 的 8 MB 存储空间是按三级结构划分的8 MB 总容量 └── Block块64 KB / 块共 128 个块 └── Sector扇区4 KB / 扇区每个块含 16 个扇区 └── Page页256 Byte / 页每个扇区含 16 页层级大小数量说明Block块64 KB128 个擦除操作的最小单位之一Sector扇区4 KB2048 个最常用的擦除单位Page页256 Byte32768 个写入操作的基本单位我的理解这个划分方式就像一本书——Block相当于章一大块内容Sector相当于节比章小一点Page相当于页最小的写入单位重要从框图可以看到芯片内部有一个256-Byte Page Buffer页缓存区。当你写入数据时数据会先进入这个缓存区然后再写入 Flash。这也是为什么一次最多只能写一页256 字节的原因3.3 地址范围W25Q64 的地址从0x000000到0x7FFFFF共 8 MBBlock 编号地址范围大小Block 00x000000~0x00FFFF64 KBBlock 10x010000~0x01FFFF64 KB.........Block 1270x7F0000~0x7FFFFF64 KB四、Flash 操作注意事项——这是最容易踩坑的地方⚠️ 这一节是重中之重很多新手写 W25Q64 驱动时出问题90% 都是因为没搞懂 Flash 的操作特性。4.1 写入操作Write的五大铁律铁律一写入前必须先写使能W25Q64 默认是只读状态任何写入/擦除操作前都必须先发送Write Enable写使能指令0x06。我的理解就像银行的保险箱平时是锁着的。你要存东西进去得先跟管理员说我要开锁写使能然后才能操作。操作完了芯片会自动上锁写禁止。铁律二只能把 1 改成 0不能把 0 改成 1这是 Flash 存储的物理特性决定的操作是否允许示例1 → 0✅ 允许0xFF→0x000 → 1❌ 不允许0x00→0xFF做不到我的理解你可以把 Flash 的每个位想象成一个开关但这是个单向开关——只能往下按1→0不能自动弹起来0→1。要把 0 变回 1必须靠擦除操作。铁律三写入前必须先擦除擦除后所有位变成 10xFF既然不能直接写 0→1那如果想在一个已经写过数据的地址上写新数据怎么办答案先擦除擦除操作会把目标区域的所有位都置为 1也就是变成0xFF。我的理解擦除就像是把黑板上的字全部擦干净变成一片空白全是 1。然后你才能在空白处写新的内容把某些 1 改成 0。铁律四擦除必须按最小擦除单元进行W25Q64 支持三种擦除粒度擦除指令擦除单位指令码说明Sector Erase4 KB一个扇区0x20最常用粒度最小Block Erase (32KB)32 KB0x52较少用Block Erase (64KB)64 KB一个块0xD8擦除速度快Chip Erase整片 8 MB0xC7/0x60全部清空很慢⚠️注意不能按字节擦除最小擦除单位是 4 KB 的扇区。如果你只想改 1 个字节也得把所在的整个 4 KB 扇区擦除我的理解这就像你住宿舍擦除不是擦你自己的桌子而是把整个宿舍扇区打扫一遍。如果你只想改一点点东西得先把整个扇区备份 → 擦除 → 修改 → 写回。铁律五连续写入最多一页256 字节超过会卷回覆盖W25Q64 内部有一个 256 字节的页缓存区。一次页编程Page Program最多只能写 256 字节。如果你连续写入超过 256 字节会发生什么数据会从当前页的起始地址重新开始覆盖而不是写到下一页我的理解想象页缓存是一个 256 格的环形传送带你不停地放东西放到第 257 个时它会回到第 1 格把原来的东西顶掉。所以写大数据时一定要分页写入铁律六写入结束后芯片进入忙状态写入或擦除操作完成后W25Q64 需要一段时间把数据真正写入 Flash 内部。这段时间内芯片处于Busy忙状态不响应新的读写操作必须等待 Busy 结束才能进行下一次操作怎么判断忙完了可以通过读取Status Register 1的 BUSY 位Bit 0BUSY 1正在忙请等待BUSY 0空闲可以操作4.2 读取操作Read——简单粗暴没那么多规矩相比写入读取就简单多了特性说明是否需要写使能❌ 不需要是否有页限制❌ 没有可以连续读取任意长度是否进入忙状态❌ 读取结束后不会进入忙状态唯一限制⚠️不能在芯片忙状态时读取我的理解读取就像看书随时可以看不需要跟芯片打招呼。但如果芯片正在写字忙状态你就不能打扰它得等它写完再看。五、常用指令集速查表W25Q64 通过 SPI 发送指令码来控制芯片。以下是开发中最常用的指令指令名称指令码功能说明Write Enable0x06写使能允许后续的写入/擦除Write Disable0x04写禁止Read Status Register-10x05读取状态寄存器1包含 BUSY 位Read Data0x03读取数据标准速度Page Program0x02页编程写入最大 256 字节Sector Erase0x20扇区擦除4 KBBlock Erase (64KB)0xD8块擦除64 KBChip Erase0xC7/0x60整片擦除Read JEDEC ID0x9F读取芯片 ID用于验证通信Power-down0xB9进入低功耗模式Release Power-down0xAB退出低功耗模式小技巧上电后先发送0x9F读取 JEDEC IDW25Q64 应该返回0xEF4017如果能正确读到说明 SPI 通信没问题这是调试时的第一步六、个人理解 / 心得这里是我自己在学习 W25Q64 过程中的一些心得体会记录下来方便以后回顾Flash 的先擦后写特性是最反直觉的地方。刚开始学的时候我直接往一个已经有数据的地址写新数据结果读出来全是乱的。后来才理解Flash 不是 RAM不能随意覆盖页写入的卷回问题也很坑。我第一次写字符串超过 256 字节时发现前面的数据被后面的覆盖了查了半天才发现是页边界的问题。现在写大数据时我一定会先算好分页。忙状态等待不能省有些教程里为了简化代码把等待 BUSY 的代码删掉了结果数据写入不完整。这个等待是必须的哪怕只有几毫秒。关于WP 和 HOLD 引脚在实际项目中如果不需要硬件写保护直接接 VCC上拉就行不用浪费 GPIO。但如果做的是产品级代码建议把 WP 接到 GPIO 上关键时刻可以硬件保护数据。七、踩坑记录记录我在实际调试 W25Q64 时遇到的真实问题和解决方案问题现象原因解决方案读取 JEDEC ID 返回0x000000SPI 通信失败可能是接线松动或时钟极性/相位配置错误检查接线确认 SPI 模式为Mode 0CPOL0, CPHA0写入数据后读取全是0xFF忘记发送写使能指令在 Page Program 前务必发送0x06程序卡死在等待 BUSY 的循环里可能是 SPI 通信异常导致读不到正确的状态寄存器加入超时机制避免死循环八、总结表格对比项写入操作读取操作是否需要写使能✅ 必须先发送0x06❌ 不需要是否需要先擦除✅ 必须先擦除❌ 不需要最小操作单位1 字节但受页限制1 字节最大连续操作256 字节一页无限制是否进入忙状态✅ 会进入 Busy❌ 不会能否在忙状态时操作❌ 不能❌ 不能擦除类型擦除大小典型耗时Sector Erase4 KB~45 msBlock Erase (32KB)32 KB~120 msBlock Erase (64KB)64 KB~150 msChip Erase8 MB~20 s写这篇文章的时候我把江科大的视频又回顾了一遍真的是常看常新。第一次看的时候光顾着抄代码了很多原理都是一知半解出了问题也不知道怎么排查。这次重新整理笔记把 W25Q64 的硬件电路、内部结构和 Flash 操作特性都梳理了一遍感觉对 SPI Flash 的理解又深了一层。⚠️ 本文内容基于 W25Q64JV 数据手册2018 版及江科大 STM32 入门教程标准库版本适用于 STM32F103C8T6 等 F1 系列芯片。如果你使用的是 HAL 库或其他型号芯片SPI 配置部分可能需要调整。开发环境Keil5 STM32F103C8T6最小系统板 ST-Link参考资料江科大STM32入门教程、《STM32F10xxx参考手册》、W25Q64JV 数据手册前置文章江科大STM32入门SPI通信协议详解——从移位寄存器到时序模式如果本文对你有帮助欢迎点赞 收藏 ⭐ 有任何问题欢迎在评论区交流一起学习进步