关于W25QXX系列的FLASH

一、总述

      W25X16、W25X32 和 W25X64 系列 FLASH 存储器可以为用户提供存储解决方案,具有“PCB 板占用空间少”“引脚数量少”、“功耗低”等特点。与普通串行 FLASH相比,使用更灵活,性能更出色。它非常适合做代码下载应用,例如存储声音,文本和数据。工作电压在 2.7V~3.6V 之间,正常工作状态下电流消耗 0.5 毫安,掉电状态下电流消耗 1微安。所有的封装都是“节省空间”型的。
      W25X16、W25X32 和 W25X64 分别有 8192、16384 和 32768 可编程页,每页 256 字节用“页编程指令”每次就可以编程 256 个字节。用“扇区 (sector) 擦除指令”每次可以擦除 16 页,用“块(block)擦除指令”每次可以擦除 256 页,用“整片擦除指令”即可以擦除整个芯片。W25X16、W25X32 和 W25X64 分别有 512、1024 和 2048 个可擦除“扇区”或32、64 和 128 个可擦除“块”。
W25X16、W25X32 和 W25X64 支持标准的 SPI接口,传输速率最大 75MHz。
四线制:
1:串行时钟引脚 CLK;
2:芯片选择引脚 CS;
3:串行数据输出引脚 DO;
4:串行数据输入输出引脚 DIO。
引脚如图:
特征:
串行 FLASH 系列存储器系列:
W25X16: 16M 比特 (bit) /2M 字节 (byte)
W25X32: 32M 比特 (bit) /4M 字节 (byte)
W25X64: 64M 比特(bit) /8M字节 (byte)

统一的 4K 字节扇区 (Sectors) 和 64K 字节块区(Blocks)。一页有256B,共有16页;一个扇区有4K(4096)=256Bx16(页),有16扇区;一个块有64K(65535)=4Kx16(扇区),有64块。

二、指令表

通过发送和接收指令实现相应功能

三、功能介绍

1.读ID指令(90H)

制造/器件ID和 JEDEC ID的执行过程及时序图是一样的。

     “读制造/器件号”指令不同于“释放掉电/器件ID指令”,“读制造/器件号”指令读出的数据包含JEDEC标准制造号和特殊器件ID号。
     先把/CS引脚拉低,然后把指令90h通过引脚DIO送到芯片,然后接着把24位地址000000h送到芯片,然后芯片会先后把“生产D”和“器件D”通过DO引脚在CLK的上升沿发送出去。如果把24位地址写为000001h,ID号的发送顺序会颠倒,即先发“器件 ID”后发“生产ID”。ID号都是8位数据。

2.写使能(06H)

     “写使能”指令将会使“状态寄存器”WEL位置位。在执行每个“页编程”“扇区擦除”“块区擦除”“芯片擦除”和“写状态寄存器”命令之前,都要先置位WEL。ICS 引脚先拉低之后,“写使能”指令代码06h 从 DI引脚输入,在CLK上升沿采集,然后再拉高/CS 引脚。

3.读状态寄存器(05H)

     当/CS拉低之后,开始把05h 从DIO 引脚送到芯片,在CLK的上升沿数据被芯片采集,当芯片认出采集到的数据时05h时,芯片就会把“状态寄存器”的值从DO引脚输出,数据在CLK的下降沿输出,高位在前。
     “读状态寄存器”指令在任何时候都可以用,甚至在编程、擦除和写状态寄存器的过程中也可以用,这样,就可以从状态寄存器的BUSY位判断编程、擦除和写状态寄存器周期有没有结束,从而让我们知道芯片是否可以接收下一条指令了。如果/CS不被拉高,状态寄存器的值将一直从DO引脚输出。ICS拉高之后,读指令结束。

4.读数据(03H)

     这个时序要注意:如果使用硬件sPI功能,一般需要主机先发送一个字节数据后才能接收到flash发送出来的数据,实际上是为了给flash提供发送的时钟(因为时钟只能由主机产生)。
     “读数据”指令允许读出一个字节或一个以上的字节被读出。先把/CS引脚拉低,然后把03h通过DIO引脚送到芯片,之后再送入24位的地址,这些数据在CLK的上升沿被芯片采集。芯片接收完24位地址之后,就会把相应地址的数据在CLK引脚的下降沿从DO引脚送出去,高位在前。当读完这个地址的数据之后,地址自动增加,然后通过DO引脚把下一个地址的数据送出去,形成一个数据流。也就是说,只要时钟在工作,通过一条读指令,就可以把整个芯片存储区的数据读出来。把/CS 引脚拉高,“读数据”指令结束。当芯片在执行编程、擦除和读状态寄存器指令的周期内,“读数据”指令不起作用。

 

5.页写(02H)

     执行“页编程”指令之前,需要先执行“写使能”指令,而且要求待写入的区域位都为1,也就是需要先把待写入的区域擦除。先把/CS 引脚拉低,然后把代码02h通过DIO引脚送到芯片,然后再把24位地址送到芯片,然后接着送要写的字节到芯片。在写完数据之后,把/CS引脚拉高。
     写完一页(256个字节)之后,必须把地址改为0,不然的话,如果时钟还在继续,地址将自动变为页的开始地址。在某些时候,需要写入的字节不足256个字节的话,其它写入的字节都是无意义的。如果写入的字节大于了256个字节,多余的字节将会加上无用的字节覆盖刚刚写入的的256个字节。所以需要保证写入的字节小于等于256个字节。
     在指令执行过程中,用“读状态寄存器”可以发现BUSY位为1,当指令执行完毕,BUSY位自动变为0。如果需要写入的地址处于“写保护”状态,“页编程”指令无效。

6.整块芯片擦除(07H)

     “芯片擦除”指令将会使整个芯片的存储区位都变为1,即字节都变位FFh。在执行“芯片擦除”指令之前需要先执行“写使能”指令。
     先把/CS引脚拉低,然后再把指令代码C7h通过DIO引脚送到芯片,然后拉高/CS 引脚。如果没有及时拉高/CS 引脚,指令无效。在“芯片擦除”指令执行周期内,可以执行“读状态寄存器”指令访问BUSY位,这时BUSY位为1,当“芯片擦除”指令执行完毕,BUSY变为0,WEL位也变为0。任何一个块区处于保护状态(BP2\BPIBP)),指令都会失效。

四、代码实现

初始化代码这里就不介绍了,注意初始化SPI和GPIO哦,引脚一定要配置正确哦

#define W25Q32_FLASH_STATUS_WIP          (1UL << 0)
#define W25Q32_FLASH_STATUS_WEL          (1UL << 1)

//函数功能:SPI发送接收数据
static uint8_t W25Q32_FlashSendBytes(uint8_t byte)
{
	while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_TXE));
	SPI_SendData(SPI1, byte);
	while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE));
	SPI_ReceiveData(SSPI1);
}
//函数功能:读JEDEC_ID  先读ID确保FLASH可以正常使用
uint32_t W25Q32_FlashReadID(void)
{
	uint32_t temp0 = 0, temp1 = 0, temp2 = 0;
	
	W25Q32_FLASH_CS_LOW();//片选拉低
	
	W25Q32_FlashSendBytes(0X9F);
	temp0=W25Q32_FlashSendBytes(0XFF)<<16;
	temp1=W25Q32_FlashSendBytes(0XFF)<<8;
	temp2=W25Q32_FlashSendBytes(0XFF);
	
	W25Q32_FLASH_CS_HIGH();	

	return (temp0|temp1|temp2);
}
//函数功能:写使能 部分功能执行前需要先执行写使能
void W25Q32_FlashWriteEnable(void)
{
	W25Q32_FLASH_CS_LOW();
	W25Q32_FlashSendBytes(0X06);
	W25Q32_FLASH_CS_HIGH();	
}
//函数功能:读状态
uint32_t W25Q32_FlashReadStatus(void)
{
	uint8_t status = 0;
	
	W25Q32_FLASH_CS_LOW();
	
	W25Q32_FlashSendBytes(0X05);
	status = W25Q32_FlashSendBytes(0XFF);
	
	W25Q32_FLASH_CS_HIGH();
	
	return status;
}
//函数功能:读数据(接收数据)
int W25Q32_FlashReadData(uint8_t *buffer, uint32_t addr, uint32_t size)
{
	int count = 0;
	
	if (addr >= MAX_SIZE || NULL == buffer || size < 1)
	{
		return 1;
	}
	
	memset(buffer, 0, size);
	
	W25Q32_FLASH_CS_LOW();
	
	W25Q32_FlashSendBytes(0X03);
	W25Q32_FlashSendBytes((addr >> 16) & 0xFF);
	W25Q32_FlashSendBytes((addr >> 8) & 0xFF);
	W25Q32_FlashSendBytes(addr & 0xFF);
	
	for (count = 0; count < size; count++)
	{
		buffer[count] = W25Q32_FlashSendBytes(0XFF);
	}
	
	W25Q32_FLASH_CS_HIGH();
	
	return 0;
}
//函数功能:页写
int W25Q32_FlashPageWrite(uint8_t *buffer, uint32_t addr, uint32_t size)//页写
{
	int count = 0;
	
	if (addr >= MAX_SIZE || NULL == buffer || size < 1)//MAX_SIZE=4M
	{
		return 1;
	}
	
	W25Q32_FlashWriteEnable();
	if (!(W25Q32_FLASH_STATUS_WEL & W25Q32_FlashReadStatus()))
	{
		return OPERATE_FAILED;
	}
	
	W25Q32_FLASH_CS_LOW();
	
	W25Q32_FlashSendBytes(0X02);
	W25Q32_FlashSendBytes((addr >> 16) & 0xFF);
	W25Q32_FlashSendBytes((addr >> 8) & 0xFF);
	W25Q32_FlashSendBytes(addr & 0xFF);
	
	for (count = 0; count < size; count++)
	{
		W25Q32_FlashSendBytes(buffer[count]);
	}
	
	W25Q32_FLASH_CS_HIGH();
	
	while (W25Q32_FlashReadStatus() & W25Q32_FLASH_STATUS_WIP);//读忙位
	
	return count;
}
//函数功能:跨页写
int W25Q32_Flash_WriteData(uint8_t* pBuff, uint32_t Addr, uint32_t Num)//发送数据
{
	uint8_t addr = 0; 
	uint8_t temp = 0;
	uint8_t count = 0; 
	uint8_t numpage = 0; 
	uint8_t numsingle = 0; 
	
	addr = Addr % PAGE_SIZE; //PAGE_SIZE=256
	count = PAGE_SIZE - addr;//当地址从中间开始时,保存前面不满一页的数据
	numpage = Num / PAGE_SIZE;
	numsingle = Num % PAGE_SIZE;
	
	if (0 == addr) //位置从一页(256)的0地址开始
	{
		if (0 == numpage)//数量不满一页(256)
		{
			W25Q32_FlashPageWrite(pBuff, Addr, Num);
		}
		else //数量超过一页
		{
			while(numpage--)
			{
				W25Q32_FlashPageWrite(pBuff, Addr, PAGE_SIZE);//写满页的数据
				Addr += PAGE_SIZE;
				pBuff += PAGE_SIZE;			
			}		
			W25Q32_FlashPageWrite(pBuff, Addr, numsingle);	//写最后未满一页的数据
		}
	}
	else
	{		
		if (0 == numpage)
		{
			if (numsingle > count)//数量过多需要跨页写
			{
				temp = numsingle - count;
				W25Q32_FlashPageWrite(pBuff, Addr, count);
				Addr += count;
				pBuff += count;	
				W25Q32_FlashPageWrite(pBuff, Addr, temp);
			}
			else 
			{
				W25Q32_FlashPageWrite(pBuff, Addr, Num);
			}
		}
		else 
		{
			Num -= count;
			numpage = Num / PAGE_SIZE;
			numsingle = Num % PAGE_SIZE;
			W25Q32_FlashPageWrite(pBuff, Addr, count);
			Addr += count;
			pBuff += count;	
			
			while(numpage--)
			{
				W25Q32_FlashPageWrite(pBuff, Addr, PAGE_SIZE);//满页的数据
				Addr += PAGE_SIZE;
				pBuff += PAGE_SIZE;			
			}		
			if (0 != numsingle)
			{
				W25Q32_FlashPageWrite(pBuff, Addr, numsingle);//写最后未满一页的数据
			}
		}
	}
}
//函数功能:整个芯片擦除  写数据前需要先进行芯片擦除,否则无法写入。
int W25Q32_FlashChipErase(void)
{
	W25Q32_FlashWriteEnable();
	if (!(W25Q32_FLASH_STATUS_WEL & W25Q32_FlashReadStatus()))
	{
		return 2;
	}
	
	W25Q32_FLASH_CS_LOW();
	W25Q32_FlashSendBytes(0XC7);
	W25Q32_FLASH_CS_HIGH();

	while (W25Q32_FlashReadStatus() & W25Q32_FLASH_STATUS_WIP);
	
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
w25x16 SPI FLASH读写 串口监测输出STC8A8K单片机KEIL工程文件源码 FLASH w25x16 2M外部flash测试 W25X16芯片,就是16Mbit 一byte等于8bit 也就是2M字节的存储空间。 256bytes为一页 4Kbytes为一个Sector (扇区) 16个扇区为1个Block (块) W25X16 容量为2M字节,共有32个Block,512个Sector 而且W25X16最小擦除量是一个扇区 即4k字节空间 W25X16擦写周期多达 10W次,具有 20年的数据保存期限, 支持电压为 2.7~3.6V ,最大SPI 时钟可以到80Mhz。 程序上是将一个字符串存到了flash地址100开始的位置,然后去读取存入的数据到数组中,在将读到的 数组数据其显示出来 整个过程由串口检测 主频为11.0592MHz 串口波特率为9600 */ #include "stc8.h" //STC15头文件 #include "def.h" //宏定义 常用函数 #include "delay.h" //延时函数 #include "spi.h" #include "flash.h" #include "uart.h" u8 scan[]={"STC8 FLASH test"}; //测试字符串 u8 buffer[19]; //接收数组 void main() { SP=0X80; //调整堆栈指向 手册286页 详解 Init_SPI(); //SPI初始化 UartInit(); //串口初始化 if(SPI_Flash_ReadID()==0xef14) UartSendStr("外部FLASH初始化成功!\r\n"); else { UartSendStr("外部FLASH初始化失败!\r\n"); while(1); } SPI_Flash_Erase_Sector(0); //擦除地址为0扇区 4k字节 SPI_Flash_Write_NoCheck(scan,100,15); //在地址100位置写入字符串 SPI_Flash_Read(buffer,100,16); //在地址100位置处读取字符串并存入buffer数组中 UartSendStr("地址100数据:\r\n"); UartSend(buffer,15); while(1); }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值