基于STM32+SPI+FLASH芯片跨页读写擦除的实现

前言     


        本次我们学习一下STM32F103关于SPI对存储芯片的读写,针对flash芯片不能跨页写的限制,写一个扇区写入的函数,不需要自己手动跨页,使用更方便,为字库,图片烧入做铺垫。

本篇博客大部分是自己收集和整理,如有侵权请联系我删除。

本次实验板子使用的是正点原子精英版,芯片是STM32F103ZET6,需要资料可以@我拿取。

交流群:717237739

如果觉得有用点赞关注收藏三连,多谢支持

本博客内容原创,创作不易,转载请注明

没有初步认识SPI协议的,可以先看看我之前的博客:SPI协议介绍
没有初步认识W25QXX芯片的,可以先看看我之前的博客:w25QXX芯片读写函数

FLASH 驱动手册自取

一. FLASH 跨页读写,分区和注意事项

1. 内部区域写入划分等级

W25Q16,W25Q32 ,W25Q64 系列的FLASH存储器分别有8192,16384,32768可编程页,每页256个字节。芯片内部分为了块,扇区,页的编程指令和擦除指令,下面来看看他们之间的关系。
内部区分是从 块 -> 扇区 -> 页->页地址字节写入

2.FLASH 注意事项

写入操作解读:

1.写使能的操作:相当于FLASH芯片做的保护措施,防止误操作写入其他数据,意义和手机的解锁差不多,其实芯片内部的FLASH也是需要解锁关锁操作的,芯片类型都一样。

2.每个数据位只能由1改为0,是因为FLASH芯片自身的限制决定,它没有完全任意修改的能力,所以芯片内部无数据的时候默认为0XFF,表示为空。

3.针对第二个限制,所以我们在每次写入flash的时候,都是需要保证芯片内部是0XFF,就需要先擦除在写入,这样数据就不会出错了。

4.擦除不能针对指定地址擦除,在W25QXX芯片介绍中就有说明,最小只能扇区擦除,所以每次都是需要擦除最小4K的扇区。

5.每次写入最多写入255个字节,芯片规定不能跨页写,写入的数据超过之后就会重新从该页的首地址写入,数据就会出错,针对这一现象等会我们分解数据进行写入。

6.写入结束之后,芯片会进入busy状态,在这个状态下不能进行读写操作。每次写完之后我们都可以判断一下这个标志位,防止出错。

7.busy状态位下,不能进行读写,此处该注意。

SPI读写函数:(可以查看之前的文章)

//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{		
	u8 retry=0;				 	
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
		{
		retry++;
		if(retry>200)return 0;
		}			  
	SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据
	retry=0;

	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
		{
		retry++;
		if(retry>200)return 0;
		}	  						    
	return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据					    
}

二. FLASH 芯片ID 读取

读取芯片ID的意义是什么?

1.首先识别芯片的型号:不同型号的 Flash 存储器可能有不同的特性,包括容量、擦写速度、寿命等。这有助于系统在运行时适应不同型号的 Flash 存储器。

2.适应对应配置:针对不同的 Flash 存储器,可能需要特定的配置参数,如擦写和写入时的超时时间、擦写和写入的命令序列等。通过读取 Flash ID,系统可以在运行时动态配置这些参数,以确保与连接的 Flash 存储器的兼容性。

3.针对固件更新检测:在一些应用中,可能需要进行固件更新。读取 Flash ID 可以帮助系统确认连接的 Flash 存储器是否与新固件兼容。这有助于防止在错误的 Flash 存储器上执行固件更新,从而避免不可逆的系统问题。

4.错误检测和恢复:Flash 存储器可能会受到环境因素或硬件问题的影响,导致数据损坏或存储器出现错误。读取 Flash ID 可以用于检测存储器是否正常工作,并在可能的情况下采取适当的措施,如进行错误检测和修复。

//读取芯片ID
//返回值如下:				   
//0XEF13,表示芯片型号为W25Q80  
//0XEF14,表示芯片型号为W25Q16    
//0XEF15,表示芯片型号为W25Q32  
//0XEF16,表示芯片型号为W25Q64 
//0XEF17,表示芯片型号为W25Q128 	  
u16 W25QXX_ReadID(void)
{
	u16 Temp = 0;	  
	W25QXX_CS=0;				    
	SPI2_ReadWriteByte(0x90);//发送读取ID命令	    
	SPI2_ReadWriteByte(0x00); 	    
	SPI2_ReadWriteByte(0x00); 	    
	SPI2_ReadWriteByte(0x00); 	 			   
	Temp|=SPI2_ReadWriteByte(0xFF)<<8;  
	Temp|=SPI2_ReadWriteByte(0xFF);	 
	W25QXX_CS=1;				    
	return Temp;
}   		

三 . FLASH 扇区读写实现

注意:因为一些写使能,写禁止的一些函数在其他文章已经封装了,这些函数不懂的可以看看主页的W25QXX芯片介绍,这些就不多介绍了,下面主要实现扇区跨页写的实现流程:

1.在指定页写入少于256个字节的数据函数,此处不做擦除,是因为在扇区函数做了擦除,这里如果想单独写入,也可以在写入之前加一个读取地址下的数据判断是否需要擦除。


//无检验写SPI FLASH 
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能 
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
{ 			 		 
	u16 pageremain;	   
	pageremain=256-WriteAddr%256; //单页剩余的字节数	关键先求出这个页剩余的空字节位置	 	    
	if( NumByteToWrite<=pageremain )	//写入的字节小于剩下的字节数
	{
	pageremain=NumByteToWrite;//不大于256个字节,直接获取写入的字节数
	}
	while(1)
	{	   
		W25QXX_Write_Page(pBuffer,WriteAddr,pageremain); //在对应的地址下写入数据,长度不超过该页的终地址

		//上面的写入,如果小于就正常写入,如果大于该页,再做判断是否需要跨页
		if( NumByteToWrite==pageremain )
		{
		break;//写入结束了	直接退出while循环,写入完成
		}
	 	else //NumByteToWrite>pageremain
		{
			pBuffer+=pageremain;		//传入的数据,往写入完成的数据作偏移
			WriteAddr+=pageremain;		//传入的地址,往写入完成的做地址偏移

			NumByteToWrite-=pageremain;			  //减去已经写入了的字节数
			if(NumByteToWrite>256)
			{
				pageremain=256; //一次可以写入256个字节
			}
			else 
			{
				pageremain=NumByteToWrite; 	  //不够256个字节了,就直接等于当前字节数
			}
		}
	}
} 

2.在扇区内连续跨页写入数据,先擦除后写入。

//写SPI FLASH  
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)						
//NumByteToWrite:要写入的字节数(最大65535)   
u8 W25QXX_BUFFER[4096];		 
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
{ 
	u32 secpos;
	u16 secoff;
	u16 secremain;	   
 	u16 i;    
	u8 * W25QXX_BUF;	  
   	W25QXX_BUF=W25QXX_BUFFER;	     	//指向首地址
 	secpos=WriteAddr/4096;//扇区地址,第几个扇区
	secoff=WriteAddr%4096;//在扇区内的偏移地址
	secremain=4096-secoff;//扇区剩余空间大小   
 	//printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
 	if(NumByteToWrite<=secremain)	//不大于4096个字节  写不完一个扇区内的空间大小,即不跨扇区
		{
			secremain=NumByteToWrite;	//假设一个扇区原来已经写了3K的数据,后来再写入0.5K,结果还有0.5K空间没写,
										//这样就不用全部擦除剩余的1K空间,只要擦除要写入的0.5K的空间就可以了
		}
	while(1) 
	{	
		W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容,保存到W25QXX_BUF
		for(i=0;i<secremain;i++)//校验数据,对数组的数据进行遍历
		{
			if(W25QXX_BUF[secoff+i]!=0XFF)break;	//需要擦除,从secoff的位置开始 读出来的内容如果不是0xff,需要擦除,手册上有说明P38	  
		}
		if(i<secremain)//需要擦除	假设写入10个数,而写到第8个的时候,上面的校验出错,那么就要擦除了
		{
			W25QXX_Erase_Sector(secpos);		 //擦除这个扇区,这时候这个扇区的内容已经被存放在W25QXX_BUF,故可以擦除
			for(i=0;i<secremain;i++)	   		//复制
			{
				W25QXX_BUF[i+secoff]=pBuffer[i];	 //之前存放的数据在上面也已经存放到                //W25QXX_BUF中了,所以从  secoff 处开始存放数据(要写入的数据),
	//这样就把之前存放的数据和后来写入的数据都存入W25QXX_BUF中了。 
			}
			W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  

		}else 
	W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写入整个扇区  secpos*4096就是地址				
		
		if(NumByteToWrite==secremain)
		{
		break;//写入结束了		
		}
		else//写入未结束		写满了一个扇区,开始往第二个扇区写数据
		{
			secpos++;//扇区地址增1	 已经是下一个扇区了,所以++
			secoff=0;//偏移位置为0 	 已经是一个新的扇区了,所以为0	 

		   	pBuffer+=secremain;  	//源数据指针偏移	已经写了secremain个数据,所以需要偏移
			WriteAddr+=secremain;				//写地址偏移	   
		   	NumByteToWrite-=secremain;	//字节数递减 
                                        //已经写了secremain个数据 故减去secremain个数据

			if(NumByteToWrite>4096)				//下一个扇区还是写不完
			{
				secremain=4096;					//下一下扇区的内容就是4096
			}
			else 
			{
				secremain=NumByteToWrite;		//下一个扇区可以写完了
			}
		}	 
	}
}

总结:

      以上就是关于SPI对FLASH芯片跨页写入的方法了,难度不是很大,其实就是一种代码逻辑的实现,主要就是注意对各种函数的调用和做一些芯片限制的判断,这样我们就能用一条函数实现我们需要的功能了。下一篇博客主要介绍字库和图片的写入存储,大家如果对我的博客有疑问或者错误,可以@我修改,大家相互交流。

交流群:717237739

如果觉得有用点赞关注收藏三连,多谢支持

本博客内容原创,创作不易,转载请注明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值