STM32F4 W25Q64存储芯片详解:特性以及应用
一、概述
W25Q64是由华邦电子(Winbond)生产的一款串行闪存(Serial Flash)芯片,其容量为64 Mbit(8 MB)。该芯片采用**SPI(Serial Peripheral Interface)**通信协议,广泛应用于嵌入式系统、物联网设备、路由器、工控板等领域。凭借其高性价比、低功耗和高速读写能力,W25Q64在嵌入式存储市场占据重要地位。
二、芯片基本参数
项目 | 参数 |
---|---|
容量 | 64 Mbit / 8 MB |
接口 | SPI(标准/双/四线) |
电压范围 | 2.7V - 3.6V |
最大时钟频率 | 104 MHz(标准 SPI) |
扇区大小 | 4 KB |
块大小 | 32 KB / 64 KB |
擦写寿命 | 10 万次擦写循环 |
数据保持时间 | 超过 20 年 |
三、存储结构
W25Q64 的存储结构被划分为:
-
扇区(Sector):每个扇区大小为 4KB,共 2048 个扇区;
-
块(Block):每个块分为 32KB 或 64KB 两种,共支持 256 个 32KB 块,或 128 个 64KB 块;
-
页(Page):每页大小为 256 字节,共有 32768 页。
页是最小写入单位,扇区或块是擦除的基本单位。不能对单字节进行擦除。
四、通信接口:SPI
W25Q64 支持三种 SPI 模式:
-
标准 SPI(Single SPI):MOSI/MISO/SCK/CS;
-
双线 SPI(Dual SPI):数据通过两个 IO(IO0, IO1)传输;
-
四线 SPI(Quad SPI):使用四个 IO(IO0 - IO3)同时传输,极大提高传输速率。
Quad SPI 模式下,读操作速度可达 80MB/s,适用于高带宽应用场景。
五,W25Q64 状态及控制寄存器介绍
位 | 名称 | 描述 |
---|---|---|
7 | SRP1 | 状态寄存器保护位1(与SRP2一起用于硬件保护) |
6 | SEC | 扇区保护指示(扇区大小 4KB/64KB) |
5 | TB | 顶部/底部保护选择位(配合BP) |
4~2 | BP2~BP0 | 块保护位(决定写保护区域范围) |
1 | WEL | 写使能锁定位(0:写禁止;1:写允许) |
0 | BUSY | 忙状态(0:空闲;1:内部操作中) |
BUSY 位:在执行写操作和擦除操作时,此位会为1,执行完后自动变为0 所以 在写完或擦除完后要等待此位变为0再继续执行程序
W25Q64芯片状态寄存器的作用是什么 ?
配置位作用 :块区保护
读状态位作用:写指令是否执行完成
如何配置(读)寄存器?
发送读指令和发送配置值
什么时候使用?
初始化配置模块的时候(配置寄存器)
等待写操作完成时候(读这个寄存器)
六、主要指令集
W25Q64 使用固定指令集进行数据操作:
-
读取指令:
-
03h
:标准读取(Slow Read) -
0Bh
:快速读取(Fast Read) -
6Bh
:双线读取(Dual Output Fast Read) -
EBh
:四线读取(Quad I/O Fast Read)
-
-
写入指令:
-
02h
:页写入(Page Program) -
20h
:扇区擦除(4KB) -
D8h
:块擦除(64KB) -
C7h/60h
:整片擦除
-
-
状态指令:
-
05h
:读取状态寄存器 1 -
01h
:写状态寄存器 -
35h
:读取状态寄存器 2
-
以下来看一下配置W25Q64的代码
void Init_W25Q64()
{
//所选SPI初始化
Init_Spi1();
//初始化片选线 CS
RCC->AHB1ENR |= (1<<1);
GPIOB->MODER &= ~(3<<28);
GPIOB->MODER |= (1<<28);
GPIOB->OTYPER &= ~(1<<14);
GPIOB->OSPEEDR &= ~(3<<28);
GPIOB->OSPEEDR |= (2<<28);
W25Q64_CS_H;
//设置状态寄存器解除所有块保护
Write_Status_Register(0x00);
}
//写使能函数
配置:
1.先拉低片选
2.发送指令0x06
3.片选拉高
void Write_W24Q64()
{
W25Q64_CS_L; // 拉低片选信号
SPI1_Byte(0x06);
W25Q64_CS_H; //拉高片选信号
}
//读状态寄存器函数
配置:
1.先拉低片选
2.发送指令0x05
//返回值不需要接收
3.接收状态寄存器的值 //发送参数随便
4.片选拉高
u8 Read_Status_Register()
{
u8 sta;
W25Q64_CS_L; //拉低片选线
SPI1_Byte(0x05);//发送读状态寄存器指令
/ sta = SPI1_Byte(0xff);//接收在状态寄存器读取的数据
W25Q64_CS_H;
return sta;
}
//写状态寄存器函数
在执行写状态寄存器指令时,先要执行“写使能”指令。
再拉低/CS引脚,然后将01h从DI引脚送到芯片,
然后将我们要设置的状态寄存器的值送到芯片.
拉高/CS,读状态寄存器的值,等待0号为是0
void Write_Status_Register(u8 cmd_data)
{
Write_W24Q64();
W25Q64_CS_L;
SPI1_Byte(0x01);
SPI1_Byte(cmd_data);
W25Q64_CS_H;
//等待Busy位自动变0
while(Read_Status_Register() & (1<<0));
}
//页写数据函数
配置:
写使能
片选拉低
发送写数据指令
发送24位地址
循环发送数据
片选拉高
等待写入完成
void w25q64_Write_Page(u32 inner_addr,u8* data,u16 len)
{
Write_W24Q64();
W25Q64_CS_L;
//发送页编程指令
SPI1_Byte(0x02);
//发送器件地址,器件地址为24位,我们要分三次发送
//首先是高位,中高位,低位
SPI1_Byte(inner_addr >> 16);
SPI1_Byte(inner_addr >> 8);
SPI1_Byte(inner_addr);
while(len)
{
SPI1_Byte(*data);
data++;
len--;
}
W25Q64_CS_H;
//等待Busy位自动变0
while(Read_Status_Register() & (1<<0));
}
//可跨页连续写多数据函数
void w26q64_Write_Bytes(u32 inner_addr,u8 *data,u32 len)
{
u16 less_num;
while(1)
{
//计算本页还剩多少字节可以写less_num
less_num = 8 - inner_addr % 8;
//判断如果要写的字节数据不需要跨页
if(less_num >= len)
{
//调用页写函数写完就可以了 inner_addr num_byte str
w25q64_Write_Page(inner_addr,data, len);
//跳出循环
break;
}
//判断如果要写的字节数据需要跨页
else
{
//调用页写函数先把本页写满 inner_addr less_num str
w25q64_Write_Page(inner_addr, data,len);
//计算还剩多少字节要写 num_byte - less_num
len = len - less_num;
//计算剩余要写的内容的地址 str + less_num
data = data + less_num;
//计算下一页要写的内容部地址 inner_addr + less_num
inner_addr = inner_addr + less_num;
}
}
}
//连续读数据操作函数
配置:
1.先拉低片选 (CS拉低);
2.发送读数据指令(0x03)
3.发送24位(3字节)地址
4.可以连续读(数据线随便发)
5.拉高片选
void w25q64_read_bytes(u32 inner_addr,u8 *data,u32 len)
{
W25Q64_CS_L;
//发送读数据指令
SPI1_Byte(0x03);
//发送器件地址,器件地址为24位,我们要分三次发送
//首先是高位,中高位,低位
SPI1_Byte(inner_addr >> 16);
SPI1_Byte(inner_addr >> 8);
SPI1_Byte(inner_addr);
//接收数据
while(len)
{
*data = SPI1_Byte(0xff);
data++;
len--;
}
W25Q64_CS_H;
}
//扇区擦除函数
配置:
0.写使能
1.先拉低片选 (CS拉低);
2.发送指令(0x20)
3.发送24位(3字节)地址
4.拉高片选
5.等待擦除完成
void w25q64_sector_erase(u32 inner_addr)
{
Write_W24Q64();
W25Q64_CS_L;
//发送页编程指令
SPI1_Byte(0x20);
//发送器件地址,器件地址为24位,我们要分三次发送
//首先是高位,中高位,低位
SPI1_Byte(inner_addr >> 16);
SPI1_Byte(inner_addr >> 8);
SPI1_Byte(inner_addr);
W25Q64_CS_H;
//等待Busy位自动变0
while(Read_Status_Register() & (1<<0));
}
//块擦除函数
void w25q64_block_erase(u32 inner_addr)
{
Write_W24Q64();
W25Q64_CS_L;
//发送页编程指令
SPI1_Byte(0xd8);
//发送器件地址,器件地址为24位,我们要分三次发送
//首先是高位,中高位,低位
SPI1_Byte(inner_addr >> 16);
SPI1_Byte(inner_addr >> 8);
SPI1_Byte(inner_addr);
W25Q64_CS_H;
//等待Busy位自动变0
while(Read_Status_Register() & (1<<0));
}