W25Q128(W25Q系列SPI Flahs和W25X系列的SPI Flash)驱动,使用句柄方式,分离底层,便于移植

W25Q128(W25Q系列SPI Flahs和W25X系列的SPI Flash)驱动,使用句柄方式,分离底层,便于移植。

编写一些应用代码,将底层与实际应用进行分离,方便移植使用,具体思路就是讲所需的全局变量,底层与硬件相关的接口集中到结构体中(类似一个句柄,存放所需的变量),这样这个程序就可以重入,并且便于维护移植,比如有多个W25芯片的时候就特别好用,在实际开发过程中,这种情况会经常遇到,这样一次写好,以后到处都能使用。

先上代码W25XXX.c

/*************************************************************************************************************
 * 文件名:		W25XXX.c
 * 功能:		W25XXX SPI flash驱动
 * 作者:		[email protected]
 * 邮箱:		[email protected]
 * 创建时间:	2011年6月30日
 * 最后修改时间:2012年6月2日
 * 详细:		注意:信号量只能防止多个读取与单个写冲突,不能防止多个写冲突(因为写入也要读取),建议不要在多个地方同时进行写入操作,如果需要请另外增加信号量进行保护
				2016-12-03:增加64KB块擦除
				2017-04-14:擦除时增加信号量,防止读写冲突
				2017-08-22:bool W25XXX_WriteAboveOneSector(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);	//增量写入,写SPI FLASH 在指定地址开始写入指定长度的数据(会清除当前地址之后的数据保留之前的数据,只能在一个扇区范围内)
				2017-11-23:当开启了看门狗,会在擦除时进行喂狗,在所有的写使能后添加写禁止,防止误写入数据。
				2018-01-14:分离底层,使用句柄方式访问
*************************************************************************************************************/
#include "SYSTEM.H"
#include "W25XXX.H"


//操作超时
#define W25XXX_TIME_OUT		120*1000		//超时时间,单位ms,120S





//调试开关
#define W25XXX_DBUG	0
#if W25XXX_DBUG
	#include "system.h"
	#define W25XXX_debug(format,...)	uart_printf("W25XXX:"format,##__VA_ARGS__)
#else
	#define W25XXX_debug(format,...)	/\
/
#endif	//W25XXX_DBUG



//页 256字节
//4KB为一个扇区Sector	   扇区擦除
//16个扇区为1个块区Block
//W25X16
//容量为2M字节,共有32个块区Block,512个扇区Sector 

u8 W25QXX_ReadSR2(W25XXX_HANDLE *pHandle);					//读取W25QXX 状态2
u8 W25QXX_ReadSR3(W25XXX_HANDLE *pHandle);					//读取W25QXX 状态3
u8 W25XXX_ReadSR(W25XXX_HANDLE *pHandle);					//读取W25X16 状态
bool W25XXX_WaitBusy(W25XXX_HANDLE *pHandle);				//等待W25X16空闲
void W25XXX_WriteSR(W25XXX_HANDLE *pHandle, u8 sr);			//写状态寄存器
void W25QXX_WriteSR2(W25XXX_HANDLE *pHandle, u8 sr);		//写状态寄存器2
u32 W25XXX_ReadStatus(W25XXX_HANDLE *pHandle);				//写状态寄存器3
void W25XXX_WriteProtect(W25XXX_HANDLE *pHandle,bool EN);	//使能或失能存储区写保护
void W25XXX_WriteEnable(W25XXX_HANDLE *pHandle);			//写使能
void W25XXX_WriteDisable(W25XXX_HANDLE *pHandle);			//写失能
bool W25XXX_WritePage(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//在一页(0~65535)内写入少于256个字节的数据,在指定地址开始写入最大256字节的数据



/*************************************************************************************************************************
* 函数			:	W25XXX_ID 	W25XXX_Init(W25XXX_HANDLE *pHandle, u8 (*SPI_ReadWrtieByte)(u8 data),void (*SPI_SetCS)(u8 CS_Level),void (*DelayMs)(u8 ms),void (*MutexPen)(void),void (*MutexPost)(void),void (*ClearWatchdog)(void))
* 功能			:	初始化W25X16 句柄
* 参数			:		pHandle:句柄;
						SPI_ReadWrtieByte:			//SPI读写接口
						SPI_SetCS:					//SPI片选控制接口
						DelayMs:					//系统ms延时接口
						MutexPen:					//申请信号量接口
						MutexPost:					//释放信号量接口
						ClearWatchdog:				//清除看门狗接口
* 返回			:	W25XXX_ID
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20178-01-14
* 最后修改时间 	: 	20178-01-14
* 说明			: 	用于初始化W25XXX芯片句柄
*************************************************************************************************************************/ 
W25XXX_ID 	W25XXX_Init(W25XXX_HANDLE *pHandle, u8 (*SPI_ReadWrtieByte)(u8 data),void (*SPI_SetCS)(u8 CS_Level),void (*DelayMs)(u8 ms),void (*MutexPen)(void),void (*MutexPost)(void),void (*ClearWatchdog)(void))
{
	u16 ID;
	
	//进行句柄的初始化
	pHandle->SPI_ReadWrtieByte = SPI_ReadWrtieByte;		//SPI读写接口
	pHandle->SPI_SetCS = SPI_SetCS;						//SPI片选控制接口
	pHandle->DelayMs = DelayMs;							//系统ms延时接口
	pHandle->MutexPen = MutexPen;						//申请信号量接口
	pHandle->MutexPost = MutexPost;						//释放信号量接口
	pHandle->ClearWatchdog = ClearWatchdog;				//清除看门狗接口
	
	pHandle->SPI_SetCS(1);								//片选无效,在单片机中一般不要对必须的函数指针或内存进行合法判断,如果没有初始化直接进入异常,否则不好调试问题
	pHandle->DelayMs(10);
	ID = W25XXX_ReadID(pHandle);						//通过读取ID判断是否初始化成功
	switch(ID)
	{
		case W25X16_ID:
		{
			W25XXX_debug("读取W25X16 ID成功!(0x%04X)\r\n",ID);
			pHandle->Model= W25X16;
		}break;
		case W25X32_ID:
		{
			W25XXX_debug("读取W25X32 ID成功!(0x%04X)\r\n",ID);
			pHandle->Model= W25X32;
		}break;
		case W25X64_ID:
		{
			W25XXX_debug("读取W25X64 ID成功!(0x%04X)\r\n",ID);
			pHandle->Model= W25X64;
		}break;
		case W25X128_ID:
		{
			W25XXX_debug("读取W25X128 ID成功!(0x%04X)\r\n",ID);
			pHandle->Model= W25X128;
		}break;
		case W25X256_ID:
		{
			W25XXX_debug("读取W25X256 ID成功!(0x%04X)\r\n",ID);
			pHandle->Model= W25X256;
		}break;
		default:
		{
			W25XXX_debug("初始化W25XXX 失败!(0x%04X)\r\n",ID);
			pHandle->Model= FLASH_NULL;
		}break;
	}
	
	return pHandle->Model;
}





/*************************************************************************************************************************
* 函数			:	u32 W25QXX_ReadJEDEC_ID(W25XXX_HANDLE *pHandle)
* 功能			:	读取W25QXX JEDEC ID
* 参数			:	pHandle:句柄
* 返回			:	ID
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	2017-11-23
* 最后修改时间 	: 	2018-01-14
* 说明			: 	
*************************************************************************************************************************/
u32 W25QXX_ReadJEDEC_ID(W25XXX_HANDLE *pHandle)
{
	u32 Temp = 0;	
	
	if(pHandle->MutexPen!=NULL)pHandle->MutexPen();			//申请信号量	
	pHandle->SPI_SetCS(0);				    
	pHandle->SPI_ReadWrtieByte(0x9F);				//发送读取 JEDEC ID命令	    
	Temp |= pHandle->SPI_ReadWrtieByte(0XFF); 
	Temp <<= 8;
	Temp |= pHandle->SPI_ReadWrtieByte(0XFF); 
	Temp <<= 8;	
	Temp |= pHandle->SPI_ReadWrtieByte(0XFF); 	 			   
	pHandle->SPI_SetCS(1);
	if(pHandle->MutexPost!=NULL)pHandle->MutexPost();		//释放信号量

	return Temp;
}


/*************************************************************************************************************************
* 函数			:	u8 W25QXX_ReadSR2(W25XXX_HANDLE *pHandle) 
* 功能			:	读取W25QXX 状态2
* 参数			:	pHandle:句柄
* 返回			:	
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	2017-11-23
* 最后修改时间 	:	2018-01-14
* 说明			: 	读取状态标志
*************************************************************************************************************************/ 
u8 W25QXX_ReadSR2(W25XXX_HANDLE *pHandle) 
{  
	u8 byte = 0;   
	pHandle->SPI_SetCS(0);                            //使能器件   
	pHandle->SPI_ReadWrtieByte(0X35);  				//0x35发送读取状态寄存器命令    
	byte = pHandle->SPI_ReadWrtieByte(0xff);           //读取一个字节  
	pHandle->SPI_SetCS(1);                            //取消片选     
	return byte;   
}



/*************************************************************************************************************************
* 函数			:	u8 W25QXX_ReadSR3(W25XXX_HANDLE *pHandle) 
* 功能			:	读取W25QXX 状态3
* 参数			:	pHandle:句柄
* 返回			:	
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	2017-11-23
* 最后修改时间 	:	2018-01-14
* 说明			: 	读取状态标志
*************************************************************************************************************************/ 
u8 W25QXX_ReadSR3(W25XXX_HANDLE *pHandle) 
{  
	u8 byte = 0;   
	pHandle->SPI_SetCS(0);                            //使能器件   
	pHandle->SPI_ReadWrtieByte(0X15);  				//0x15发送读取状态寄存器命令    
	byte = pHandle->SPI_ReadWrtieByte(0xff);           //读取一个字节  
	pHandle->SPI_SetCS(1);                            //取消片选     
	return byte;   
}




/*************************************************************************************************************************
* 函数			:	u8 W25XXX_ReadSR(W25XXX_HANDLE *pHandle) 
* 功能			:	读取W25X16 状态
* 参数			:	pHandle:句柄
* 返回			:	BIT	7	6   5   4   3   2   1   0
					SPR  RV  TB BP2 BP1 BP0 WEL BUSY
					SPR:默认0,状态寄存器保护位,配合WP使用
					TB,BP2,BP1,BP0:FLASH区域写保护设置
					WEL:写使能锁定
					BUSY:忙标记位(1,忙;0,空闲)
					默认:0x00
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	读取状态标志
*************************************************************************************************************************/ 
u8 W25XXX_ReadSR(W25XXX_HANDLE *pHandle) 
{  
	u8 byte = 0;   
	pHandle->SPI_SetCS(0);                            //使能器件   
	pHandle->SPI_ReadWrtieByte(W25X_ReadStatusReg);    //0x05发送读取状态寄存器命令    
	byte = pHandle->SPI_ReadWrtieByte(0xff);           //读取一个字节  
	pHandle->SPI_SetCS(1);                            //取消片选     
	return byte;   
}


/*************************************************************************************************************************
* 函数			:	bool W25XXX_WaitBusy(void) 
* 功能			:	等待W25X16空闲
* 参数			:	pHandle:句柄
* 返回			:	TRUE:成功;FALSE:超时
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间	: 	2018-01-14
* 说明			: 	2014-08-20:添加超时处理
*************************************************************************************************************************/   
bool W25XXX_WaitBusy(W25XXX_HANDLE *pHandle) 
{   
	u32 TimeOut = W25XXX_TIME_OUT;
	
	while (((W25XXX_ReadSR(pHandle)&0x01)==0x01) && (TimeOut--))			//等待BUSY位清空
	{
		pHandle->DelayMs(1);
		if(pHandle->ClearWatchdog!=NULL) pHandle->ClearWatchdog();	//清除看门狗
	}
	if(TimeOut == 0) 
	{
		W25XXX_debug("操作超时!\r\n");
		return FALSE;
	}
	else return TRUE;
} 



/*************************************************************************************************************************
* 函数			:	void W25XXX_WriteSR(W25XXX_HANDLE *pHandle, u8 sr) 
* 功能			:	写W25X16的状态寄存器
* 参数			:	pHandle:句柄,状态值 ;只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
* 返回			:	无
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
*************************************************************************************************************************/
void W25XXX_WriteSR(W25XXX_HANDLE *pHandle, u8 sr)  
{   
	pHandle->SPI_SetCS(0);                           	 	//使能器件   
	pHandle->SPI_ReadWrtieByte(W25X_WriteStatusReg);   		//发送写取状态寄存器命令    
	pHandle->SPI_ReadWrtieByte(sr);               			//写入一个字节  
	pHandle->SPI_SetCS(1);                           		//取消片选 
}


/*************************************************************************************************************************
* 函数			:	void W25QXX_WriteSR2(W25XXX_HANDLE *pHandle, u8 sr)  
* 功能			:	写W25X16的状态寄存器2
* 参数			:	pHandle:句柄,状态值 ;
* 返回			:	无
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	2017-11-23
* 最后修改时间 	: 	2018-01-14
* 说明			: 	
*************************************************************************************************************************/
void W25QXX_WriteSR2(W25XXX_HANDLE *pHandle, u8 sr)  
{   
	pHandle->SPI_SetCS(0);                            		//使能器件   
	pHandle->SPI_ReadWrtieByte(W25QXX_WRITE_STATUS2);   	//发送写取状态寄存器命令    
	pHandle->SPI_ReadWrtieByte(sr);               			//写入一个字节  
	pHandle->SPI_SetCS(1);                            		//取消片选  
}



/*************************************************************************************************************************
* 函数			:	u32 W25XXX_ReadStatus(W25XXX_HANDLE *pHandle) 
* 功能			:	读取W25X16 状态(3个状态寄存器)
* 参数			:	pHandle:句柄
* 返回			:	状态
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	2017-11-23
* 最后修改时间 	: 	2018-01-14
* 说明			: 	读取状态标志
*************************************************************************************************************************/ 
u32 W25XXX_ReadStatus(W25XXX_HANDLE *pHandle) 
{  
	u32 temp;
	
	temp = W25QXX_ReadSR3(pHandle);
	temp <<= 8;
	temp |= W25QXX_ReadSR2(pHandle);
	temp <<= 8;
	temp |= W25XXX_ReadSR(pHandle);
	
	return temp;
}


/*************************************************************************************************************************
* 函数			:	void W25XXX_WriteProtect(W25XXX_HANDLE *pHandle,bool EN)
* 功能			:	使能或失能W25X16存储区写保护
* 参数			:	pHandle:句柄,EN:ENABLE,写保护使能,否则失能
* 返回			:	无
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	无
*************************************************************************************************************************/
void W25XXX_WriteProtect(W25XXX_HANDLE *pHandle,bool EN)
{
	W25XXX_WriteEnable(pHandle);		//写使能 
    if(EN)
		 W25XXX_WriteSR(pHandle, 0x3c);	//存储区保护
	else
		W25XXX_WriteSR(pHandle,0x00);	//取消存储区保护
	W25XXX_WriteDisable(pHandle);		//写禁止   
}



/*************************************************************************************************************************
* 函数			:	void W25XXX_WriteEnable(W25XXX_HANDLE *pHandle)
* 功能			:	W25X16写使能
* 参数			:	pHandle:句柄
* 返回			:	无
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	将WEL置位,使能W25X16写数据
*************************************************************************************************************************/
void W25XXX_WriteEnable(W25XXX_HANDLE *pHandle)
{
	pHandle->SPI_SetCS(0);  							//使能器件                           
    pHandle->SPI_ReadWrtieByte(W25X_WriteEnable);      //发送写使能  
	pHandle->SPI_SetCS(1);                            //取消片选     	      
}


/*************************************************************************************************************************
* 函数			:	void W25XXX_WriteDisable(W25XXX_HANDLE *pHandle)
* 功能			:	W25X16写失能
* 参数			:	pHandle:句柄
* 返回			:	无
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	将WEL清零 ,失能W25X16写数据
*************************************************************************************************************************/
void W25XXX_WriteDisable(W25XXX_HANDLE *pHandle)
{  
	pHandle->SPI_SetCS(0);                            //使能器件   
    pHandle->SPI_ReadWrtieByte(W25X_WriteDisable);     //发送写禁止指令    
	pHandle->SPI_SetCS(1);                            //取消片选     	      
}

 


/*************************************************************************************************************************
* 函数			:	u16 W25XXX_ReadID(W25XXX_HANDLE *pHandle)
* 功能			:	读取W25X16 ID
* 参数			:	pHandle:句柄
* 返回			:	ID,	0xEF14
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	W25X16的ID:0xEF14
*************************************************************************************************************************/
u16 W25XXX_ReadID(W25XXX_HANDLE *pHandle)
{
	u16 Temp = 0;	

	if(pHandle->MutexPen!=NULL)pHandle->MutexPen();			//申请信号量	
	pHandle->SPI_SetCS(0);				    
	pHandle->SPI_ReadWrtieByte(0x90);//发送读取ID命令	    
	pHandle->SPI_ReadWrtieByte(0x00); 	    
	pHandle->SPI_ReadWrtieByte(0x00); 	    
	pHandle->SPI_ReadWrtieByte(0x00); 	 			   
	Temp|=pHandle->SPI_ReadWrtieByte(0xFF) << 8;  
	Temp|=pHandle->SPI_ReadWrtieByte(0xFF);	 
	pHandle->SPI_SetCS(1);	
	if(pHandle->MutexPost!=NULL)pHandle->MutexPost();		//释放信号量
	
	return Temp;
}



/*************************************************************************************************************************
* 函数			:	void W25XXX_Read(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
* 功能			:	从W25X16 在指定地址开始读取指定长度的数据
* 参数			:	pHandle:句柄,pBuffer:数据缓冲区
					ReadAddr:起始地址,24bit
					NumByteToRead:要读取的字节数(最大65535)
* 返回			:	无
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	无
*************************************************************************************************************************/
void W25XXX_Read(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 ReadAddr,u16 NumByteToRead) 
{ 
 	u16 i;
	
	if(pHandle->MutexPen!=NULL)pHandle->MutexPen();			//申请信号量
	
	pHandle->SPI_SetCS(0);                            		//使能器件   
    pHandle->SPI_ReadWrtieByte(W25X_ReadData);         		//发送读取命令   
    pHandle->SPI_ReadWrtieByte((u8)((ReadAddr) >> 16));  	//发送24bit地址    
    pHandle->SPI_ReadWrtieByte((u8)((ReadAddr) >> 8));   
    pHandle->SPI_ReadWrtieByte((u8)ReadAddr);   
    for(i=0;i<NumByteToRead;i++)
	{ 
        pBuffer[i] = pHandle->SPI_ReadWrtieByte(0xff);   	//循环读数  
    }
	pHandle->SPI_SetCS(1);                            		//取消片选 
	
	if(pHandle->MutexPost!=NULL)pHandle->MutexPost();		//释放信号量
}


/*************************************************************************************************************************
* 函数			:	bool W25XXX_WritePage(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
* 功能			:	SPI在一页(0~65535)内写入少于256个字节的数据,在指定地址开始写入最大256字节的数据
* 参数			:	pHandle:句柄,pBuffer:数据缓冲区
					WriteAddr:起始地址,24bit
					NumByteToWrite:要写的字节数(最大256)
* 返回			:	TRUE:成功;FALSE:失败,超时
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!	
*************************************************************************************************************************/ 
bool W25XXX_WritePage(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
 	u16 i; 
	bool status;
	
	if(pHandle->MutexPen!=NULL)pHandle->MutexPen();			//申请信号量
	
    W25XXX_WriteEnable(pHandle);                  			//SET WEL 
	pHandle->SPI_SetCS(0);                            		//使能器件   
    pHandle->SPI_ReadWrtieByte(W25X_PageProgram);      		//发送写页命令   
    pHandle->SPI_ReadWrtieByte((u8)((WriteAddr) >> 16)); 	//发送24bit地址    
    pHandle->SPI_ReadWrtieByte((u8)((WriteAddr) >> 8));   
    pHandle->SPI_ReadWrtieByte((u8)WriteAddr);   
    for(i = 0;i < NumByteToWrite;i ++)
		pHandle->SPI_ReadWrtieByte(pBuffer[i]);				//循环写数  
	pHandle->SPI_SetCS(1);                            		//取消片选 
	status = W25XXX_WaitBusy(pHandle);						//等待写入结束
	W25XXX_WriteDisable(pHandle);							//写禁止 
	if(pHandle->MutexPost!=NULL)pHandle->MutexPost();		//释放信号量
	
	return status;
}



/*************************************************************************************************************************
* 函数			:	bool W25XXX_EraseSector(W25XXX_HANDLE *pHandle,u32 Dst_Addr)  
* 功能			:	擦除一个扇区
* 参数			:	pHandle:句柄,Dst_Addr:扇区地址 0~511 for w25x16
* 返回			:	TRUE:成功;FALSE:失败,超时
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	擦除一个扇区的最少时间:150ms
*************************************************************************************************************************/ 
bool W25XXX_EraseSector(W25XXX_HANDLE *pHandle,u32 Dst_Addr)  
{   
	bool status;
	
	Dst_Addr *= 4096;						   			//计算扇区起始地址
	if(pHandle->MutexPen!=NULL)pHandle->MutexPen();		//申请信号量

    W25XXX_WriteEnable(pHandle);                  		//写使能	 
    if(W25XXX_WaitBusy(pHandle) == FALSE)   			//检测忙信号
	{
		W25XXX_WriteDisable(pHandle);					//写禁止 
		if(pHandle->MutexPost!=NULL)pHandle->MutexPost();	//释放信号量
		return FALSE;
	}
  	pHandle->SPI_SetCS(0);                            	//使能器件   
    pHandle->SPI_ReadWrtieByte(W25X_SectorErase);      	//发送扇区擦除指令 
    pHandle->SPI_ReadWrtieByte((u8)((Dst_Addr) >> 16));  //发送24bit地址    
    pHandle->SPI_ReadWrtieByte((u8)((Dst_Addr) >> 8));   
    pHandle->SPI_ReadWrtieByte((u8)Dst_Addr);  
	pHandle->SPI_SetCS(1);                            	//取消片选     	      
    status = W25XXX_WaitBusy(pHandle);   				  //等待擦除完成
	W25XXX_WriteDisable(pHandle);						//写禁止 
	if(pHandle->MutexPost!=NULL)pHandle->MutexPost();	//释放信号量
	return status;
} 


/*************************************************************************************************************************
* 函数			:	bool W25XXX_EraseChip(W25XXX_HANDLE *pHandle) 
* 功能			:	擦除整个芯片,格式化
* 参数			:	pHandle:句柄
* 返回			:	TRUE:成功;FALSE:失败,超时
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	整片擦除时间:
					W25X16:25s 
					W25X32:40s 
					W25X64:40s 
					等待时间超长...
*************************************************************************************************************************/  
bool W25XXX_EraseChip(W25XXX_HANDLE *pHandle)
{            
	bool status;

	if(pHandle->MutexPen!=NULL)pHandle->MutexPen();		//申请信号量
    W25XXX_WriteEnable(pHandle);                  		//写使能
	if(W25XXX_WaitBusy(pHandle)==FALSE)   				//检测忙信号
	{
		W25XXX_WriteDisable(pHandle);					//写禁止 
		if(pHandle->MutexPost!=NULL)pHandle->MutexPost();//释放信号量	
		return FALSE;
	}
  	pHandle->SPI_SetCS(0);                          	//使能器件   
    pHandle->SPI_ReadWrtieByte(W25X_ChipErase);      	//发送片擦除命令  
	pHandle->SPI_SetCS(1);                          	//取消片选     	      
	status = W25XXX_WaitBusy(pHandle);   				//等待芯片擦除结束
	W25XXX_WriteDisable(pHandle);						//写禁止 
	if(pHandle->MutexPost!=NULL)pHandle->MutexPost();	//释放信号量
	
	return status;
}



/*************************************************************************************************************************
* 函数			:	bool W25XXX_WriteNoCheck(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite) 
* 功能			:	无检验写W25XXX
* 参数			:	pHandle:句柄,pBuffer	:数据存储区
					WriteAddr:开始写入的地址(24bit)
					NumByteToWrite:要写入的字节数(最大65535)
* 返回			:	TRUE:成功;FALSE:失败,超时
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	必须确保所写的地址范围内的数据全部为0xFF,否则在非0xFF处写入的数据将失败! 
					在指定地址开始写入指定长度的数据,但是要确保地址不越界!
					具有自动换页功能 
*************************************************************************************************************************/ 
bool W25XXX_WriteNoCheck(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)  
{ 			 	
	u16 pageremain;	   
	pageremain = 256 - WriteAddr % 256; 			//单页剩余的字节数		 	    
	if(NumByteToWrite <= pageremain)
		pageremain = NumByteToWrite;				//不大于256个字节
	

	while(1)
	{	   
		if(W25XXX_WritePage(pHandle, pBuffer,WriteAddr,pageremain)==FALSE) 
		{

			return FALSE;
		}
		if(NumByteToWrite == pageremain)
			break;									//写入结束了
	 	else //NumByteToWrite>pageremain
		{
			pBuffer += pageremain;
			WriteAddr += pageremain;	

			NumByteToWrite -= pageremain;			  //减去已经写入了的字节数
			if(NumByteToWrite > 256)
				pageremain = 256; 					//一次可以写入256个字节
			else 
				pageremain = NumByteToWrite; 	  //不够256个字节了
		}
	}
	
	return TRUE;
}



/*************************************************************************************************************************
* 函数			:	bool W25XXX_Write(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)	
* 功能			:	写SPI FLASH 在指定地址开始写入指定长度的数据
* 参数			:	pHandle:句柄,pBuffer	:数据存储区
					WriteAddr:开始写入的地址(24bit)
					NumByteToWrite:要写入的字节数(最大65535)
* 返回			:	无
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	该函数带擦除操作!
					该函数需要的堆栈很大,注意
*************************************************************************************************************************/ 	 
bool W25XXX_Write(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)	 					    
{ 
	u32 secpos;
	u16 secoff;
	u16 secremain;	   
 	u16 i; 

	secpos = WriteAddr / 4096;									//扇区地址 0~511 for w25x16
	secoff = WriteAddr % 4096;									//在扇区内的偏移
	secremain = 4096 - secoff;									//扇区剩余空间大小   

	if(NumByteToWrite <= secremain)
		secremain = NumByteToWrite;								//不大于4096个字节
	
	while(1) 
	{	
		W25XXX_Read(pHandle,pHandle->SwapBuff,secpos*4096,4096);//读出整个扇区的内容
		for(i = 0;i < secremain;i ++)							//校验数据
		{
			if(pHandle->SwapBuff[secoff+i] != 0xff)break;		//需要擦除  	  
		}
		if(i < secremain)										//需要擦除
		{
			if(W25XXX_EraseSector(pHandle,secpos)==FALSE)		//擦除这个扇区
			{
				return FALSE;
			}
			for(i = 0;i < secremain;i ++)	   					//复制
			{
				pHandle->SwapBuff[i + secoff] = pBuffer[i];	  
			}
			if(W25XXX_WriteNoCheck(pHandle,pHandle->SwapBuff,secpos*4096,4096)==FALSE)//写入整个扇区  
			{
				return FALSE;
			}
		}
		else
		{
			if(W25XXX_WriteNoCheck(pHandle,pBuffer,WriteAddr,secremain)==FALSE)//写已经擦除了的,直接写入扇区剩余区间.
			{
				return FALSE;
			}
				
		}
							   
		if(NumByteToWrite==secremain) break;				//写入结束了
		else//写入未结束
		{
			secpos ++;										//扇区地址增1
			secoff = 0;										//偏移位置为0 	 

		   	pBuffer += secremain;  							//指针偏移
			WriteAddr += secremain;							//写地址偏移	   
		   	NumByteToWrite -= secremain;					//字节数递减
			if(NumByteToWrite > 4096)
				secremain = 4096;							//下一个扇区还是写不完
			else 
				secremain = NumByteToWrite;					//下一个扇区可以写完了
		}	 
	}
	
	return TRUE;
} 




/*************************************************************************************************************************
* 函数			:	bool W25XXX_WriteAboveOneSector(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)	
* 功能			:	写SPI FLASH 在指定地址开始使用增量方式写入指定长度的数据(保留之前的数据,会清除当前地址之后的数据,只能在一个扇区范围内)
* 参数			:	pHandle:句柄,pBuffer	:数据存储区
					WriteAddr:开始写入的地址(24bit)
					NumByteToWrite:要写入的字节数(最大65535)
* 返回			:	无
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	该函数带擦除操作,不会判断是否需要擦除,会直接执行擦除操作!
					该函数需要的堆栈很大,注意
					只会将当前地址之前的数据进行重写写入,并且写入当前的数据,之后的数据将不会进行还原,不能超出一个扇区范围
*************************************************************************************************************************/ 	 
bool W25XXX_WriteAboveOneSector(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)	 					    
{ 
	u32 secpos;
	u16 secoff;
	u16 secremain;	   
 	u16 i; 

	secpos = WriteAddr / 4096;										//扇区地址 0~511 for w25x16
	secoff = WriteAddr % 4096;										//在扇区内的偏移
	secremain = 4096 - secoff;										//扇区剩余空间大小   

	if(NumByteToWrite > secremain)
		 NumByteToWrite = secremain;								//不大于剩余空间
	
	
	W25XXX_Read(pHandle, pHandle->SwapBuff,secpos*4096, secoff);	//读出需要写入之前的数据

	if(W25XXX_EraseSector(pHandle,secpos)==FALSE)					//擦除这个扇区
	{
		return FALSE;
	}
	for(i = 0;i < NumByteToWrite;i ++)	   							//复制当前要写入的数据
	{
		pHandle->SwapBuff[i + secoff] = pBuffer[i];	  
	}
	if(W25XXX_WriteNoCheck(pHandle,pHandle->SwapBuff,secpos*4096,secoff+NumByteToWrite)==FALSE)//写入数据
	{
		return FALSE;
	}

	return TRUE;
} 


/*************************************************************************************************************************
* 函数			:	void W25XXX_PowerDown(W25XXX_HANDLE *pHandle)
* 功能			:	进入掉电模式
* 参数			:	pHandle:句柄
* 返回			:	无
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	函数不带信号量保护,使用需要注意,调用后将无法读写flash
*************************************************************************************************************************/ 
void W25XXX_PowerDown(W25XXX_HANDLE *pHandle)
{ 
  	pHandle->SPI_SetCS(0);                            //使能器件   
    pHandle->SPI_ReadWrtieByte(W25X_PowerDown);       //发送掉电命令  
	pHandle->SPI_SetCS(1);                            //取消片选                    	 	
}


/*************************************************************************************************************************
* 函数			:	void W25XXX_WAKEUP(W25XXX_HANDLE *pHandle)
* 功能			:	唤醒W25XXX
* 参数			:	pHandle:句柄
* 返回			:	无
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	20110630
* 最后修改时间 	: 	2018-01-14
* 说明			: 	函数不带信号量保护
*************************************************************************************************************************/ 
void W25XXX_WAKEUP(W25XXX_HANDLE *pHandle)   
{                              	 
  	pHandle->SPI_SetCS(0);                            		//使能器件   
    pHandle->SPI_ReadWrtieByte(W25X_ReleasePowerDown);   	// send W25X_PowerDown command 0xAB    
	pHandle->SPI_SetCS(1);                            		//取消片选     	      
    pHandle->DelayMs(1);                              		//等待TRES1
} 




/*************************************************************************************************************************
* 函数			:	bool W25XXX_EraseBlock64KB(W25XXX_HANDLE *pHandle,u32 Block_Addr)  
* 功能			:	擦除一个64KB块
* 参数			:	pHandle:句柄,Block_Addr:块地址,1块=64KB
* 返回			:	TRUE:成功;FALSE:失败,超时
* 依赖			:	底层读写函数
* 作者			:	[email protected]
* 时间			:	2016-12-03
* 最后修改时间 	:	2018-01-14
* 说明			: 	擦除一个的最少时间:150ms
*************************************************************************************************************************/ 
bool W25XXX_EraseBlock64KB(W25XXX_HANDLE *pHandle,u32 Block_Addr)  
{   
	bool status;
	
	if(pHandle->MutexPen!=NULL)pHandle->MutexPen();		//申请信号量
	Block_Addr *= 64*1024;						   		//计算块的起始地址
    W25XXX_WriteEnable(pHandle);                  		//写使能	 
    if(W25XXX_WaitBusy(pHandle) == FALSE)   			//检测忙信号
	{
		W25XXX_WriteDisable(pHandle);					//写禁止 
		if(pHandle->MutexPost!=NULL)pHandle->MutexPost();//释放信号量
		return FALSE;
	}
  	pHandle->SPI_SetCS(0);                            	//使能器件   
    pHandle->SPI_ReadWrtieByte(W25X_BlockErase);      	//发送64KB块擦除指令 
    pHandle->SPI_ReadWrtieByte((u8)((Block_Addr) >> 16)); //发送24bit地址    
    pHandle->SPI_ReadWrtieByte((u8)((Block_Addr) >> 8));   
    pHandle->SPI_ReadWrtieByte((u8)Block_Addr);  
	pHandle->SPI_SetCS(1);                            	//取消片选     	      
    status = W25XXX_WaitBusy(pHandle);   				//等待擦除完成
	W25XXX_WriteDisable(pHandle);						//写禁止 
	if(pHandle->MutexPost!=NULL)pHandle->MutexPost();	//释放信号量
	
	return status;
} 

//W25XXX.h

/*************************************************************************************************************
 * 文件名:		W25XXX.c
 * 功能:		W25XXX SPI flash驱动
 * 作者:		[email protected]
 * 邮箱:		[email protected]
 * 创建时间:	2011年6月30日
 * 最后修改时间:2012年6月2日
 * 详细:		注意:信号量只能防止多个读取与单个写冲突,不能防止多个写冲突(因为写入也要读取),建议不要在多个地方同时进行写入操作,如果需要请另外增加信号量进行保护
				2016-12-03:增加64KB块擦除
				2017-04-14:擦除时增加信号量,防止读写冲突
				2017-08-22:bool W25XXX_WriteAboveOneSector(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);	//增量写入,写SPI FLASH 在指定地址开始写入指定长度的数据(会清除当前地址之后的数据保留之前的数据,只能在一个扇区范围内)
				2017-11-23:当开启了看门狗,会在擦除时进行喂狗,在所有的写使能后添加写禁止,防止误写入数据。
				2018-01-14:分离底层,使用句柄方式访问
*************************************************************************************************************/
#ifndef __W25XXX_H_
#define __W25XXX_H_
#include "system.h"

//W25XXX ID定义
#define W25X16_ID 0XEF14
#define W25X32_ID 0XEF15
#define W25X64_ID 0XEF16
#define W25X128_ID 0XEF17
#define W25X256_ID 0X4019

//W25XXX类型
typedef enum
{
	W25X16 = 0,				//W25X16
	W25X32 = 1,				//W25X32
	W25X64 = 2,				//W25X64
	W25X128 = 3,			//W25Q128
	W25X256 = 4,			//W25Q256
	FLASH_NULL = 0XFF,		//无效
}W25XXX_ID;



//W25XXX系列芯片句柄
typedef struct
{
	u8 (*SPI_ReadWrtieByte)(u8 data);		//底层SPI接口,读写一个字节
	void (*SPI_SetCS)(u8 CS_Level);			//片选控制
	void (*DelayMs)(u8 ms);					//延时,毫秒
	void (*MutexPen)(void);					//申请并等待信号量-如果为空,将不使用
	void (*MutexPost)(void);				//释放信号量-如果为空,将不使用
	void (*ClearWatchdog)(void);			//清除看门狗-如果为空,将不使用
	W25XXX_ID Model;						//芯片型号
	u8 SwapBuff[4096];						//一个扇区大小的交换缓冲区(用于写数据擦除时临时备份),当然你可以修改为指针方式,共用其他位置的缓冲区,达到节省内存的目的,仅仅用于W25XXX_Write
}W25XXX_HANDLE;




//相关接口函数
//初始化W25X16 句柄
W25XXX_ID 	W25XXX_Init(W25XXX_HANDLE *pHandle, u8 (*SPI_ReadWrtieByte)(u8 data),void (*SPI_SetCS)(u8 CS_Level),void (*DelayMs)(u8 ms),void (*MutexPen)(void),void (*MutexPost)(void),void (*ClearWatchdog)(void));
u32 W25QXX_ReadJEDEC_ID(W25XXX_HANDLE *pHandle);									//读取W25QXX JEDEC ID
u16 W25XXX_ReadID(W25XXX_HANDLE *pHandle);											//读取W25X16 ID
void W25XXX_Read(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);//在指定地址开始读取指定长度的数据
bool W25XXX_EraseSector(W25XXX_HANDLE *pHandle,u32 Dst_Addr);						//擦除一个扇区
bool W25XXX_EraseBlock64KB(W25XXX_HANDLE *pHandle,u32 Block_Addr) ;					//擦除一个64KB块
bool W25XXX_EraseChip(W25XXX_HANDLE *pHandle);										//擦除整个芯片,格式化
bool W25XXX_WriteNoCheck(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);			//无检验写W25XXX
bool W25XXX_Write(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);					//写SPI FLASH 在指定地址开始写入指定长度的数据
bool W25XXX_WriteAboveOneSector(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);	//写SPI FLASH 在指定地址开始使用增量方式写入指定长度的数据(保留之前的数据,会清除当前地址之后的数据,只能在一个扇区范围内)
void W25XXX_PowerDown(W25XXX_HANDLE *pHandle);										//进入掉电模式
void W25XXX_WAKEUP(W25XXX_HANDLE *pHandle)  ;										//唤醒W25XXX



//指令表
#define W25X_WriteEnable		0x06 
#define W25X_WriteDisable		0x04 
#define W25X_ReadStatusReg		0x05 
#define W25X_WriteStatusReg		0x01 
#define W25X_ReadData			0x03 
#define W25X_FastReadData		0x0B 
#define W25X_FastReadDual		0x3B 
#define W25X_PageProgram		0x02 
#define W25X_BlockErase			0xD8 
#define W25X_SectorErase		0x20 
#define W25X_ChipErase			0xC7 
#define W25X_PowerDown			0xB9 
#define W25X_ReleasePowerDown	0xAB 
#define W25X_DeviceID			0xAB 
#define W25X_ManufactDeviceID	0x90 
#define W25X_JedecDeviceID		0x9F 
#define W25QXX_JedecDeviceID	0x9F 
#define W25QXX_QPI_Mode			0x38
#define W25QXX_EnableReset		0x66
#define W25QXX_Reset			0x99
#define W25QXX_EXIT_QPI_MODE	0xFF
#define W25QXX_WRITE_STATUS2	0x31
#define W25QXX_WRITE_STATUS3	0x11




#endif //__W25XXX_H_

//初始化

W25X16_HardwaveInit();						//初始化W25X16底层硬件SPI接口
	W25X16_OSMutexCreate();						//W25X16信号量初始化-需要在任务中进行初始化
	g_SysFlag.FlashId = W25XXX_Init(&g_W25X16_Handle, 
			BI_W25_SPI_ReadWrtieByte,			//W25XXX SPI通信接口
			BI_W25_SPI_SetCS,					//W25XXX SPI片选控制接口
			BI_OS_DelayMs,						//系统ms延时
			BI_W25_MutexPen,					//W25XXX 信号量申请
			BI_W25_MutexPost,					//W25XXX 信号量释放
			BI_OS_ClearWatchdog					//系统看门狗清除
		);

//相关接口

/////////////////////////////////////////////////////////////////////////////////////////////
//W25X64 FLASH支持						
//SPI接口设置
#define SPI_FLASH_CS					PGout(8)		//W25X16片选
#define W25X16_SPI_CH					SPI_CH1
//W26X16 硬件接口初始化
void __inline W25X16_HardwaveInit(void)		
{
	SPIx_Init(W25X16_SPI_CH, &SPI_DEFAULT_01, SPI_1_256);//初始化SPI
	SYS_DeviceClockEnable(DEV_GPIOG, TRUE);
	SYS_GPIOx_OneInit(GPIOG, 8,OUT_PP, SPEED_50M);
}
void W25X16_OSMutexCreate(void);						//W25X16信号量初始化-需要在任务中进行初始化

/////////////////////////////////////////////////////////////////////////////////////////////
//信号量定义-信号量也会占用优先级的
#define W25X16_SEMP		1		//W25X16信号量
//信号量初始化-需要在任务中进行初始化
void W25X16_OSMutexCreate(void)
{
	INT8U err;
	
	g_SysFlag.w25x16_semp = OSMutexCreate(W25X16_SEMP, &err);							//注册W25X16信号量
	if(g_SysFlag.w25x16_semp == NULL)
	{
		DEBUG("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxW25X16信号量初始化失败%dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n",err);
	}
}
/***********************W25X16相关接口************************/
//W25XXX SPI通信接口
u8 BI_W25_SPI_ReadWrtieByte(u8 data)
{
	return SPIx_ReadWriteByte(W25X16_SPI_CH, data);
}

//W25XXX SPI片选控制接口
void BI_W25_SPI_SetCS(u8 CS_Level)
{
	SPI_FLASH_CS = CS_Level;
}
	

//W25XXX 信号量申请
void BI_W25_MutexPen(void)
{
	INT8U err;
	
	OSMutexPend(g_SysFlag.w25x16_semp, 0, &err);		//申请W25X16信号量
}


//W25XXX 信号量释放
void BI_W25_MutexPost(void)
{
	OSMutexPost(g_SysFlag.w25x16_semp);				//释放W25X16信号量
}


//系统看门狗清除
void BI_OS_ClearWatchdog(void)
{
	IWDG_Feed();
}


//系统ms延时
void BI_OS_DelayMs(u8 ms)
{
	OSTimeDlyHMSM(0,0,0,ms);
}





猜你喜欢

转载自blog.csdn.net/cp1300/article/details/79055864