来先看个基本款图
w25q64是一款flash(nor flash)芯片,最大存储8Mbytes的数据,其中8Mbits的存储单元划分为128块(block 64kb),每个块划分为16块(sector 4kb),每个扇区分为了16页(page 256byte),可对照上图
由于flash的特性(读取速度较慢),在和芯片外部数据交互时,通常需要注意
写入操作时:
(后面会详细介绍操作)
1.写入前,必须先使能
2.数据只能由1改写到0,不能由0改写到1(所以写入数据时需要先擦除)
3.擦除必须以最小单元进行(w25q64擦出的最小单元时一个扇区 4096 个字节)
4.连续写入多个字节时,一次最多写一个页,若超出会从此页的开头覆盖
5. 写入,擦除操作时,芯片会进行忙等待,不响应新的操作
读出操作时:
直接调用读取指令,无需使能,没有额外操作,没有页的限制,读取操作不会进去忙状态,但不能在忙状态读取
看到这儿,你是否对此芯片的操作有大致的步骤了~~~zhi
重点来了,接下来我们看看
w25q64的状态寄存器
BUSY:在写扇区,擦除,写状态寄存器时置1,表示忙,在这时芯片会忽视指令(除了读状态寄存器和暂停擦除指令),所以我们在连续写扇区等指令时要等待 BUSY 位为0
WEL:进行写使能指令后 WEL 置1,当设备写失能时,WEL 置0,或者当执行写扇区,擦除,写状态寄存器指令后自行清零
这里我们不详细介绍BP0,BP1,BP2,TB,SEC位,他们的作用大概为了保护flash的数据,处于保护下不能写和擦除,详细规则可以看上图。注意这几位都为非易失位(掉电不丢失),我们想写和擦除时,就要先解除这种保护,然后根据自己的需求设置保护。
其他的不做介绍,感兴趣的可以去看看手册
指令集:
注意:表中带括号的为芯片输出的数据,dummy为无用的数据。
Write Enable:写使能,在写扇区,擦除,写状态寄存器前必须先写使能
Read Status Segiser:读状态寄存器
Sage Program:页编程,(写入数据),首先发送指令码 02h, 接下来我们依次发送地址A23~A0(24位),在发送数据,这里最小1个字节,最大256个,超过会覆盖,需要注意一下
Quar Program:四倍页编程,其速率更大(需要用到额外数据输入线),我们目前不用
Erase:擦除,这里有扇区除,块擦除......,注意的时擦除前需要先写使能,注意每种大小下的地址对齐
比较重要就这么多,感兴趣的可以在去仔细看看数据手册
测试代码:
w25q64.h
#ifndef __W25Q64_H
#define __W25Q64_H
#define W25Q_STATUS_BUSY 0X01
#define W25Q_WRITE_ENABLE 0X06
#define W25Q_WRITE_DISABLE 0X04
#define W25Q_READ_STATUS_REGISTER_1 0X05
#define W25Q_READ_STATUS_REGISTER_2 0X35
#define W25Q_READ_STATUS_REGISTER 0X01
#define W25Q_PAGE_PROGRAM 0X02
#define W25Q_READ_DATA 0X03
#define W25Q_QUAR_PAGE_PROGRAM 0X32
#define W25Q_BLOCK_ERASE_64KB 0XD8
#define W25Q_BLOCK_ERASE_32KB 0X52
#define W25Q_SECTOR_ERASE 0X20
#define W25Q_CHIP_ERASE 0X60
#define W25Q_ERASE_SUSPEND 0X75
#define W25Q_ERASE_RESUME 0X7A
#define W25Q_POWER_DOWN 0XB9
#define W25Q_HIGH_PERFROMANCE_MODE 0XA3
#define W25Q_CONTIOUS_READ_MODE 0XFF
#define W25Q_RELSE_POWER_DOWN 0XAB
#define W25Q_DEVICE_ID 0X90
#define W25Q_READ_UNIQUE_ID 0X4B
#define W25Q_JEDEC_ID 0X9F
#define W25Q_DUMMY_BYTE 0XFF
void W25Q64_Init(void);
void W25Q64_ReadID(uint8_t* MID,uint16_t* DID);
void W25Q64_PageProgram(uint32_t Address,uint8_t* DataArrany,uint16_t Count);
void W25Q64_SectorEarse(uint32_t Address);
void W25Q64_ReadData(uint32_t Address,uint8_t* DataArrany,uint32_t Count);
void W25Q64_ReadStatusRegister(uint8_t* reg1,uint8_t* reg2);
void W25Q64_WriteStatusRegister(uint8_t reg1,uint8_t reg2);
#endif
main
#include "myspi.h"
#include "w25q64.h"
void W25Q64_Init(void){
MySPI_Init();
}
void W25Q64_ReadID(uint8_t* MID,uint16_t* DID){
MySPI_Start();
MySPI_SwapByte_Soft(W25Q_JEDEC_ID);
*MID = MySPI_SwapByte_Soft(W25Q_DUMMY_BYTE);
*DID = MySPI_SwapByte_Soft(W25Q_DUMMY_BYTE);
*DID <<= 8;
*DID |= MySPI_SwapByte_Soft(W25Q_DUMMY_BYTE);
MySPI_Stop();
}
void W25Q64_WriteEnable(void){
MySPI_Start();
MySPI_SwapByte_Soft(0x06);
MySPI_Stop();
}
void W25Q64_WAIT_BUSY(void){
uint32_t TimeOut;
TimeOut = 100000;
MySPI_Start();
MySPI_SwapByte_Soft(W25Q_READ_STATUS_REGISTER_1);
while(MySPI_SwapByte_Soft(W25Q_DUMMY_BYTE) & W25Q_STATUS_BUSY){
TimeOut --;
if(!TimeOut){
break;
}
}
MySPI_Stop();
}
void W25Q64_PageProgram(uint32_t Address,uint8_t* DataArrany,uint16_t Count){
uint16_t i;
uint8_t a;
W25Q64_WriteEnable();
MySPI_Start();
a = MySPI_SwapByte_Soft(W25Q_PAGE_PROGRAM);
a = MySPI_SwapByte_Soft(Address >> 16);
a = MySPI_SwapByte_Soft(Address >> 8);
a = MySPI_SwapByte_Soft(Address);
for(i = 0;i<Count;++i){
MySPI_SwapByte_Soft(DataArrany[i]);
}
MySPI_Stop();
W25Q64_WAIT_BUSY();
}
void W25Q64_SectorEarse(uint32_t Address){
W25Q64_WriteEnable();
MySPI_Start();
MySPI_SwapByte_Soft(W25Q_SECTOR_ERASE);
MySPI_SwapByte_Soft(Address >> 16);
MySPI_SwapByte_Soft(Address >> 8);
MySPI_SwapByte_Soft(Address);
MySPI_Stop();
W25Q64_WAIT_BUSY();
}
void W25Q64_ReadData(uint32_t Address,uint8_t* DataArrany,uint32_t Count){
uint16_t i;
MySPI_Start();
MySPI_SwapByte_Soft(W25Q_READ_DATA);
MySPI_SwapByte_Soft(Address >> 16);
MySPI_SwapByte_Soft(Address >> 8);
MySPI_SwapByte_Soft(Address);
for(i = 0;i<Count;++i){
DataArrany[i] = MySPI_SwapByte_Soft(W25Q_DUMMY_BYTE);
}
MySPI_Stop();
}
void W25Q64_ReadStatusRegister(uint8_t* reg1,uint8_t* reg2){
MySPI_Start();
MySPI_SwapByte_Soft(W25Q_READ_STATUS_REGISTER_1);
*reg1 = MySPI_SwapByte_Soft(W25Q_DUMMY_BYTE);
MySPI_SwapByte_Soft(W25Q_READ_STATUS_REGISTER_2);
*reg1 = MySPI_SwapByte_Soft(W25Q_DUMMY_BYTE);
MySPI_Stop();
}
void W25Q64_WriteStatusRegister(uint8_t reg1,uint8_t reg2){
W25Q64_WriteEnable();
MySPI_Start();
MySPI_SwapByte_Soft(W25Q_READ_STATUS_REGISTER);
MySPI_SwapByte_Soft(reg1);
MySPI_SwapByte_Soft(reg2);
MySPI_Stop();
W25Q64_WAIT_BUSY();
}
main.c
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "24l01.h"
#include "w25q64.h"
//ecodeing = "ANSI"
uint16_t DID;
uint8_t MID;
uint8_t ArranyWrite[] = "W25Q64 TEST!!!\0";
uint8_t ArranyRead[50] ;
uint32_t Adderss = 0x000001;
uint8_t reg1,reg2;
int main(void)
{
OLED_Init();
W25Q64_Init();
W25Q64_ReadID(&MID,&DID);
OLED_ShowHexNum(1,1,MID,4);
OLED_ShowHexNum(1,8,DID,4);
W25Q64_ReadStatusRegister(®1,®2);
OLED_ShowHexNum(2,1,reg1,2);
OLED_ShowHexNum(2,4,reg2,2);
reg1 &= 0x9B;
W25Q64_WriteStatusRegister(reg1,reg2);
W25Q64_ReadStatusRegister(®1,®2);
OLED_ShowHexNum(2,7,reg1,2);
OLED_ShowHexNum(2,10,reg2,2);
W25Q64_SectorEarse(Adderss);
W25Q64_PageProgram(Adderss,ArranyWrite,20);
W25Q64_ReadData(Adderss,ArranyRead,20);
OLED_ShowString(3,1,ArranyRead);
while(1);
}
几点补充......
1. <w25q64.c> W24Q64_WAIT_BUSY 函数判断 status register 1 的 BUSY 位,我们程序采用事后等待,写,擦除后等待非忙后退出
2.<main.c> 写操作前注意看看写入的扇区是否允许写入(有的芯片买回来默认全部扇区处于保护模式)
最后看看代码效果~~~~
(OLED 刷新率有点低,,,)