目录
一、初识W25Q64
1. 基本认识
W25Q64是华邦公司推出的大容量 基于SPI通信 的FLASH 产品,工作电压为 2.7~3.6V,存储容量为 64Mb(8MB),擦写周期可达 10万次,数据保存时间可达 20年。 W25Q64系列 Flash存储器与 普通串行Flash存储器 相比,其使用 更灵活、性能更出色,非常 适合用于存储声音、文本和数据。
W25Q64 有 32768个 可编程页,每页 256字节。
使用 页编程指令 就可以 每次编程 256字节。
使用 扇区擦除指令 可以 每次擦除 256字节。
使用 块擦除指令 可以 每次擦除 256 页。
使用 整片擦除指令 可以 擦除整个 芯片。
W25Q64 共有 2048个可擦除扇区(一个大小 4096字节) 或 128个 可擦除块。
2. 引脚介绍
W25Q16、 W25Q32 和 W25Q64 支持标准的 SPl接口,传输速率最大 75 MHz,采用四线制,即4个引脚。
① 串行 时钟引脚 (CLK)
② 芯片 选择引脚 (CS)
③ 串行数据 输出引脚(DO)
④ 串行数据 输入 / 输出引脚(DIO):在普通情况下,该引脚是 串行输入引脚(DI),当使用 快读双输出 指令时,该 引脚就变成了 输出引脚,在 这种情况下,芯片就有 2个 DO引脚,所以称为 双输出,其 通信速率 相当于翻了 一番,所以 传输速率更快。
二、W25Q64特性
1. SPI模式
W25Q16 / 32 / 64支持通过 四线制SPl总线方式访问,支持 两种 SPI通信方式,即模式 0 和模式 3 都支持。
模式 0 和 模式 3 的主要区别是:当主机的SPl接口处于空闲或者没有数据传输时,CLK的电平是 高电平还是 低电平。对于模式 0,CLK的电平为 低电平;对于模式 3,CLK的电平为 高电平。在 两种模式下芯片都是在 CLK的上升沿 采集输入数据,下降沿 输出数据。
2. 双输出SPI方式
W25Q16 / 32 / 64 支持 SPI 双输出方式,但需要使用 快读双输出指令(Fast Read Dual Output),这时通信速率相当于标准 SPI 的 2倍。这个命令非常适合在 需要一上电就快速下载代码到内存中的情况 或者 需要缓存代码段到内存中运行的情况。在使用快读双输出指令后,DI 引脚变为 输出引脚。
3.保持功能
芯片处于使能状态(CS=0)时,把 HOLD引脚拉低可以 使芯片暂停工作,适用于芯片和其他器件 共享主机 SPI 接口的情况。
例如:当 主机接收到一个更高优先级的中断时 就会抢占主机的 SPl接口,而这时芯片的页缓存区(Page Buffer)还有一部分 没有写完,在这种情况下,保持功能可以保存好 页缓存区的数据,等中断释放 SPI 口时,再继续完成刚才 没有写完的工作。
使用保持功能,CS引脚必须为低电平。在 HOLD引脚出现下降沿以后,如果CLK引脚为低电平,将 开启保持功能;如果 CLK引脚为高电平,保持功能在 CLK引脚的下一个 下降沿开始。在 HOLD引脚 出现上升沿以后,如果 CLK引脚为低电平,保持功能将结束;如果 CLK引脚为高电平,在 CLK引脚的下一个下降沿保持功能将结束。
在 保持功能起作用期间,DO引脚 处于 高阻抗状态,DI引脚 和 DO引脚上的信号将被忽略,而且在此期间,CS引脚 也必须 保持低电平,如果在此期间 CS引脚电平 被拉高,芯片内部的 逻辑将会被重置。
三、状态寄存器
1. BUSY位
忙位,是只读位,位于状态寄存器中的S0。当执行页编程、扇区擦除、块擦除、芯片擦除、写状态寄存器等指令时,该位将自动置 1。此时,除了读状态寄存器指令,其他指令都忽略;当页编程、扇区擦除、块擦除、芯片擦除和写状态寄存器等指令执行完毕之后,该位将自动清 0,表示芯片可以接收其他指令了。
2. WEL位
写保护位,是只读位,位于状态寄存器中的S1。执行完写使能指令后,该位将置 1。当芯片处于写保护状态下,该位为 0。
在下面两种情况下,会进入 写保护状态:掉电后执行指令写禁止、页编程、扇区擦除、块擦除、芯片擦除,以及 写状态寄存器。
3. BP2、BP1、 BP0位
块保护位,是可读可写位,分别位于状态寄存器的S4、S3、S2,可以用 写状态寄存器指令置位 这些块保护位。
在默认状态下,这些位都为 0,即 块处于 未保护状态下。可以设置块为没有保护、部分保护或者全部保护等状态。
当 SPR位为 1 或 /P引脚 为低电平时,这些位 不可以被更改。
4. TB位
底部和顶部块的保护位,是可读可写位,位于状态寄存器的 S5。该位默认为 0,表明顶部和底部块 处于未被保护状态下,可以用 写状态寄存器指令置位该位。当 SPR位为 1 或 /WP引脚 为低电平时,这些位不可以被更改。
5. 保留位
位于状态寄存器的 S6,读取状态寄存器值时,该位为 0。
6. SRP位
状态寄存器保护位,是可读可写位,位于状态寄存器的 S7。该位结合 /P引脚 可以禁止写状态寄存器功能。
该位默认值为0。当SRP=0时,/WP引脚 不能控制状态寄存器的写禁止;当 SRP=1 且 /P=0时,写状态寄存器指令失效;当SRP=1 且 /P=1 时,可以执行写状态寄存器指令。
四、常用操作指令
1. 写使能指令(06h)
该指令会使 状态寄存器WEL位置位。在执行每个页编程、扇区擦除、块擦除、芯片擦除和写状态寄存器等指令之前,都要先置位 WEL。/CS引脚 先拉低为低电平后,写使能指令代码 06h 从 DI引脚输入,在 CLK上升沿采集,然后将 /CS引脚 拉高为高电平。
2. 写禁止指令(04h)
该指令将会使 WEL位 变为0。/CS引脚 拉低为低电平后,再把 04h 从 DI引脚 输入到芯片,将 /CS引脚 拉高为高电平后,就可完成这个指令。
在执行完 写状态寄存器、页编程、扇区擦除、块擦除、芯片擦除等指令之后,WEL位就会自动变为 0。
3. 读状态寄存器指令(05h)
当 /CS引脚 拉低为低电平后,开始把 05h 从 DI引脚 输入到芯片,在 CLK的上升沿 时数据被芯片采集,当芯片采集到的数据为 05h 时,芯片就会把 状态寄存器的值从 DO引脚输出,数据在CLK的下降沿输出,高位在前。
读状态寄存器指令 在任何时候都可以用,甚至在 编程、擦除 和 写状态寄存器的过程中也可以用,这样就可以 根据状态寄存器的 BUSY位 判断编程、擦除和写状态寄存器周期有没有结束,从而知道芯片 是否可以接收 下一条指令了。
如果 /CS引脚 没有被拉高为高电平,状态寄存器的值将一直从DO引脚输出。/CS引脚拉高为高电平后,读状态寄存器指令结束。
4. 写状态寄存器指令(01h)
在执行 写状态寄存器指令之前,需要 先执行写使能指令。先将 /CS引脚 拉低为低电平后,然后把 01h 从 DI引脚 输入到芯片,接着把 想要设置的状态寄存器值通过 DI引脚 输入到芯片,/CS引脚拉高为高电平时,写状态寄存器指令结束。如果此时没有把 /CS 引脚 拉高为高电平 或者 拉得晚了,值将不会被写入,指令无效。
只有 状态寄存器中的 SRP、TB、BP2、BP1、BP0 位可以被写入,其他只读位的值不会变。在该指令执行的过程中,状态寄存器中的 BUSY位为 1,这时可以用 读状态寄存器指令读出 状态寄存器的值并进行判断。当 写寄存器指令 执行完毕时,BUSY 位将自动变为 0,WEL位 也自动变为 0。
通过对 TB、 BP2、 BP1、 BP0 等位写 1,就可以 实现将芯片的部分或全部存储区域设置为只读。通过对SRP位写 1,再把 /WP引脚 拉低为低电平,就可以 实现禁止写入 状态寄存器的功能。
5. 读数据指令(03h)
该指令 允许读出一个字节或一个以上的字节。先把 /CS引脚 拉低为低电平,然后把 03h 通过DI引脚 写入芯片,再送入 24位的地址,这些数据将在 CLK 的上升沿被芯片采集。
芯片接收完 24位地址 之后,就会把相应地址的数据在 CLK引脚的下降沿从 DO引脚 发送出去,高位在前。当发送完这个地址的数据之后,地址将自动增加,然后通过 DO引脚把 下一个地址的数据发送出去,从而形成一个 数据流。也就是说,只要时钟在工作,通过 一条读指令,就可以把 整个芯片存储区的数据读出来。
把 /CS引脚 拉高为高电平时,读数据指令将结束。当芯片在 执行页编程、扇区擦除、块擦除、芯片擦除和读状态寄存器指令的周期内,读数据指令不起作用。
五、相关代码
1. W25Q64的常用操作驱动。
/**
* @brief W25Q64等待空闲。读取状态寄存器,查询BUSY状态位命令。
*
* @return void
*/
void W25Q64_Busy(void)
{
uint8_t cmd[2],data[2];
cmd[0] = 0x05; // 读寄存器指令
cmd[1] = 0xFF; // 激活时钟
do
{
CS_ENABLE;
HAL_SPI_TransmitReceive(&hspi1,cmd,data,2,1000);
CS_DISABLE;
} while ((data[1]&0x01)==0x01); // 判断 BUSY位 是否为 0
}
/**
* @brief W25Q64写使能。
*
* @return void
*/
void W25Q64_WriteEnable(void)
{
uint8_t cmd;
cmd = 0x06;
W25Q64_Busy(); // 等待W25Q64空闲
CS_ENABLE;
HAL_SPI_Transmit(&hspi1,&cmd,1,1000);
CS_DISABLE;
}
/**
* @brief 擦除一个扇区 4kB
*
* @param SectorNum 指定扇区编号
*
* @return void
*/
void W25Q64_SectorErase(uint32_t SectorNum)
{
uint8_t cmd[4];
cmd[0] = 0x20; // 擦除寄存器指令
cmd[1] = (SectorNum*4096)>>16;
cmd[2] = (SectorNum*4096)>>8;
cmd[3] = (SectorNum*4096)>>0;
W25Q64_WriteEnable(); // W25Q64写使能
W25Q64_Busy(); // 等待W25Q64空闲
CS_ENABLE;
HAL_SPI_Transmit(&hspi1,cmd,4,1000);
CS_DISABLE;
}
/**
* @brief W25Q64写入一个页
*
* @param date 待保存数据
* @param PageNum 指定页编号
*
* @return void
*/
void W25Q64_WritePage(uint8_t* date, uint32_t PageNum)
{
uint8_t cmd[4];
cmd[0] = 0x02; // 擦除寄存器指令
cmd[1] = (PageNum*256)>>16;
cmd[2] = (PageNum*256)>>8;
cmd[3] = (PageNum*256)>>0;
W25Q64_WriteEnable(); // W25Q64写使能
W25Q64_Busy(); // 等待W25Q64空闲
CS_ENABLE;
HAL_SPI_Transmit(&hspi1,cmd,4,1000);
HAL_SPI_Transmit(&hspi1,date,256,5000);
CS_DISABLE;
}
/**
* @brief W25Q64读取数据
*
* @param rdate 读取数据的保存地址
* @param addr 读取数据的地址
* @param len 读取数据的长度
*
* @return void
*/
void W25Q64_ReadData(uint8_t* rdate, uint32_t addr, uint32_t len)
{
uint8_t cmd[4];
cmd[0] = 0x03; // 擦除寄存器指令
cmd[1] = addr>>16;
cmd[2] = addr>>8;
cmd[3] = addr>>0;
W25Q64_Busy(); // 等待W25Q64空闲
CS_ENABLE;
HAL_SPI_Transmit(&hspi1,cmd,4,1000);
HAL_SPI_Receive(&hspi1,rdate,len,50000);
CS_DISABLE;
}
2. 工程地址
2_Flash_W25Q64 · CXDNW/STM32F103C8T6练习项目 - 码云 - 开源中国 (gitee.com)
串口软件可以查看这篇博客: