一、基本概况
- 型号:W25Q128JV(常见版本)
- 厂家:华邦(Winbond)
- 存储容量:128 Mbit = 16 MB
- 接口类型:SPI(支持标准 SPI、Dual SPI、Quad SPI,甚至 Octal SPI)
- 工作电压:2.7V ~ 3.6V(常见 3.3V)
- 封装:SOIC-8、WSON-8、BGA 等
这颗 Flash 在 STM32、ESP32、FPGA、单片机外部存储扩展 中非常常见,常用于:
- 存放程序(BootLoader、固件更新)
- 存放图片、字体、资源(UI、音频)
- 数据记录(黑匣子、日志)
二、存储结构
W25Q128 的存储组织方式(层级关系):
- 容量:128 Mbit(16 MB)
- 扇区(Sector):4096 字节 (4 KB)
- 块(Block):64 KB = 16 个扇区
- 页(Page):256 字节
- 总页数:16 MB ÷ 256B = 65536 页
📌 访问和擦除的最小单位:
- 写入最小单位:1 字节(但一次操作最大写 256 字节 / 页写入)
- 擦除最小单位:4 KB(Sector Erase)
三、SPI 通信
常用引脚
- CS#:片选(低有效)
- CLK:时钟
- MOSI:主机输出数据
- MISO:主机输入数据 (在 Quad SPI 模式下会扩展到 4 条数据线 IO0~IO3)
常用指令(Opcode)
- 0x06:写使能(Write Enable)
- 0x04:写禁止(Write Disable)
- 0x05:读状态寄存器 1
- 0x02:页编程(Page Program,最多 256B)
- 0x20:扇区擦除(4 KB)
- 0xD8:块擦除(64 KB)
- 0xC7/0x60:整片擦除
- 0x03:读取数据(低速)
- 0x0B:快速读取(Fast Read)
- 0x9F:读取 JEDEC ID(常用于设备识别,W25Q128 → 0xEF 0x40 0x18)
四、工作流程举例
1. 读 JEDEC ID
- CS 拉低
- 发送
0x9F - 返回 3 字节 ID →
EF 40 18
2. 写数据(页编程)
- 发送
0x06(写使能) - 发送
0x02(Page Program)+ 24 位地址 - 连续写入最多 256 字节
- 等待 BUSY 清零(状态寄存器 bit0 = 0)
3. 擦除数据
- 擦除前必须 写使能
- 发送
0x20(扇区擦除,4 KB),地址指定扇区起始地址 - 等待擦除完成(几 ms)
五、优点 & 特点
- 容量大(16 MB)
- 速度高(SPI 时钟可达 104 MHz,Quad SPI 更快)
- 低功耗(待机电流 < 1 μA)
- 支持掉电指令(节能)
- 使用广泛,资料丰富,成本低
六、容量对比
| 参数 | W25Q64 | W25Q128 |
|---|---|---|
| 容量 | xxxxxxxxxx #include "stm32f10x.h"#include "MyI2C.h"#include "AT24C02.h"#include "usart.h"#include <stdio.h>#include "Delay.h"int main(void){ USART2_Init(115200); MyI2C_Init(); uint16_t i; uint8_t data[256]; for (i = 0; i < 256; i++) { AT24C02_WriteByte(i, i); Delay_ms(5); } AT24C02_ReadBuffer(0, data, 256); for (int i = 0; i < 256; i++) { printf("Data[%d] = %d\n", i, data[i]); }}c | 128 Mbit = 16 MB |
| 页 (Page) | 256 B | 256 B |
| 页数 | 32768 页 | 65536 页 |
| 扇区 (Sector) | 4 KB | 4 KB |
| 扇区数 | 2048 | 4096 |
| 块 (Block) | 64 KB | 64 KB |
| 块数 | 128 | 256 |
七、页 (Page)、扇区 (Sector)、块 (Block) 的概念和层级顺序
一页有256个字节,一个扇区有16页(16*256Byte=4096Byte)
所以第0页的地址是0-255,第一页的地址是256-511
扇区的地址怎么计算?
因为十进制4096=十六进制0x1000,所以下一个扇区的起始地址 = 当前地址 +
0x1000第0个扇区的地址是0x000000
存储组织层次
SPI Flash 的存储是分级组织的(小 → 大):
字节 (Byte) → 页 (Page) → 扇区 (Sector) → 块 (Block) → 整片 (Chip)可以类比成一本书:
- 字节 = 一个字
- 页 = 一页纸
- 扇区 = 一小章(几页组成)
- 块 = 一大章(很多小章组成)
- 整片 = 整本书
具体定义(以 W25Q128 为例)
- 页 (Page)
- 大小:256 字节
- 最小的编程(写入)单位
- 一次最多写满一页,写超过会回绕到页头覆盖
- 扇区 (Sector)
- 大小:4 KB = 16 页
- 最小的擦除单位(擦除会清零,变成全
0xFF) - 写之前必须先擦除所在的扇区
- 块 (Block)
- 大小:64 KB = 16 扇区
- 可以整块擦除,速度比逐个扇区快
- 整片 (Chip Erase)
- 擦除整个 Flash(全部清零为
0xFF)
- 擦除整个 Flash(全部清零为
八、stm32测试代码
传地址的注意事项
c
W25Q128_PageProgram(256, data, 256); // 十进制
W25Q128_PageProgram(0x100, data, 256); // 十六进制
W25Q128_PageProgram(0b100000000, data, 256); // 二进制(C语言可能不支持直接写)c
#include "stm32f10x.h"
#include "MyI2C.h"
#include "AT24C02.h"
#include "usart.h" // 用于打印结果,可自行实现
#include <stdio.h>
#include "Delay.h"
#include "MySPI.h"
#include "W25Q128.h"
uint8_t wdata[256];
uint8_t rdata[256];
int main(void)
{
USART2_Init(921600);
W25Q128_Init();
uint16_t i,j;
// 擦除前两个扇区(共 8KB,足够 32 页)0-31页面
W25Q128_SectorErase(0x000000); // 4KB
W25Q128_SectorErase(0x001000); // 另 4KB
Delay_ms(10);
// 写入前 32 页(每页 256 字节,共 8KB)
printf("Writing 32 pages...\r\n");
for (i = 0; i < 32; i++) {
uint32_t addr = i * 256;
// 构造数据:第 i 页全填 i
for (j = 0; j < 256; j++) {
wdata[j] = i;
}
W25Q128_PageProgram(addr, wdata, 256);
printf("Page %d written at 0x%06X\r\n", i, addr);
}
// 读取验证
printf("Reading back...\r\n");
for (i = 0; i < 32; i++) {
uint32_t addr = i * 256;
W25Q128_ReadData(addr, rdata, 256);
for (j = 0; j < 256; j++) {
// if (rdata[j] != i) {
// printf("ERROR: Page=%d, Offset=%d, Expected=%d, Got=%d\r\n", i, j, i, rdata[j]);
// }
printf("Page=%d, Offset=%d, Expected=%d, Got=%d\r\n", i, j, i, rdata[j]);
}
}
}