W25Q64模块
简介
W25Q256支持3字节地址,4字节地址读写
而3字节地址读写,只能呢个读写前16MB ,后面的16MB够不着
4字节地址读写,都可以进行读写
2^24/1024=16384kb
16384/1024=16MB
硬件电路
W25Q64的框图
W25Q64 的整个存储器空间采用分层划分方式:
- 先划分若干个块(Block)
- 每个块又划分若干个扇区(Sector)
- 每个扇区内部再分成多个页(Page)
存储器以字节为单位,每个字节都有唯一的地址,地址宽度为 24 位(3 个字节),整个地址空间从 00 00 00 到 7F FF FF
各级划分详情
- 块(Block):整个存储空间以 64KB 为一个基本单元划分块。W25Q64 总容量为 8MB(8192KB),共分为 128 个块(0-127)。
- 扇区(Sector):每个块划分为 16 个扇区(0-15),每个扇区大小为 4KB。
- 页(Page):每个扇区包含 16 页,每页大小为 256 字节。页地址变化范围从 xx xx 00 到 xx xx FF。
块:
一整个存储器空间,先划分若干个块Block,其中每个块划分若干的扇形区Sector,对于每个扇区Sector,内部又可以分成很多页page。
存储器以字节为单位,每个字节都有唯一的地址,地址宽度 24位,3个字节
整个地址空间:从00 00 00到7F FF FF,整个空间里以64KB为一个基本单元
8MB*1204=8192KB
8192KB/64KB=128块 0-127
扇区:
64/4=16块 0-15
4KB*1024=4096字节
4096/256=16页
页:
当写入数据时,还有更细的划分Page,页是对整个存储空间划分
页地址变化范围xx xx 00 到 xx xx FF
SPI逻辑控制:
SPI数据传入过程
SPI发送3个字节地址,因为一页是256字节,所以一页内的字节地址,就取决于最低一个字节。高位的2个字节就是对应的页地址。
页地址,通过写保护和行解码,来选择操作哪一页,
字节地址,通过列解码和256字节页缓存来进行指定地址的读写操作
256字节的页缓存区,其实也是256字节的RAM存储器,数据读写就是通过RAM缓存区来进行的,我们写入数据,会先放到缓存区,等时序结束后,芯片将缓存区的数据复制到对应的Flash
为什么要缓存区?
因为SPI的写入的频率很高,而Flash的写入需要掉电不丢失,写入的数据比较慢,所以将写入的数据放到页缓存区存着,因为缓存区是RAM所以它的速度非常快,能跟上SPI总线的速度。
写入一个数据,连续写入的数据量,不能超过256字节
Flash操作注意事项
写使能只对之后跟随的一条时序有效,一条时序结束后顺手关门
有事后忙等待,和事前忙等待
原来数据
0XAA 1010 1010
覆盖数据
0X55 0101 0101(只能进行1改写为0)
得到的是0000 0000出错了,
所以写入数据前先擦除,再将所有数据位变为1
最小的擦除单元为一个扇区
手册信息
1.状态寄存器
11.1.1 忙标志(BUSY)
BUSY 是状态寄存器(S0 位)中的一个只读位。当设备正在执行页编程、扇区擦除、块擦除、整片擦除或写状态寄存器指令时,该位会被置为 1 。在此期间,设备将忽略进一步的指令,但 “读状态寄存器” 指令以及 “擦除挂起” 指令除外(具体见交流特性中的 tW、tPP、tSE、tBE 和 tCE 相关内容 )。当编程、擦除或写状态寄存器指令执行完成后,BUSY 位会被清零为 0 ,表示设备已准备好接收后续指令。
11.1.2 写使能锁存(Write Enable Latch,WEL)
写使能锁存(WEL)是状态寄存器(S1 位)中的一个只读位。在执行 “写使能” 指令后,该位会被置为 1 。当设备进入写禁用状态时,WEL 状态位会被清零为 0 。写禁用状态会在以下情况出现:上电时,或者执行了 “写禁用”、“页编程”、“扇区擦除”、“块擦除”、“整片擦除” 以及 “写状态寄存器” 中的任意一条指令后 。
指令码
- 数据字节以最高有效位优先的方式移位。括号内带有数据的字节字段 “()” 表示从设备的 DO 引脚读取的数据。
- 状态寄存器的内容会持续重复,直到 /CS(芯片选择,低电平有效)终止该指令。
产品ID 9Fh
读状态寄存器 05h
读取最低位的BUSY看看是不是为1
1表示芯片还在忙 0 表示芯片不忙了
等待BUSY为0
页编程 02h
读数据 03h 
程序
读取产品ID
发送指令0X9F 主机发送在 “发虚拟字节” 的同时,接收从机返回的 ID 数据
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_JEDEC_ID); //交换发送读取ID的指令*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //交换接收MID,通过输出参数返回*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //交换接收DID高8位*DID <<= 8; //高8位移到高位*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE); //或上交换接收DID的低8位,通过输出参数返回MySPI_Stop(); //SPI终止
}
W25Q64写使能
发送指令 0X06
void W25Q64_WriteEnable(void)
{MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_WRITE_ENABLE); //交换发送写使能的指令MySPI_Stop(); //SPI终止
}
页编程
发送指令码 0X02 发送地址
地址发送:通过 3 次 MySPI_SwapByte
拆分 24 位地址,依次发高→中→低 8 位,让 Flash 定位到具体要编程的页。
发送地址的高 8 位(23~16 位)
MySPI_SwapByte(Address >> 16);
- 逻辑:
Address
是 32 位地址(如0x00123456
),右移 16 位后,取高 8 位(0x00
)。 - 作用:告诉 Flash 要擦除的扇区地址的高 8 位
发送地址的中 8 位(15~8 位)
MySPI_SwapByte(Address >> 8);
- 逻辑:
Address
右移 8 位后,取中 8 位(0x12
)。 - 作用:告诉 Flash 要擦除的扇区地址的中 8 位。
发送地址的低 8 位(7~0 位)
MySPI_SwapByte(Address);
- 逻辑:直接取
Address
的低 8 位(0x34
)。 - 作用:告诉 Flash 要擦除的扇区地址的低 8 位
数据写入
MySPI_SwapByte(DataArray[i]);
把 DataArray
里的第 i
个字节,通过 SPI 发送到页缓存。
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable(); //写使能MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_PAGE_PROGRAM); //交换发送页编程的指令MySPI_SwapByte(Address >> 16); //交换发送地址23~16位 0X123456 右移16位 为0X12MySPI_SwapByte(Address >> 8); //交换发送地址15~8位 0X123456 右移8位 剩下0X1234 但是由于SPI 按照8字节传输 截断高8位 数据位0X34MySPI_SwapByte(Address); //交换发送地址7~0位 把高位0X1234舍弃 保留0X56for (i = 0; i < Count; i ++) //循环Count次{MySPI_SwapByte(DataArray[i]); //依次在起始地址后写入数据}MySPI_Stop(); //SPI终止W25Q64_WaitBusy(); //等待忙
}
擦除
发送指令码 0X20 发送数据地址
void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable(); //写使能MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB); //交换发送扇区擦除的指令MySPI_SwapByte(Address >> 16); //交换发送地址23~16位MySPI_SwapByte(Address >> 8); //交换发送地址15~8位MySPI_SwapByte(Address); //交换发送地址7~0位MySPI_Stop(); //SPI终止W25Q64_WaitBusy(); //等待忙
}
读数据
发送指令 0X03 发送地址 读取数据
读取数据
DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //依次在起始地址后读取数据
把接收到的字节存入缓冲区,实现 “从起始地址连续读数据”。
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{uint32_t i;MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_READ_DATA); //交换发送读取数据的指令MySPI_SwapByte(Address >> 16); //交换发送地址23~16位MySPI_SwapByte(Address >> 8); //交换发送地址15~8位MySPI_SwapByte(Address); //交换发送地址7~0位for (i = 0; i < Count; i ++) //循环Count次{DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //依次在起始地址后读取数据}MySPI_Stop(); //SPI终止
}