11-2W25Q64简介&软件SPI读写W25Q64

W25Q64简介

•W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器(数据掉电不丢失),常应用于数据存储、字库存储、固件程序存储等场景
•存储介质:Nor Flash(闪存)
•时钟频率:80MHz / 160MHz (Dual SPI双重SPI) / 320MHz (Quad SPI四重SPI)
•存储容量(24位地址):
  W25Q40:    4Mbit / 512KByte
  W25Q80:    8Mbit / 1MByte
  W25Q16:    16Mbit / 2MByte
  W25Q32:    32Mbit / 4MByte
  W25Q64:    64Mbit / 8MByte
  W25Q128:  128Mbit / 16MByte
  W25Q256:  256Mbit / 32MByte

硬件电路

硬件电路

硬件电路

引脚功能
VCC、GND电源(2.7~3.6V)
CS(SS)SPI片选
CLK(SCK)SPI时钟
DI(MOSI)SPI主机输出从机输入
DO(MISO)SPI主机输入从机输出
WP写保护
HOLD数据保持

W25Q64框图

W25Q64框图
右上角为存储器的规划示意图,以64KB为基本单元,分为若干个块。

Flash操作注意事项

写入操作时:

•写入操作前,必须先进行写使能(使用SPI发送写使能的指令)
•每个数据位只能由1改写为0,不能由0改写为1
•写入数据前必须先擦除,擦除后,所有数据位变为1
•擦除必须按最小擦除单元进行
•连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入
•写入操作结束后,芯片进入忙状态,不响应新的读写操作

读取操作时:

•直接调用读取时序,无需使能,无需额外操作,没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取

软件SPI读写W25Q64

MySPI.c

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
#include "stm32f10x.h"                  // Device header void MySPI_W_CS(uint8_t BitValue)//写CS引脚{	GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);} void MySPI_W_SCK(uint8_t BitValue)//写SCK引脚{	GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)BitValue);} void MySPI_W_MOSI(uint8_t BitValue)//写MOSI引脚{	GPIO_WriteBit(GPIOA,GPIO_Pin_7,(BitAction)BitValue);} uint8_t MySPI_R_MISO(void)//读MISO引脚{	return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);} void MySPI_Init(void){	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	//初始化DI、CLK、CS为推挽输出	GPIO_InitTypeDef GPIO_InitStructure;	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	GPIO_Init(GPIOA, &GPIO_InitStructure);	GPIO_SetBits(GPIOA,GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7);	//初始化DO为上拉输入	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	GPIO_Init(GPIOA, &GPIO_InitStructure);	GPIO_SetBits(GPIOA,GPIO_Pin_6);	MySPI_W_CS(1);	MySPI_W_SCK(0);} void MySPI_Start(void)//起始信号,只需要把CS置低电平即可{	MySPI_W_CS(0);} void MySPI_Stop(void)//终止信号,只需要把CS置高电平即可{	MySPI_W_CS(1);}uint8_t MySPI_SwapByte(uint8_t ByteSend)//交换字节{// uint8_t i,ByteReceive = 0x00;//接收到的字节//	for(i = 0; i < 8 ;i ++)//	{//	MySPI_W_MOSI(ByteSend & (0x80 >> i));//	MySPI_W_SCK(1);//	if (MySPI_R_MISO() == 1 ) //	{//		ByteReceive |= (0x80 >> i);//	}//	MySPI_W_SCK(0);//	}	uint8_t i;	for(i = 0; i < 8 ;i ++)	{		MySPI_W_MOSI(ByteSend & 0x80);		ByteSend <<= 1;		MySPI_W_SCK(1);		if (MySPI_R_MISO() == 1 ) 		{			ByteSend |= 0x01;		}		MySPI_W_SCK(0);	} 	return ByteSend;} 

W25Q64.c

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
#include "stm32f10x.h"                  // Device header#include "MySPI.h"#include "W25Q64_Ins.h" void W25Q64_Init(void)//初始化W25Q64{	MySPI_Init();} void W25Q64_ReadID(uint8_t *MID,uint16_t *DID)//读取ID{	MySPI_Start();//先发送起始信号	MySPI_SwapByte(W25Q64_JEDEC_ID);//发送0x9F,交换到厂商号	*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//接收厂商号,发送的0xFF是为了交换到返回的厂商号	*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//接收返回值的高8位	*DID <<= 8;//左移8位	*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);//接收返回值的低8位	MySPI_Stop();} void W25Q64_WriteEnable(void)//写使能{	MySPI_Start();//先发送起始信号	MySPI_SwapByte(W25Q64_WRITE_ENABLE);//发送指令码,写使能	MySPI_Stop();//发送停止信号结束} void W25Q64_WaitBusy(void){	uint32_t Timeout;	MySPI_Start();//先发送起始信号	MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);//发送指令码,读状态寄存器1	Timeout = 100000;	while((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)	{		Timeout -- ;		if(Timeout == 0)		{			break;		}	}	MySPI_Stop();} void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArray,uint16_t Count){	W25Q64_WriteEnable();	uint16_t i;	MySPI_Start();//先发送起始信号	MySPI_SwapByte(W25Q64_PAGE_PROGRAM);//发送指令码	MySPI_SwapByte(Address >> 16);//发送地址最高位	MySPI_SwapByte(Address >> 8);//发送地址中间位	MySPI_SwapByte(Address);//发送地址最低位,高位被舍弃了 	for(i = 0; i < Count; i ++)	{	MySPI_SwapByte(DataArray[i]);	} 	MySPI_Stop();	W25Q64_WaitBusy();} void W25Q64_SectorErase(uint32_t Address){	W25Q64_WriteEnable();	MySPI_Start();//先发送起始信号	MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);//发送指令码,扇区擦除	MySPI_SwapByte(Address >> 16);//发送地址最高位	MySPI_SwapByte(Address >> 8);//发送地址中间位	MySPI_SwapByte(Address);//发送地址最低位,高位被舍弃了	MySPI_Stop();	W25Q64_WaitBusy();//事后等待} void W25Q64_ReadData(uint32_t Address,uint8_t *DataArray,uint32_t Count){	uint32_t i;	MySPI_Start();//先发送起始信号	MySPI_SwapByte(W25Q64_READ_DATA);//发送指令码,读数据	MySPI_SwapByte(Address >> 16);//发送地址最高位	MySPI_SwapByte(Address >> 8);//发送地址中间位	MySPI_SwapByte(Address);//发送地址最低位,高位被舍弃了 	for(i = 0; i < Count; i ++)	{	DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//发送0xFF,换回有用的数据	}	MySPI_Stop();} 

main.c

123456789101112131415161718192021222324252627282930313233343536373839
#include "stm32f10x.h"                  // Device header#include "Delay.h"#include "OLED.h"#include "W25Q64.h" uint8_t MID;uint16_t DID; uint8_t ArrayWrite[] = {0x55,0x66,0x77,0x88};uint8_t ArrayRead[4]; int main(void){	OLED_Init();	W25Q64_Init(); 	OLED_ShowString(1,1,"MID:   DID:");	OLED_ShowString(2,1,"W:");	OLED_ShowString(3,1,"R:");	W25Q64_ReadID(&MID,&DID);	OLED_ShowHexNum(1,5,MID,2);	OLED_ShowHexNum(1,12,DID,4); //	W25Q64_SectorErase(0x000000);	W25Q64_PageProgram(0x000000,ArrayWrite,4);	W25Q64_ReadData(0x000000,ArrayRead,4); 	OLED_ShowHexNum(2,3,ArrayWrite[0],2);	OLED_ShowHexNum(2,6,ArrayWrite[1],2);	OLED_ShowHexNum(2,9,ArrayWrite[2],2);	OLED_ShowHexNum(2,12,ArrayWrite[3],2); 	OLED_ShowHexNum(3,3,ArrayRead[0],2);	OLED_ShowHexNum(3,6,ArrayRead[1],2);	OLED_ShowHexNum(3,9,ArrayRead[2],2);	OLED_ShowHexNum(3,12,ArrayRead[3],2);}  
🔍 ×