块、扇区、页
我们可以用一个**“笔记本”**来完美比喻这个结构:
1. 结构层级(从大到小)
假设 W25Q64 是一本厚厚的笔记本(总共 8MB)。
Block(块)—— 相当于“一章”
- 大小:通常是 64KB。
- 地位:是较大的逻辑单元。W25Q64 总共有 128 个 Block(8MB / 64KB = 128)。
- 操作:支持“整块擦除”(指令
0xD8)。如果你想撕掉一大叠纸,就用这个。
Sector(扇区)—— 相当于“一张纸”
- 大小:固定为 4KB。
- 关系:1 个 Block 里面包含了 16 个 Sector。
- 地位:这是 Flash 擦除的最小单位!(敲黑板重点)
- 在 Flash 里,你不能只擦掉一个字,你必须把整张纸(4KB)上的字全部擦掉(变成 0xFF),才能重新写字。
- 操作:支持“扇区擦除”(指令
0x20)。这是最常用的擦除指令。
Page(页)—— 相当于“一行”
- 大小:固定为 256 Bytes。
- 关系:1 个 Sector 里面包含了 16 个 Page。
- 地位:这是 Flash 写入(编程)的基本单位。
- 虽然你可以一个字节一个字节地写,但硬件内部其实是按页来缓存的。
- 关键限制:一次写入操作(Page Program
0x02)最多只能写 256 字节,而且不能跨行写。
2. 算术关系表 (W25Q64)
| 层级 | 大小 | 数量关系 | 常用操作 |
|---|---|---|---|
| Chip (芯片) | 8MB | 1 个 | Chip Erase (0xC7) - 全片擦除 |
| Block (块) | 64KB | 128 个 | Block Erase (0xD8) - 块擦除 |
| Sector (扇区) | 4KB | 每个 Block 有 16 个 | Sector Erase (0x20)- 扇区擦除 |
| Page (页) | 256 B | 每个 Sector 有 16 个 | Page Program (0x02)- 页编程 |
| Byte (字节) | 1 B | 每个 Page 有 256 个 | Read Data (0x03) - 读取 |
3. 写代码时的两个“大坑”
坑一:必须“先擦后写” (Erase before Write)
Flash 有个物理特性:只能把 1 变成 0,不能把 0 变成 1。
- 擦除 (Erase):把整个扇区(4KB)所有的位都变成 1 (即 0xFF)。
- 写入 (Program):把某些位变成 0 (比如写入 0x55, 即 01010101)。
后果: 如果你没擦除(扇区里有旧数据),直接去写新数据,新旧数据会进行**“与运算”**,导致数据变废。
- 比如原数据是
0xAA(10101010),你想写0x55(01010101)。 - 结果变成:
0xAA & 0x55 = 0x00。 - 解决: 写入前,必须检查该位置是否为
0xFF。如果不是,必须先把整个 Sector(4KB) 读出来缓存,擦除该 Sector,修改缓存里的数据,再把整个 4KB 写回去。
坑二:页写入回绕 (Page Wrap Around)
这是 0x02 指令最容易踩的坑。
规则: 一次 Page Program 指令,只能在同一页(256字节)内操作,不能跨过页的边界。
举例:
- 假设一页的范围是地址
0x0000到0x00FF。 - 你想从
0x00F0开始,写入 20 个字节。 - 理应写到:
0x00F0~0x0103(跨到了下一页)。 - 实际发生: 硬件写到
0x00FF后,指针会跳回本页的开头0x0000继续写! - 结果: 你的数据会覆盖掉本页开头的数据 (
0x0000~0x0003),而下一页0x0100啥也没写进去。
解决: 写代码时必须计算**“页剩余空间”**。如果数据长度超过本页剩余空间,必须要把数据切成两段,分两次发送 0x02 指令。
总结
- Sector (4KB) 是橡皮擦的最小单位(要擦就擦一张纸)。
- Page (256B) 是提笔写字的最大连续单位(一次最多写一行,写到行尾要换行)。