Просмотр исходного кода

1.提交新增 SPI FLAS芯片W25QXX驱动文件

libo лет назад: 2
Родитель
Сommit
854334d258
6 измененных файлов с 2326 добавлено и 0 удалено
  1. 484 0
      obj/obj_hal_w25qxx.c
  2. 94 0
      obj/obj_hal_w25qxx.h
  3. 538 0
      obj/obj_soft_w25qxx.c
  4. 100 0
      obj/obj_soft_w25qxx.h
  5. 975 0
      obj/obj_spi_w25qxx.c
  6. 135 0
      obj/obj_spi_w25qxx.h

+ 484 - 0
obj/obj_hal_w25qxx.c

@@ -0,0 +1,484 @@
+
+#include "obj_hal_w25qxx.h"
+#ifdef USE_OBJ_HAL_W25QXX
+#include <stdint.h>
+#include <stdbool.h>
+#include "main.h"
+#include "sys_delay.h"
+#include "dev_spi_w25qxx.h"
+
+#if 0
+#define	W25QXX_CS_GPIO							GPIOD					//GPIOB
+#define	W25QXX_CS_PIN								GPIO_PIN_8		//GPIO_PIN_8
+#else
+#define	W25QXX_CS_GPIO							GPIOB					
+#define	W25QXX_CS_PIN								GPIO_PIN_12		
+#endif
+
+extern void delay_us(uint32_t us);
+
+w25qxx_object_t   w25qxx_obj = 
+{
+	.W25Q_TYPE = W25Q256,				//默认是W25Q256
+	.W25Q_OK 	 = false,
+};
+void W25QXX_Init_info(void);
+
+//4Kbytes为一个Sector
+//16个扇区为1个Block
+//W25Q256
+//容量为32M字节,共有512个Block,8192个Sector 
+void W25QXX_CS_HIGH(void)
+{
+	if(WATCH_DOG_ON)
+	{
+		HAL_GPIO_TogglePin(GPIOC, DWI_Pin); //看门狗sp706
+	}
+	HAL_GPIO_WritePin(W25QXX_CS_GPIO, W25QXX_CS_PIN, GPIO_PIN_SET);
+	//delay_us(1);
+}
+void W25QXX_CS_LOW(void)
+{
+	if(WATCH_DOG_ON)
+	{
+		HAL_GPIO_TogglePin(GPIOC, DWI_Pin); //看门狗sp706
+	}
+	HAL_GPIO_WritePin(W25QXX_CS_GPIO, W25QXX_CS_PIN, GPIO_PIN_RESET);
+	//delay_us(1);
+}
+
+void W25QXX_CS_INIT(void)
+{
+	  __HAL_RCC_GPIOD_CLK_ENABLE();           //使能GPIOD时钟	
+	
+	  GPIO_InitTypeDef GPIO_Initure;
+    //PD8
+    GPIO_Initure.Pin=W25QXX_CS_PIN;          //PD8
+    GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
+    GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
+    GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //快速         
+    HAL_GPIO_Init(W25QXX_CS_GPIO,&GPIO_Initure);     //初始化
+}
+													 
+//初始化SPI FLASH的IO口
+void W25QXX_Init(void)
+{ 
+    uint8_t temp;
+	
+		W25QXX_CS_INIT();
+    
+		W25QXX_CS_HIGH();			             //SPI FLASH不选中
+	
+		SPI_W25Q_Init();		   			       //初始化SPI
+	
+		SPI_W25Q_SetSpeed(SPI_BAUDRATEPRESCALER_256); //设置为45M时钟,高速模式
+
+		w25qxx_obj.W25Q_TYPE=W25QXX_ReadID();	      	//读取FLASH ID.
+	
+    if(w25qxx_obj.W25Q_TYPE>=W25Q256)            //SPI FLASH为W25Q256
+    {
+        temp=W25QXX_ReadSR(3);          //读取状态寄存器3,判断地址模式
+        if((temp&0X01)==0)			        //如果不是4字节地址模式,则进入4字节地址模式
+				{
+					W25QXX_CS_LOW(); 			        //选中
+					SPI_W25Q_ReadWriteByte(W25X_Enable4ByteAddr);//发送进入4字节地址模式指令   
+					W25QXX_CS_HIGH();       		        //取消片选   
+				}
+    }
+		
+		W25QXX_Init_info();		
+		W25QXX_ID_OK();
+}  
+
+void W25QXX_Init_info(void)
+{
+	switch(w25qxx_obj.W25Q_TYPE&0x0000FFFF)
+	{
+		case W25Q512:	// 	w25q512
+			w25qxx_obj.BlockCount=1024;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q512\r\n");
+			#endif
+		break;
+		case W25Q256:	// 	w25q256
+			w25qxx_obj.BlockCount=512;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q256\r\n");
+			#endif
+		break;
+		case W25Q128:	// 	w25q128
+			w25qxx_obj.BlockCount=256;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q128\r\n");
+			#endif
+		break;
+		case W25Q64:	//	w25q64
+			w25qxx_obj.BlockCount=128;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q64\r\n");
+			#endif
+		break;
+		case W25Q32:	//	w25q32
+			w25qxx_obj.BlockCount=64;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q32\r\n");
+			#endif
+		break;
+		case W25Q16:	//	w25q16
+			w25qxx_obj.BlockCount=32;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q16\r\n");
+			#endif
+		break;
+		case W25Q80:	//	w25q80
+			w25qxx_obj.BlockCount=16;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q80\r\n");
+			#endif
+		break;
+		case W25Q40:	//	w25q40
+			w25qxx_obj.BlockCount=8;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q40\r\n");
+			#endif
+		break;
+		case W25Q20:	//	w25q20
+			w25qxx_obj.BlockCount=4;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q20\r\n");
+			#endif
+		break;
+		case W25Q10:	//	w25q10
+			w25qxx_obj.BlockCount=2;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q10\r\n");
+			#endif
+		break;
+		default:
+			w25qxx_obj.BlockCount=0;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Unknown ID\r\n");
+			#endif
+		break;
+				
+	}		
+	w25qxx_obj.PageSize=256;
+	w25qxx_obj.SectorSize=0x1000;
+	w25qxx_obj.SectorCount=w25qxx_obj.BlockCount*16;
+	w25qxx_obj.PageCount=(w25qxx_obj.SectorCount*w25qxx_obj.SectorSize)/w25qxx_obj.PageSize;
+	w25qxx_obj.BlockSize=w25qxx_obj.SectorSize*16;
+	w25qxx_obj.CapacityInKiloByte=(w25qxx_obj.SectorCount*w25qxx_obj.SectorSize)/1024;
+	
+	return;
+}
+
+uint16_t W25QXX_ID_OK(void)
+{
+	w25qxx_obj.W25Q_TYPE=W25QXX_ReadID();
+	
+	if((w25qxx_obj.W25Q_TYPE<=W25Q512)&&(w25qxx_obj.W25Q_TYPE>=W25Q16))
+	{
+			w25qxx_obj.W25Q_OK 	 = true;
+	}
+	else
+	{
+			w25qxx_obj.W25Q_OK 	 = false;
+	}
+	
+	
+	return w25qxx_obj.W25Q_OK; 
+}
+
+//读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器
+//状态寄存器1:
+//BIT7  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
+//状态寄存器2:
+//BIT7  6   5   4   3   2   1   0
+//SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
+//状态寄存器3:
+//BIT7      6    5    4   3   2   1   0
+//HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
+//regno:状态寄存器号,范:1~3
+//返回值:状态寄存器值
+uint8_t W25QXX_ReadSR(uint8_t regno)   
+{  
+	uint8_t byte=0,command=0; 
+    switch(regno)
+    {
+        case 1:
+            command=W25X_ReadStatusReg1;    //读状态寄存器1指令
+            break;
+        case 2:
+            command=W25X_ReadStatusReg2;    //读状态寄存器2指令
+            break;
+        case 3:
+            command=W25X_ReadStatusReg3;    //读状态寄存器3指令
+            break;
+        default:
+						regno = 1;
+            command=W25X_ReadStatusReg1;    
+            break;
+    }    
+	W25QXX_CS_LOW();                            //使能器件   
+	SPI_W25Q_ReadWriteByte(command);            //发送读取状态寄存器命令    
+	byte=SPI_W25Q_ReadWriteByte(0Xff);          //读取一个字节  
+	W25QXX_CS_HIGH();                           //取消片选   
+	
+	if((regno > 0)&&(regno <= 3))
+	{
+		w25qxx_obj.StatusRegister[regno-1] = byte;
+	}
+
+		
+	return byte;   
+} 
+//写W25QXX状态寄存器
+void W25QXX_Write_SR(uint8_t regno,uint8_t sr)   
+{   
+    uint8_t command=0;
+    switch(regno)
+    {
+        case 1:
+            command=W25X_WriteStatusReg1;    //写状态寄存器1指令
+            break;
+        case 2:
+            command=W25X_WriteStatusReg2;    //写状态寄存器2指令
+            break;
+        case 3:
+            command=W25X_WriteStatusReg3;    //写状态寄存器3指令
+            break;
+        default:
+            command=W25X_WriteStatusReg1;    
+            break;
+    }   
+	W25QXX_CS_LOW();                            //使能器件   
+	SPI_W25Q_ReadWriteByte(command);            //发送写取状态寄存器命令    
+	SPI_W25Q_ReadWriteByte(sr);                 //写入一个字节  
+	W25QXX_CS_HIGH();                            //取消片选     	      
+}   
+//W25QXX写使能	
+//将WEL置位   
+void W25QXX_Write_Enable(void)   
+{
+	W25QXX_CS_LOW();                            //使能器件   
+    SPI_W25Q_ReadWriteByte(W25X_WriteEnable);   //发送写使能  
+	W25QXX_CS_HIGH();                            //取消片选     	      
+} 
+//W25QXX写禁止	
+//将WEL清零  
+void W25QXX_Write_Disable(void)   
+{  
+	W25QXX_CS_LOW();                            //使能器件   
+    SPI_W25Q_ReadWriteByte(W25X_WriteDisable);  //发送写禁止指令    
+	W25QXX_CS_HIGH();                            //取消片选     	      
+} 
+
+//读取芯片ID
+//返回值如下:				   
+//0XEF13,表示芯片型号为W25Q80  
+//0XEF14,表示芯片型号为W25Q16    
+//0XEF15,表示芯片型号为W25Q32  
+//0XEF16,表示芯片型号为W25Q64 
+//0XEF17,表示芯片型号为W25Q128 	  
+//0XEF18,表示芯片型号为W25Q256
+
+uint16_t  W25QXX_ReadID(void)
+{
+	uint16_t  Temp = 0;	  
+	W25QXX_CS_LOW();				    
+	SPI_W25Q_ReadWriteByte(0x90);//发送读取ID命令	    
+	SPI_W25Q_ReadWriteByte(0x00); 	    
+	SPI_W25Q_ReadWriteByte(0x00); 	    
+	SPI_W25Q_ReadWriteByte(0x00); 	 			   
+	Temp|=SPI_W25Q_ReadWriteByte(0xFF)<<8;  
+	Temp|=SPI_W25Q_ReadWriteByte(0xFF);	 
+	W25QXX_CS_HIGH();				    
+	return Temp;
+}   		    
+//读取SPI FLASH  
+//在指定地址开始读取指定长度的数据
+//pBuffer:数据存储区
+//ReadAddr:开始读取的地址(24bit)
+//NumByteToRead:要读取的字节数(最大65535)
+void W25QXX_Read(uint8_t* pBuffer,uint32_t  ReadAddr,uint16_t  NumByteToRead)   
+{ 
+ 	uint16_t  i;   										    
+	W25QXX_CS_LOW();                            //使能器件   
+    SPI_W25Q_ReadWriteByte(W25X_ReadData);      //发送读取命令  
+    if(w25qxx_obj.W25Q_TYPE>=W25Q256)                //如果是W25Q256的话地址为4字节的,要发送最高8位
+    {
+        SPI_W25Q_ReadWriteByte((uint8_t)((ReadAddr)>>24));    
+    }
+    SPI_W25Q_ReadWriteByte((uint8_t)((ReadAddr)>>16));   //发送24bit地址    
+    SPI_W25Q_ReadWriteByte((uint8_t)((ReadAddr)>>8));   
+    SPI_W25Q_ReadWriteByte((uint8_t)ReadAddr);   
+    for(i=0;i<NumByteToRead;i++)
+	{ 
+        pBuffer[i]=SPI_W25Q_ReadWriteByte(0XFF);    //循环读数  
+    }
+	W25QXX_CS_HIGH();  				    	      
+}  
+//SPI在一页(0~65535)内写入少于256个字节的数据
+//在指定地址开始写入最大256字节的数据
+//pBuffer:数据存储区
+//WriteAddr:开始写入的地址(24bit)
+//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!	 
+void W25QXX_Write_Page(uint8_t* pBuffer,uint32_t  WriteAddr,uint16_t  NumByteToWrite)
+{
+		uint16_t  i;  
+    W25QXX_Write_Enable();                  		//SET WEL 
+		W25QXX_CS_LOW();                            //使能器件   
+    SPI_W25Q_ReadWriteByte(W25X_PageProgram);   //发送写页命令   
+    if(w25qxx_obj.W25Q_TYPE>=W25Q256)                		//如果是W25Q256的话地址为4字节的,要发送最高8位
+    {
+        SPI_W25Q_ReadWriteByte((uint8_t)((WriteAddr)>>24)); 
+    }
+    SPI_W25Q_ReadWriteByte((uint8_t)((WriteAddr)>>16)); //发送24bit地址    
+    SPI_W25Q_ReadWriteByte((uint8_t)((WriteAddr)>>8));   
+    SPI_W25Q_ReadWriteByte((uint8_t)WriteAddr);   
+    for(i=0;i<NumByteToWrite;i++)SPI_W25Q_ReadWriteByte(pBuffer[i]);//循环写数  
+		W25QXX_CS_HIGH();                            //取消片选 
+		W25QXX_Wait_Busy();					   //等待写入结束
+} 
+//无检验写SPI FLASH 
+//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
+//具有自动换页功能 
+//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
+//pBuffer:数据存储区
+//WriteAddr:开始写入的地址(24bit)
+//NumByteToWrite:要写入的字节数(最大65535)
+//CHECK OK
+void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t  WriteAddr,uint16_t  NumByteToWrite)   
+{ 			 		 
+	uint16_t  pageremain;	   
+	pageremain=256-WriteAddr%256; //单页剩余的字节数		 	    
+	if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
+	while(1)
+	{	   
+		W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
+		if(NumByteToWrite==pageremain)break;//写入结束了
+	 	else //NumByteToWrite>pageremain
+		{
+			pBuffer+=pageremain;
+			WriteAddr+=pageremain;	
+
+			NumByteToWrite-=pageremain;			  //减去已经写入了的字节数
+			if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
+			else pageremain=NumByteToWrite; 	  //不够256个字节了
+		}
+	};	    
+} 
+//写SPI FLASH  
+//在指定地址开始写入指定长度的数据
+//该函数带擦除操作!
+//pBuffer:数据存储区
+//WriteAddr:开始写入的地址(24bit)						
+//NumByteToWrite:要写入的字节数(最大65535)   
+uint8_t W25QXX_BUFFER[4096];		 
+void W25QXX_Write(uint8_t* pBuffer,uint32_t  WriteAddr,uint16_t  NumByteToWrite)   
+{ 
+	uint32_t  secpos;
+	uint16_t  secoff;
+	uint16_t  secremain;	   
+ 	uint16_t  i;    
+	uint8_t * 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)secremain=NumByteToWrite;//不大于4096个字节
+	while(1) 
+	{	
+		W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
+		for(i=0;i<secremain;i++)//校验数据
+		{
+			if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除  	  
+		}
+		if(i<secremain)//需要擦除
+		{
+			W25QXX_Erase_Sector(secpos);//擦除这个扇区
+			for(i=0;i<secremain;i++)	   //复制
+			{
+				W25QXX_BUF[i+secoff]=pBuffer[i];	  
+			}
+			W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  
+
+		}else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间. 				   
+		if(NumByteToWrite==secremain)break;//写入结束了
+		else//写入未结束
+		{
+			secpos++;//扇区地址增1
+			secoff=0;//偏移位置为0 	 
+
+		   	pBuffer+=secremain;  //指针偏移
+				WriteAddr+=secremain;//写地址偏移	   
+		   	NumByteToWrite-=secremain;				//字节数递减
+			if(NumByteToWrite>4096)secremain=4096;	//下一个扇区还是写不完
+			else secremain=NumByteToWrite;			//下一个扇区可以写完了
+		}	 
+	};	 
+}
+//擦除整个芯片		  
+//等待时间超长...
+void W25QXX_Erase_Chip(void)   
+{                                   
+    W25QXX_Write_Enable();                  			//SET WEL 
+    W25QXX_Wait_Busy();   
+  	W25QXX_CS_LOW();                            	//使能器件   
+    SPI_W25Q_ReadWriteByte(W25X_ChipErase);       //发送片擦除命令  
+		W25QXX_CS_HIGH();                            	//取消片选     	      
+		W25QXX_Wait_Busy();   				   							//等待芯片擦除结束
+}   
+//擦除一个扇区
+//Dst_Addr:扇区地址 根据实际容量设置
+//擦除一个扇区的最少时间:150ms
+void W25QXX_Erase_Sector(uint32_t  Dst_Addr)   
+{  
+		//监视falsh擦除情况,测试用   
+		//printf("fe:%x\r\n",Dst_Addr);	  
+		Dst_Addr*=4096;
+    W25QXX_Write_Enable();                  //SET WEL 	 
+    W25QXX_Wait_Busy();   
+  	W25QXX_CS_LOW();                            //使能器件   
+    SPI_W25Q_ReadWriteByte(W25X_SectorErase);   //发送扇区擦除指令 
+    if(w25qxx_obj.W25Q_TYPE>=W25Q256)                //如果是W25Q256的话地址为4字节的,要发送最高8位
+    {
+        SPI_W25Q_ReadWriteByte((uint8_t)((Dst_Addr)>>24)); 
+    }
+    SPI_W25Q_ReadWriteByte((uint8_t)((Dst_Addr)>>16));  //发送24bit地址    
+    SPI_W25Q_ReadWriteByte((uint8_t)((Dst_Addr)>>8));   
+    SPI_W25Q_ReadWriteByte((uint8_t)Dst_Addr);  
+		W25QXX_CS_HIGH();                            //取消片选     	      
+    W25QXX_Wait_Busy();   				    //等待擦除完成
+}  
+//等待空闲
+void W25QXX_Wait_Busy(void)   
+{   
+		while((W25QXX_ReadSR(1)&0x01)==0x01);   // 等待BUSY位清空
+}  
+//进入掉电模式
+void W25QXX_PowerDown(void)   
+{ 
+  	W25QXX_CS_LOW();                            //使能器件   
+    SPI_W25Q_ReadWriteByte(W25X_PowerDown);     //发送掉电命令  
+		W25QXX_CS_HIGH();                            //取消片选     	      
+    delay_us(3);                            //等待TPD  
+}   
+//唤醒
+void W25QXX_WAKEUP(void)   
+{  
+  	W25QXX_CS_LOW();                                //使能器件   
+    SPI_W25Q_ReadWriteByte(W25X_ReleasePowerDown);  //  send W25X_PowerDown command 0xAB    
+		W25QXX_CS_HIGH();                                //取消片选     	      
+    delay_us(3);                                //等待TRES1
+}   
+
+#endif //--------------------USE_OBJ_HAL_W25QXX-------------------//
+

+ 94 - 0
obj/obj_hal_w25qxx.h

@@ -0,0 +1,94 @@
+#include "dev_spi_conf.h"
+#ifdef USE_OBJ_HAL_W25QXX
+#ifndef OBJ_HAL_W25QXX_H
+#define OBJ_HAL_W25QXX_H
+#include "stm32f7xx.h"
+#include <stdio.h>
+
+
+
+//W25X系列/Q系列芯片列表	   
+//W25Q80  ID  0XEF13
+//W25Q16  ID  0XEF14
+//W25Q32  ID  0XEF15
+//W25Q64  ID  0XEF16	
+//W25Q128 ID  0XEF17	
+//W25Q256 ID  0XEF18
+//W25Q512 ID  0XEF19
+#define W25Q10 				0XEF10 
+#define W25Q20 				0XEF11 
+#define W25Q40 				0XEF12 
+#define W25Q80 				0XEF13 	
+#define W25Q16 				0XEF14
+#define W25Q32 				0XEF15
+#define W25Q64 				0XEF16
+#define W25Q128				0XEF17
+#define W25Q256 			0XEF18
+#define W25Q512 			0XEF19
+
+		   
+
+			
+////////////////////////////////////////////////////////////////////////////////// 
+//指令表
+#define W25X_WriteEnable				0x06 
+#define W25X_WriteDisable				0x04 
+#define W25X_ReadStatusReg1			0x05 
+#define W25X_ReadStatusReg2			0x35 
+#define W25X_ReadStatusReg3			0x15 
+#define W25X_WriteStatusReg1    0x01 
+#define W25X_WriteStatusReg2    0x31 
+#define W25X_WriteStatusReg3    0x11 
+#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 W25X_Enable4ByteAddr    0xB7
+#define W25X_Exit4ByteAddr      0xE9
+
+
+typedef struct
+{
+	uint16_t  	W25Q_TYPE;
+	uint16_t  	W25Q_OK;
+	uint8_t			UniqID[8];
+
+	uint16_t    PageSize;
+	uint32_t		PageCount;
+	uint32_t		SectorSize;
+	uint32_t		SectorCount;
+	uint32_t		BlockSize;
+	uint32_t		BlockCount; // so block trong chip flash
+
+	uint32_t		CapacityInKiloByte;
+	
+	uint8_t			StatusRegister[3];	
+}w25qxx_object_t;
+extern w25qxx_object_t   w25qxx_obj ;
+
+void W25QXX_Init(void);
+uint16_t W25QXX_ReadID(void);  	    				//读取FLASH ID
+uint8_t  W25QXX_ReadSR(uint8_t  regno);     //读取状态寄存器 
+void W25QXX_4ByteAddr_Enable(void);     		//使能4字节地址模式
+void W25QXX_Write_SR(uint8_t  regno,uint8_t  sr);   //写状态寄存器
+void W25QXX_Write_Enable(void);  		//写使能 
+void W25QXX_Write_Disable(void);		//写保护
+void W25QXX_Write_NoCheck(uint8_t * pBuffer,uint32_t  WriteAddr,uint16_t  NumByteToWrite);
+void W25QXX_Read(uint8_t * pBuffer,uint32_t  ReadAddr,uint16_t  NumByteToRead);   //读取flash
+void W25QXX_Write(uint8_t * pBuffer,uint32_t  WriteAddr,uint16_t  NumByteToWrite);//写入flash
+void W25QXX_Erase_Chip(void);    	  	//整片擦除
+void W25QXX_Erase_Sector(uint32_t  Dst_Addr);	//扇区擦除
+void W25QXX_Wait_Busy(void);           	//等待空闲
+void W25QXX_PowerDown(void);        	//进入掉电模式
+void W25QXX_WAKEUP(void);				//唤醒
+uint16_t W25QXX_ID_OK(void);
+#endif
+#endif //-----------------------USE_OBJ_HAL_W25QXX--------------------------------------//

+ 538 - 0
obj/obj_soft_w25qxx.c

@@ -0,0 +1,538 @@
+#include "obj_soft_w25qxx.h"
+#ifdef USE_OBJ_SOFT_W25QXX
+#include <stdint.h>
+#include <stdbool.h>
+#include "sys_delay.h"
+#include "dev_soft_spi.h"
+/* Definition for SPIx Pins */
+#define SPIx_SCK_PIN                   GPIO_PIN_13
+#define SPIx_SCK_GPIO_PORT             GPIOB
+
+#define SPIx_MISO_PIN                  GPIO_PIN_14
+#define SPIx_MISO_GPIO_PORT            GPIOB
+
+#define SPIx_MOSI_PIN                  GPIO_PIN_15
+#define SPIx_MOSI_GPIO_PORT            GPIOB
+
+#if 0
+#define	SPIx_CS_PIN										 GPIO_PIN_8		//GPIO_PIN_8
+#define	SPIx_CS_GPIO_PORT							 GPIOD				//GPIOD
+#else
+#define	SPIx_CS_PIN										 GPIO_PIN_12		//GPIO_PIN_8
+#define	SPIx_CS_GPIO_PORT							 GPIOB					//GPIOB
+
+#define	SPIx_WP_PIN										 GPIO_PIN_8		//GPIO_PIN_8
+#define	SPIx_WP_GPIO_PORT							 GPIOD				//GPIO
+#endif
+
+
+SoftSPI_TypeDef SoftSPIx_obj = {
+	  .SCLK_GPIO	= SPIx_SCK_GPIO_PORT,
+    .SCLK_Pin		= SPIx_SCK_PIN,
+
+    .MOSI_GPIO	= SPIx_MOSI_GPIO_PORT,
+    .MOSI_Pin		= SPIx_MOSI_PIN,
+
+    .MISO_GPIO	= SPIx_MISO_GPIO_PORT,
+    .MISO_Pin		= SPIx_MISO_PIN,
+
+    .SS_GPIO		= SPIx_CS_GPIO_PORT,
+    .SS_Pin			= SPIx_CS_PIN,
+
+	  .WP_GPIO		= SPIx_WP_GPIO_PORT,
+    .WP_Pin			= SPIx_WP_PIN,
+	
+    .Delay_Time = 1,
+};
+void W25QXX_Init_info(void);
+void W25QXX_SR_TEST(void);
+
+w25qxx_object_t   w25qxx_obj = 
+{
+	.W25Q_TYPE = W25Q256,				//默认是W25Q256
+	.W25Q_OK 	 = false,
+};
+
+
+
+//4Kbytes为一个Sector
+//16个扇区为1个Block
+//W25Q256
+//容量为32M字节,共有512个Block,8192个Sector 
+void W25QXX_CS_HIGH(void)
+{
+	SoftSPI_SetSS(&SoftSPIx_obj);
+	delay_us(1);
+}
+void W25QXX_CS_LOW(void)
+{
+	SoftSPI_ClrSS(&SoftSPIx_obj);
+	delay_us(1);
+}
+
+void W25QXX_SPI_INIT(void)
+{
+	  SoftSPI_Init(&SoftSPIx_obj);
+}
+													 
+//初始化SPI FLASH的IO口
+void W25QXX_Init(void)
+{ 
+    uint8_t temp;
+	
+		W25QXX_SPI_INIT();		   			     //初始化SPI
+    
+		W25QXX_CS_HIGH();			             //SPI FLASH不选中		
+
+		w25qxx_obj.W25Q_TYPE=W25QXX_ReadID();	      //读取FLASH ID.
+	
+    if(w25qxx_obj.W25Q_TYPE>=W25Q256)            //SPI FLASH为W25Q256
+    {
+        temp=W25QXX_ReadSR(3);          //读取状态寄存器3,判断地址模式
+        if((temp&0X01)==0)			        //如果不是4字节地址模式,则进入4字节地址模式
+				{
+					W25QXX_CS_LOW(); 			        //选中
+					SoftSPI_WriteRead(&SoftSPIx_obj, W25X_Enable4ByteAddr);//发送进入4字节地址模式指令   
+					W25QXX_CS_HIGH();       		        //取消片选   
+				}
+    }
+		
+		while(0)
+		{
+			W25QXX_SR_TEST();
+		}
+		
+		W25QXX_Init_info();		
+		
+		W25QXX_IS_OK();
+		
+}  
+uint16_t W25QXX_ID_OK(void)
+{
+	w25qxx_obj.W25Q_TYPE=W25QXX_ReadID();
+	
+	if((w25qxx_obj.W25Q_TYPE<=W25Q512)&&(w25qxx_obj.W25Q_TYPE>=W25Q16))
+	{
+			w25qxx_obj.W25Q_OK 	 = true;
+	}
+	else
+	{
+			w25qxx_obj.W25Q_OK 	 = false;
+	}
+	
+	
+	return w25qxx_obj.W25Q_OK; 
+}
+void W25QXX_SR_TEST(void)
+{
+		SoftSPI_WriteRead(&SoftSPIx_obj,W25X_WriteSrEnable);
+		W25QXX_Write_UnProtect();	
+		SoftSPI_WriteRead(&SoftSPIx_obj,W25X_WriteSrEnable);
+		W25QXX_Write_SR	(1,0);
+		SoftSPI_WriteRead(&SoftSPIx_obj,W25X_WriteSrEnable);
+		W25QXX_ReadSR	(1);
+		SoftSPI_WriteRead(&SoftSPIx_obj,W25X_WriteSrEnable);
+		W25QXX_Write_SR	(2,0);
+		W25QXX_ReadSR	(2);
+		//W25QXX_Write_SR	(3,0);
+		W25QXX_ReadSR	(3);
+		
+
+		
+		return;
+}
+
+void W25QXX_Init_info(void)
+{
+	switch(w25qxx_obj.W25Q_TYPE&0x0000FFFF)
+	{
+		case W25Q512:	// 	w25q512
+			w25qxx_obj.BlockCount=1024;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q512\r\n");
+			#endif
+		break;
+		case W25Q256:	// 	w25q256
+			w25qxx_obj.BlockCount=512;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q256\r\n");
+			#endif
+		break;
+		case W25Q128:	// 	w25q128
+			w25qxx_obj.BlockCount=256;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q128\r\n");
+			#endif
+		break;
+		case W25Q64:	//	w25q64
+			w25qxx_obj.BlockCount=128;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q64\r\n");
+			#endif
+		break;
+		case W25Q32:	//	w25q32
+			w25qxx_obj.BlockCount=64;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q32\r\n");
+			#endif
+		break;
+		case W25Q16:	//	w25q16
+			w25qxx_obj.BlockCount=32;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q16\r\n");
+			#endif
+		break;
+		case W25Q80:	//	w25q80
+			w25qxx_obj.BlockCount=16;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q80\r\n");
+			#endif
+		break;
+		case W25Q40:	//	w25q40
+			w25qxx_obj.BlockCount=8;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q40\r\n");
+			#endif
+		break;
+		case W25Q20:	//	w25q20
+			w25qxx_obj.BlockCount=4;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q20\r\n");
+			#endif
+		break;
+		case W25Q10:	//	w25q10
+			w25qxx_obj.BlockCount=2;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q10\r\n");
+			#endif
+		break;
+		default:
+			w25qxx_obj.BlockCount=0;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Unknown ID\r\n");
+			#endif
+		break;
+				
+	}		
+	w25qxx_obj.PageSize   = 256;
+	w25qxx_obj.SectorSize = 0x1000;
+	w25qxx_obj.SectorCount= w25qxx_obj.BlockCount*16;
+	w25qxx_obj.PageCount  = (w25qxx_obj.SectorCount*w25qxx_obj.SectorSize)/w25qxx_obj.PageSize;
+	w25qxx_obj.BlockSize  =w25qxx_obj.SectorSize*16;
+	w25qxx_obj.CapacityInKiloByte=(w25qxx_obj.SectorCount*w25qxx_obj.SectorSize)/1024;
+	
+	return;
+}
+
+void W25QXX_ID_check(void)
+{
+	while((w25qxx_obj.W25Q_TYPE > 0XEF19)
+			&&(w25qxx_obj.W25Q_TYPE < W25Q32))		//检测不到W25Q128
+	{
+
+		w25qxx_obj.W25Q_TYPE = W25QXX_ReadID();
+		HAL_Delay(100);
+	}	
+}
+
+//读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器
+//状态寄存器1:
+//BIT7  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
+//状态寄存器2:
+//BIT7  6   5   4   3   2   1   0
+//SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
+//状态寄存器3:
+//BIT7      6    5    4   3   2   1   0
+//HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
+//regno:状态寄存器号,范:1~3
+//返回值:状态寄存器值
+uint8_t W25QXX_ReadSR(uint8_t regno)   
+{  
+	uint8_t byte=0,command=0; 
+    switch(regno)
+    {
+        case 1:
+            command=W25X_ReadStatusReg1;    //读状态寄存器1指令
+            break;
+        case 2:
+            command=W25X_ReadStatusReg2;    //读状态寄存器2指令
+            break;
+        case 3:
+            command=W25X_ReadStatusReg3;    //读状态寄存器3指令
+            break;
+        default:
+            command=W25X_ReadStatusReg1;    
+            break;
+    }    
+	W25QXX_CS_LOW();                            //使能器件   
+	SoftSPI_WriteRead(&SoftSPIx_obj, command);            //发送读取状态寄存器命令    
+	byte=SoftSPI_WriteRead(&SoftSPIx_obj, 0Xff);          //读取一个字节  
+	W25QXX_CS_HIGH();                           //取消片选     
+	if((regno > 0)&&(regno <= 3))
+	{
+		w25qxx_obj.StatusRegister[regno-1] = byte;
+	}
+	return byte;   
+} 
+//写W25QXX状态寄存器
+void W25QXX_Write_SR(uint8_t regno,uint8_t sr)   
+{   
+    uint8_t command=0;
+    switch(regno)
+    {
+        case 1:
+            command=W25X_WriteStatusReg1;    //写状态寄存器1指令
+            break;
+        case 2:
+            command=W25X_WriteStatusReg2;    //写状态寄存器2指令
+            break;
+        case 3:
+            command=W25X_WriteStatusReg3;    //写状态寄存器3指令
+            break;
+        default:
+            command=W25X_WriteStatusReg1;    
+            break;
+    }   
+	W25QXX_CS_LOW();                            //使能器件   
+	SoftSPI_WriteRead(&SoftSPIx_obj, command);            //发送写取状态寄存器命令    
+	SoftSPI_WriteRead(&SoftSPIx_obj, sr);                 //写入一个字节  
+	W25QXX_CS_HIGH();                            //取消片选     	      
+}   
+//W25QXX写使能	
+//将WEL置位   
+void W25QXX_Write_Enable(void)   
+{
+	W25QXX_CS_LOW();                            //使能器件   
+    SoftSPI_WriteRead(&SoftSPIx_obj, W25X_WriteEnable);   //发送写使能  
+	W25QXX_CS_HIGH();                            //取消片选     	      
+} 
+
+//清除写保护
+void W25QXX_Write_UnProtect(void)   
+{
+	W25QXX_CS_LOW();                            //使能器件   
+  W25QXX_Write_SR(1, 0);   									//清除写保护 
+	W25QXX_CS_HIGH();                            //取消片选     	      
+} 
+
+//W25QXX写禁止	
+//将WEL清零  
+void W25QXX_Write_Disable(void)   
+{  
+	W25QXX_CS_LOW();                            //使能器件   
+    SoftSPI_WriteRead(&SoftSPIx_obj, W25X_WriteDisable);  //发送写禁止指令    
+	W25QXX_CS_HIGH();                            //取消片选     	      
+} 
+
+//读取芯片ID
+//返回值如下:				   
+//0XEF13,表示芯片型号为W25Q80  
+//0XEF14,表示芯片型号为W25Q16    
+//0XEF15,表示芯片型号为W25Q32  
+//0XEF16,表示芯片型号为W25Q64 
+//0XEF17,表示芯片型号为W25Q128 	  
+//0XEF18,表示芯片型号为W25Q256
+
+uint16_t  W25QXX_ReadID(void)
+{
+	uint16_t  Temp = 0;	  
+	W25QXX_CS_LOW();				    
+	SoftSPI_WriteRead(&SoftSPIx_obj, 0x90);//发送读取ID命令	    
+	SoftSPI_WriteRead(&SoftSPIx_obj, 0x00); 	    
+	SoftSPI_WriteRead(&SoftSPIx_obj, 0x00); 	    
+	SoftSPI_WriteRead(&SoftSPIx_obj, 0x00); 	 			   
+	Temp|=SoftSPI_WriteRead(&SoftSPIx_obj, 0xFF)<<8;  
+	Temp|=SoftSPI_WriteRead(&SoftSPIx_obj, 0xFF);	 
+	W25QXX_CS_HIGH();				    
+	return Temp;
+}   		    
+//读取SPI FLASH  
+//在指定地址开始读取指定长度的数据
+//pBuffer:数据存储区
+//ReadAddr:开始读取的地址(24bit)
+//NumByteToRead:要读取的字节数(最大65535)
+void W25QXX_Read(uint8_t* pBuffer,uint32_t  ReadAddr,uint16_t  NumByteToRead)   
+{ 
+ 	uint16_t  i;   										    
+	W25QXX_CS_LOW();                            //使能器件   
+    SoftSPI_WriteRead(&SoftSPIx_obj, W25X_ReadData);      //发送读取命令  
+    if(w25qxx_obj.W25Q_TYPE>=W25Q256)                //如果是W25Q256的话地址为4字节的,要发送最高8位
+    {
+        SoftSPI_WriteRead(&SoftSPIx_obj, (uint8_t)((ReadAddr)>>24));    
+    }
+    SoftSPI_WriteRead(&SoftSPIx_obj, (uint8_t)((ReadAddr)>>16));   //发送24bit地址    
+    SoftSPI_WriteRead(&SoftSPIx_obj, (uint8_t)((ReadAddr)>>8));   
+    SoftSPI_WriteRead(&SoftSPIx_obj, (uint8_t)ReadAddr);   
+    for(i=0;i<NumByteToRead;i++)
+	{ 
+        pBuffer[i]=SoftSPI_WriteRead(&SoftSPIx_obj, 0XFF);    //循环读数  
+    }
+	W25QXX_CS_HIGH();  				    	      
+}  
+//SPI在一页(0~65535)内写入少于256个字节的数据
+//在指定地址开始写入最大256字节的数据
+//pBuffer:数据存储区
+//WriteAddr:开始写入的地址(24bit)
+//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!	 
+void W25QXX_Write_Page(uint8_t* pBuffer,uint32_t  WriteAddr,uint16_t  NumByteToWrite)
+{
+		uint16_t  i;  
+    W25QXX_Write_Enable();                  		//SET WEL 
+		W25QXX_CS_LOW();                            //使能器件   
+    SoftSPI_WriteRead(&SoftSPIx_obj, W25X_PageProgram);   //发送写页命令   
+    if(w25qxx_obj.W25Q_TYPE>=W25Q256)                		//如果是W25Q256的话地址为4字节的,要发送最高8位
+    {
+        SoftSPI_WriteRead(&SoftSPIx_obj, (uint8_t)((WriteAddr)>>24)); 
+    }
+    SoftSPI_WriteRead(&SoftSPIx_obj, (uint8_t)((WriteAddr)>>16)); //发送24bit地址    
+    SoftSPI_WriteRead(&SoftSPIx_obj, (uint8_t)((WriteAddr)>>8));   
+    SoftSPI_WriteRead(&SoftSPIx_obj, (uint8_t)WriteAddr);   
+    for(i=0;i<NumByteToWrite;i++)SoftSPI_WriteRead(&SoftSPIx_obj, pBuffer[i]);//循环写数  
+		W25QXX_CS_HIGH();                            //取消片选 
+		W25QXX_Wait_Busy();					   //等待写入结束
+} 
+//无检验写SPI FLASH 
+//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
+//具有自动换页功能 
+//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
+//pBuffer:数据存储区
+//WriteAddr:开始写入的地址(24bit)
+//NumByteToWrite:要写入的字节数(最大65535)
+//CHECK OK
+void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t  WriteAddr,uint16_t  NumByteToWrite)   
+{ 			 		 
+	uint16_t  pageremain;	   
+	pageremain=256-WriteAddr%256; //单页剩余的字节数		 	    
+	if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
+	while(1)
+	{	   
+		W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
+		if(NumByteToWrite==pageremain)break;//写入结束了
+	 	else //NumByteToWrite>pageremain
+		{
+			pBuffer+=pageremain;
+			WriteAddr+=pageremain;	
+
+			NumByteToWrite-=pageremain;			  //减去已经写入了的字节数
+			if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
+			else pageremain=NumByteToWrite; 	  //不够256个字节了
+		}
+	};	    
+} 
+//写SPI FLASH  
+//在指定地址开始写入指定长度的数据
+//该函数带擦除操作!
+//pBuffer:数据存储区
+//WriteAddr:开始写入的地址(24bit)						
+//NumByteToWrite:要写入的字节数(最大65535)   
+uint8_t W25QXX_BUFFER[16][256];		 
+void W25QXX_Write(uint8_t* pBuffer,uint32_t  WriteAddr,uint16_t  NumByteToWrite)   
+{ 
+	uint32_t  secpos;
+	uint16_t  secoff;
+	uint16_t  secremain;	   
+ 	uint16_t  i;    
+	uint8_t * W25QXX_BUF;	  
+  W25QXX_BUF=&W25QXX_BUFFER[0][0];	     
+ 	secpos=WriteAddr/4096;//扇区地址  
+	secoff=WriteAddr%4096;//在扇区内的偏移
+	secremain=4096-secoff;//扇区剩余空间大小   
+ 	//printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
+ 	if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
+	while(1) 
+	{	
+		W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
+		for(i=0;i<secremain;i++)//校验数据
+		{
+			if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除  	  
+		}
+		if(i<secremain)//需要擦除
+		{
+			W25QXX_Erase_Sector(secpos);//擦除这个扇区
+			for(i=0;i<secremain;i++)	   //复制
+			{
+				W25QXX_BUF[i+secoff]=pBuffer[i];	  
+			}
+			W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  
+
+		}else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间. 				   
+		if(NumByteToWrite==secremain)break;//写入结束了
+		else//写入未结束
+		{
+			secpos++;//扇区地址增1
+			secoff=0;//偏移位置为0 	 
+
+		   	pBuffer+=secremain;  //指针偏移
+				WriteAddr+=secremain;//写地址偏移	   
+		   	NumByteToWrite-=secremain;				//字节数递减
+			if(NumByteToWrite>4096)secremain=4096;	//下一个扇区还是写不完
+			else secremain=NumByteToWrite;			//下一个扇区可以写完了
+		}	 
+	};	 
+}
+//擦除整个芯片		  
+//等待时间超长...
+void W25QXX_Erase_Chip(void)   
+{                                   
+    W25QXX_Write_Enable();                  								//SET WEL 
+    W25QXX_Wait_Busy();   
+  	W25QXX_CS_LOW();                            						//使能器件   
+    SoftSPI_WriteRead(&SoftSPIx_obj, W25X_ChipErase);       //发送片擦除命令  
+		W25QXX_CS_HIGH();                            						//取消片选     	      
+		W25QXX_Wait_Busy();   				   												//等待芯片擦除结束
+}   
+//擦除一个扇区
+//Dst_Addr:扇区地址 根据实际容量设置
+//擦除一个扇区的最少时间:150ms
+void W25QXX_Erase_Sector(uint32_t  Dst_Addr)   
+{  
+		//监视falsh擦除情况,测试用   
+		//printf("fe:%x\r\n",Dst_Addr);	  
+		Dst_Addr*=4096;
+    W25QXX_Write_Enable();                  //SET WEL 	 
+    W25QXX_Wait_Busy();   
+  	W25QXX_CS_LOW();                            //使能器件   
+    SoftSPI_WriteRead(&SoftSPIx_obj, W25X_SectorErase);   //发送扇区擦除指令 
+    if(w25qxx_obj.W25Q_TYPE>=W25Q256)                //如果是W25Q256的话地址为4字节的,要发送最高8位
+    {
+        SoftSPI_WriteRead(&SoftSPIx_obj, (uint8_t)((Dst_Addr)>>24)); 
+    }
+    SoftSPI_WriteRead(&SoftSPIx_obj, (uint8_t)((Dst_Addr)>>16));  //发送24bit地址    
+    SoftSPI_WriteRead(&SoftSPIx_obj, (uint8_t)((Dst_Addr)>>8));   
+    SoftSPI_WriteRead(&SoftSPIx_obj, (uint8_t)Dst_Addr);  
+		W25QXX_CS_HIGH();                            //取消片选     	      
+    W25QXX_Wait_Busy();   				    //等待擦除完成
+}  
+//等待空闲
+void W25QXX_Wait_Busy(void)   
+{   
+		while((W25QXX_ReadSR(1)&0x01)==0x01)
+		{
+			delay_us(1);
+		};   // 等待BUSY位清空
+}  
+//进入掉电模式
+void W25QXX_PowerDown(void)   
+{ 
+  	W25QXX_CS_LOW();                            //使能器件   
+    SoftSPI_WriteRead(&SoftSPIx_obj, W25X_PowerDown);     //发送掉电命令  
+		W25QXX_CS_HIGH();                            //取消片选     	      
+    delay_us(3);                            //等待TPD  
+}   
+//唤醒
+void W25QXX_WAKEUP(void)   
+{  
+  	W25QXX_CS_LOW();                                //使能器件   
+    SoftSPI_WriteRead(&SoftSPIx_obj, W25X_ReleasePowerDown);  //  send W25X_PowerDown command 0xAB    
+		W25QXX_CS_HIGH();                                //取消片选     	      
+    delay_us(3);                                //等待TRES1
+}   
+
+#endif //--------------------USE_OBJ_SOFT_W25QXX-------------------//
+

+ 100 - 0
obj/obj_soft_w25qxx.h

@@ -0,0 +1,100 @@
+#include "dev_spi_conf.h"
+
+#ifdef USE_OBJ_SOFT_W25QXX
+
+#ifndef OBJ_SOFT_W25QXX_H
+#define OBJ_SOFT_W25QXX_H
+#include "stm32f7xx.h"
+#include <stdio.h>
+
+
+
+//W25X系列/Q系列芯片列表	   
+//W25Q80  ID  0XEF13
+//W25Q16  ID  0XEF14
+//W25Q32  ID  0XEF15
+//W25Q64  ID  0XEF16	
+//W25Q128 ID  0XEF17	
+//W25Q256 ID  0XEF18
+//W25Q512 ID  0XEF19
+#define W25Q10 				0XEF10 
+#define W25Q20 				0XEF11 
+#define W25Q40 				0XEF12 
+#define W25Q80 				0XEF13 	
+#define W25Q16 				0XEF14
+#define W25Q32 				0XEF15
+#define W25Q64 				0XEF16
+#define W25Q128				0XEF17
+#define W25Q256 			0XEF18
+#define W25Q512 			0XEF19
+
+		   
+
+			
+////////////////////////////////////////////////////////////////////////////////// 
+//指令表
+#define W25X_WriteEnable				0x06 
+#define W25X_WriteDisable				0x04 
+#define W25X_ReadStatusReg1			0x05 
+#define W25X_ReadStatusReg2			0x35 
+#define W25X_ReadStatusReg3			0x15 
+#define W25X_WriteStatusReg1    0x01 
+#define W25X_WriteStatusReg2    0x31 
+#define W25X_WriteStatusReg3    0x11 
+#define W25X_WriteSrEnable			0x50 
+#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 W25X_Enable4ByteAddr    0xB7
+#define W25X_Exit4ByteAddr      0xE9
+
+
+typedef struct
+{
+	uint16_t  	W25Q_TYPE;
+	uint16_t  	W25Q_OK;
+	uint8_t			UniqID[8];
+
+	uint16_t    PageSize;
+	uint32_t		PageCount;
+	uint32_t		SectorSize;
+	uint32_t		SectorCount;
+	uint32_t		BlockSize;
+	uint32_t		BlockCount; // so block trong chip flash
+
+	uint32_t		CapacityInKiloByte;
+	
+	uint8_t			StatusRegister[3];	
+}w25qxx_object_t;
+
+extern w25qxx_object_t   w25qxx_obj ;
+
+void W25QXX_Init(void);
+uint16_t W25QXX_ReadID(void);  	    		//读取FLASH ID
+uint8_t  W25QXX_ReadSR(uint8_t  regno);             //读取状态寄存器 
+void W25QXX_4ByteAddr_Enable(void);     //使能4字节地址模式
+void W25QXX_Write_SR(uint8_t  regno,uint8_t  sr);   //写状态寄存器
+void W25QXX_Write_Enable(void);  		//写使能 
+void W25QXX_Write_Disable(void);		//写保护
+void W25QXX_Write_NoCheck(uint8_t * pBuffer,uint32_t  WriteAddr,uint16_t  NumByteToWrite);
+void W25QXX_Read(uint8_t * pBuffer,uint32_t  ReadAddr,uint16_t  NumByteToRead);   //读取flash
+void W25QXX_Write(uint8_t * pBuffer,uint32_t  WriteAddr,uint16_t  NumByteToWrite);//写入flash
+void W25QXX_Erase_Chip(void);    	  					//整片擦除
+void W25QXX_Erase_Sector(uint32_t  Dst_Addr);	//扇区擦除
+void W25QXX_Wait_Busy(void);           				//等待空闲
+void W25QXX_PowerDown(void);        					//进入掉电模式
+void W25QXX_WAKEUP(void);											//唤醒
+void W25QXX_Write_UnProtect(void);
+uint16_t W25QXX_ID_OK(void);
+#endif
+
+#endif //---------------------------USE_OBJ_SOFT_W25QXX---------------------//

+ 975 - 0
obj/obj_spi_w25qxx.c

@@ -0,0 +1,975 @@
+#ifdef USE_W25QXX_SPI
+
+#if (_W25QXX_DEBUG==1)
+#include <stdio.h>
+#endif
+#include "sys_delay.h"
+#include "dev_spi_sdcard.h"
+#include "obj_spi_w25qxx.h"
+
+
+#define W25QXX_DUMMY_BYTE         0xA5
+SPI_HandleTypeDef hspi_w25q = {0};
+w25qxx_t	w25qxx;
+
+#if (_W25QXX_USE_FREERTOS==1)
+#define	W25qxx_Delay(delay)		osDelay(delay)
+#include "cmsis_os.h"
+#else
+#define	W25qxx_Delay(delay)		delay_us(delay)//sys_delay_ms(delay)//
+
+#endif
+void W25qxx_Cs_Init()
+{
+	GPIO_InitTypeDef  GPIO_InitStruct;
+	SPIx_CS_GPIO_CLK_ENABLE();
+  GPIO_InitStruct.Pin 		= _W25QXX_CS_PIN ;
+  GPIO_InitStruct.Mode 		= GPIO_MODE_OUTPUT_PP;
+	GPIO_InitStruct.Speed   = GPIO_SPEED_FREQ_VERY_HIGH;
+  HAL_GPIO_Init(_W25QXX_CS_GPIO, &GPIO_InitStruct); 
+	
+	W25QXX_CS_HIGH();
+}
+
+/**
+  * @brief SPI MSP Initialization 
+  *        This function configures the hardware resources used in this example: 
+  *           - Peripheral's clock enable
+  *           - Peripheral's GPIO Configuration  
+  * @param hspi: SPI handle pointer
+  * @retval None
+  */
+
+void HAL_SPI_MspInit_flash(void)
+{
+  GPIO_InitTypeDef  GPIO_InitStruct;
+  
+  /*##-1- Enable peripherals and GPIO Clocks #################################*/
+  /* Enable GPIO TX/RX clock */
+  SPIx_SCK_GPIO_CLK_ENABLE();
+  SPIx_MISO_GPIO_CLK_ENABLE();
+  SPIx_MOSI_GPIO_CLK_ENABLE();
+  
+  /*##-2- Configure peripheral GPIO ##########################################*/  
+  /* SPI SCK GPIO pin configuration  */
+  GPIO_InitStruct.Pin       = SPIx_SCK_PIN;
+  GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;	
+  GPIO_InitStruct.Pull      = GPIO_PULLUP;
+  GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;
+	GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
+  
+  HAL_GPIO_Init(SPIx_SCK_GPIO_PORT, &GPIO_InitStruct);
+    
+  /* SPI MISO GPIO pin configuration  */
+  GPIO_InitStruct.Pin = SPIx_MISO_PIN;  
+  HAL_GPIO_Init(SPIx_MISO_GPIO_PORT, &GPIO_InitStruct);
+  
+  /* SPI MOSI GPIO pin configuration  */
+  GPIO_InitStruct.Pin = SPIx_MOSI_PIN; 
+  HAL_GPIO_Init(SPIx_MOSI_GPIO_PORT, &GPIO_InitStruct);  
+
+}
+
+
+void W25qxx_Spi_Init(void)	
+{
+	  /* Enable SPI clock */
+		SPIx_CLK_ENABLE(); 
+	
+    hspi_w25q.Instance								=	SPIx;                         //SP2
+    hspi_w25q.Init.Mode								=	SPI_MODE_MASTER;             	//设置SPI工作模式,设置为主模式
+    hspi_w25q.Init.Direction					=	SPI_DIRECTION_2LINES;   			//设置SPI单向或者双向的数据模式:SPI设置为双线模式
+    hspi_w25q.Init.DataSize						=	SPI_DATASIZE_8BIT;       			//设置SPI的数据大小:SPI发送接收8位帧结构
+    hspi_w25q.Init.CLKPolarity				=	SPI_POLARITY_HIGH;    				//串行同步时钟的空闲状态为高电平
+    hspi_w25q.Init.CLKPhase						=	SPI_PHASE_2EDGE;         			//串行同步时钟的第二个跳变沿(上升或下降)数据被采样
+    hspi_w25q.Init.NSS								=	SPI_NSS_SOFT;                 //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
+    hspi_w25q.Init.BaudRatePrescaler	=	SPI_BAUDRATEPRESCALER_256;		//定义波特率预分频的值:波特率预分频值为256
+    hspi_w25q.Init.FirstBit						=	SPI_FIRSTBIT_MSB;        			//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
+    hspi_w25q.Init.TIMode							=	SPI_TIMODE_DISABLE;        		//关闭TI模式
+    hspi_w25q.Init.CRCCalculation			=	SPI_CRCCALCULATION_DISABLE;		//关闭硬件CRC校验
+    hspi_w25q.Init.CRCPolynomial			=	7;                  					//CRC值计算的多项式
+    
+	  HAL_SPI_Init(&hspi_w25q);//初始化
+    
+    __HAL_SPI_ENABLE(&hspi_w25q);                    //使能SPI2
+	
+}
+
+
+//###################################################################################################################
+uint8_t	W25qxx_Spi(uint8_t	Data)
+{
+	uint8_t	ret;
+	HAL_SPI_TransmitReceive(&_W25QXX_SPI,&Data,&ret,1,10);
+	return ret;	
+}
+//###################################################################################################################
+uint32_t W25qxx_ReadID(void)
+{
+  uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
+  W25QXX_CS_LOW();
+  W25qxx_Spi(0x9F);
+  Temp0 = W25qxx_Spi(W25QXX_DUMMY_BYTE);
+  Temp1 = W25qxx_Spi(W25QXX_DUMMY_BYTE);
+  Temp2 = W25qxx_Spi(W25QXX_DUMMY_BYTE);
+  W25QXX_CS_HIGH();
+  Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
+  return Temp;
+}
+//###################################################################################################################
+void W25qxx_ReadUniqID(void)
+{
+  W25QXX_CS_LOW();
+  W25qxx_Spi(0x4B);
+	for(uint8_t	i=0;i<4;i++)
+		W25qxx_Spi(W25QXX_DUMMY_BYTE);
+	for(uint8_t	i=0;i<8;i++)
+		w25qxx.UniqID[i] = W25qxx_Spi(W25QXX_DUMMY_BYTE);
+  W25QXX_CS_HIGH();
+}
+//###################################################################################################################
+void W25qxx_WriteEnable(void)
+{
+  W25QXX_CS_LOW();
+  W25qxx_Spi(0x06);
+  W25QXX_CS_HIGH();
+	W25qxx_Delay(1);
+}
+//###################################################################################################################
+void W25qxx_WriteDisable(void)
+{
+  W25QXX_CS_LOW();
+  W25qxx_Spi(0x04);
+  W25QXX_CS_HIGH();
+	W25qxx_Delay(1);
+}
+//###################################################################################################################
+uint8_t W25qxx_ReadStatusRegister(uint8_t	SelectStatusRegister_1_2_3)
+{
+	uint8_t	status=0;
+  W25QXX_CS_LOW();
+	if(SelectStatusRegister_1_2_3==1)
+	{
+		W25qxx_Spi(0x05);
+		status=W25qxx_Spi(W25QXX_DUMMY_BYTE);	
+		w25qxx.StatusRegister1 = status;
+	}
+	else if(SelectStatusRegister_1_2_3==2)
+	{
+		W25qxx_Spi(0x35);
+		status=W25qxx_Spi(W25QXX_DUMMY_BYTE);	
+		w25qxx.StatusRegister2 = status;
+	}
+	else
+	{
+		W25qxx_Spi(0x15);
+		status=W25qxx_Spi(W25QXX_DUMMY_BYTE);	
+		w25qxx.StatusRegister3 = status;
+	}	
+  W25QXX_CS_HIGH();
+	return status;
+}
+//###################################################################################################################
+void W25qxx_WriteStatusRegister(uint8_t	SelectStatusRegister_1_2_3,uint8_t Data)
+{
+  W25QXX_CS_LOW();
+	if(SelectStatusRegister_1_2_3==1)
+	{
+		W25qxx_Spi(0x01);
+		w25qxx.StatusRegister1 = Data;
+	}
+	else if(SelectStatusRegister_1_2_3==2)
+	{
+		W25qxx_Spi(0x31);
+		w25qxx.StatusRegister2 = Data;
+	}
+	else
+	{
+		W25qxx_Spi(0x11);
+		w25qxx.StatusRegister3 = Data;
+	}
+	W25qxx_Spi(Data);
+  W25QXX_CS_HIGH();
+}
+//###################################################################################################################
+void W25qxx_WaitForWriteEnd(void)
+{
+	W25qxx_Delay(1);
+	W25QXX_CS_LOW();
+	W25qxx_Spi(0x05);
+  do
+  {
+    w25qxx.StatusRegister1 = W25qxx_Spi(W25QXX_DUMMY_BYTE);
+		W25qxx_Delay(1);
+  }
+  while ((w25qxx.StatusRegister1 & 0x01) == 0x01);
+ W25QXX_CS_HIGH();
+}
+//###################################################################################################################
+bool	W25qxx_Init(void)
+{
+	w25qxx.Lock=1;	
+	while(HAL_GetTick()<100)
+		W25qxx_Delay(1);
+	uint32_t	id;
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx Init Begin...\r\n");
+	#endif
+	id=W25qxx_ReadID();
+	
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx ID:0x%X\r\n",id);
+	#endif
+	switch(id&0x0000FFFF)
+	{
+		case 0x401A:	// 	w25q512
+			w25qxx.ID=ID_W25Q512;
+			w25qxx.BlockCount=1024;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q512\r\n");
+			#endif
+		break;
+		case 0x4019:	// 	w25q256
+			w25qxx.ID=ID_W25Q256;
+			w25qxx.BlockCount=512;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q256\r\n");
+			#endif
+		break;
+		case 0x4018:	// 	w25q128
+			w25qxx.ID=ID_W25Q128;
+			w25qxx.BlockCount=256;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q128\r\n");
+			#endif
+		break;
+		case 0x4017:	//	w25q64
+			w25qxx.ID=ID_W25Q64;
+			w25qxx.BlockCount=128;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q64\r\n");
+			#endif
+		break;
+		case 0x4016:	//	w25q32
+			w25qxx.ID=ID_W25Q32;
+			w25qxx.BlockCount=64;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q32\r\n");
+			#endif
+		break;
+		case 0x4015:	//	w25q16
+			w25qxx.ID=ID_W25Q16;
+			w25qxx.BlockCount=32;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q16\r\n");
+			#endif
+		break;
+		case 0x4014:	//	w25q80
+			w25qxx.ID=ID_W25Q80;
+			w25qxx.BlockCount=16;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q80\r\n");
+			#endif
+		break;
+		case 0x4013:	//	w25q40
+			w25qxx.ID=ID_W25Q40;
+			w25qxx.BlockCount=8;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q40\r\n");
+			#endif
+		break;
+		case 0x4012:	//	w25q20
+			w25qxx.ID=ID_W25Q20;
+			w25qxx.BlockCount=4;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q20\r\n");
+			#endif
+		break;
+		case 0x4011:	//	w25q10
+			w25qxx.ID=ID_W25Q10;
+			w25qxx.BlockCount=2;
+			#if (_W25QXX_DEBUG==1)
+			printf("w25qxx Chip: w25q10\r\n");
+			#endif
+		break;
+		default:
+				#if (_W25QXX_DEBUG==1)
+				printf("w25qxx Unknown ID\r\n");
+				#endif
+			w25qxx.Lock=0;	
+			return false;
+				
+	}		
+	w25qxx.PageSize=256;
+	w25qxx.SectorSize=0x1000;
+	w25qxx.SectorCount=w25qxx.BlockCount*16;
+	w25qxx.PageCount=(w25qxx.SectorCount*w25qxx.SectorSize)/w25qxx.PageSize;
+	w25qxx.BlockSize=w25qxx.SectorSize*16;
+	w25qxx.CapacityInKiloByte=(w25qxx.SectorCount*w25qxx.SectorSize)/1024;
+	W25qxx_ReadUniqID();
+	W25qxx_ReadStatusRegister(1);
+	W25qxx_ReadStatusRegister(2);
+	W25qxx_ReadStatusRegister(3);
+	#if (_W25QXX_DEBUG==1)
+		printf("w25qxx Page Size: %d Bytes\r\n",w25qxx.PageSize);
+		printf("w25qxx Page Count: %d\r\n",w25qxx.PageCount);
+		printf("w25qxx Sector Size: %d Bytes\r\n",w25qxx.SectorSize);
+		printf("w25qxx Sector Count: %d\r\n",w25qxx.SectorCount);
+		printf("w25qxx Block Size: %d Bytes\r\n",w25qxx.BlockSize);
+		printf("w25qxx Block Count: %d\r\n",w25qxx.BlockCount);
+		printf("w25qxx Capacity: %d KiloBytes\r\n",w25qxx.CapacityInKiloByte);
+		printf("w25qxx Init Done\r\n");
+	#endif
+	w25qxx.Lock=0;	
+	return true;
+}	
+//###################################################################################################################
+void	W25qxx_EraseChip(void)
+{
+	while(w25qxx.Lock==1)
+		W25qxx_Delay(1);
+	w25qxx.Lock=1;	
+	#if (_W25QXX_DEBUG==1)
+	uint32_t	StartTime=HAL_GetTick();	
+	printf("w25qxx EraseChip Begin...\r\n");
+	#endif
+	W25qxx_WriteEnable();
+	W25QXX_CS_LOW();
+  W25qxx_Spi(0xC7);
+  W25QXX_CS_HIGH();
+	W25qxx_WaitForWriteEnd();
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx EraseBlock done after %d ms!\r\n",HAL_GetTick()-StartTime);
+	#endif
+	W25qxx_Delay(10);
+	w25qxx.Lock=0;	
+}
+//###################################################################################################################
+void W25qxx_EraseSector(uint32_t SectorAddr)
+{
+	while(w25qxx.Lock==1)
+		W25qxx_Delay(1);
+	w25qxx.Lock=1;	
+	#if (_W25QXX_DEBUG==1)
+	uint32_t	StartTime=HAL_GetTick();	
+	printf("w25qxx EraseSector %d Begin...\r\n",SectorAddr);
+	#endif
+	W25qxx_WaitForWriteEnd();
+	SectorAddr = SectorAddr * w25qxx.SectorSize;
+  W25qxx_WriteEnable();
+  W25QXX_CS_LOW();
+  W25qxx_Spi(0x20);
+	if(w25qxx.ID>=ID_W25Q256)
+		W25qxx_Spi((SectorAddr & 0xFF000000) >> 24);
+  W25qxx_Spi((SectorAddr & 0xFF0000) >> 16);
+  W25qxx_Spi((SectorAddr & 0xFF00) >> 8);
+  W25qxx_Spi(SectorAddr & 0xFF);
+	W25QXX_CS_HIGH();
+  W25qxx_WaitForWriteEnd();
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx EraseSector done after %d ms\r\n",HAL_GetTick()-StartTime);
+	#endif
+	W25qxx_Delay(1);
+	w25qxx.Lock=0;
+}
+//###################################################################################################################
+void W25qxx_EraseBlock(uint32_t BlockAddr)
+{
+	while(w25qxx.Lock==1)
+		W25qxx_Delay(1);
+	w25qxx.Lock=1;	
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx EraseBlock %d Begin...\r\n",BlockAddr);
+	W25qxx_Delay(100);
+	uint32_t	StartTime=HAL_GetTick();	
+	#endif
+	W25qxx_WaitForWriteEnd();
+	BlockAddr = BlockAddr * w25qxx.SectorSize*16;
+  W25qxx_WriteEnable();
+  W25QXX_CS_LOW();
+  W25qxx_Spi(0xD8);
+	if(w25qxx.ID>=ID_W25Q256)
+		W25qxx_Spi((BlockAddr & 0xFF000000) >> 24);
+  W25qxx_Spi((BlockAddr & 0xFF0000) >> 16);
+  W25qxx_Spi((BlockAddr & 0xFF00) >> 8);
+  W25qxx_Spi(BlockAddr & 0xFF);
+	W25QXX_CS_HIGH();
+  W25qxx_WaitForWriteEnd();
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx EraseBlock done after %d ms\r\n",HAL_GetTick()-StartTime);
+	W25qxx_Delay(100);
+	#endif
+	W25qxx_Delay(1);
+	w25qxx.Lock=0;
+}
+//###################################################################################################################
+uint32_t	W25qxx_PageToSector(uint32_t	PageAddress)
+{
+	return ((PageAddress*w25qxx.PageSize)/w25qxx.SectorSize);
+}
+//###################################################################################################################
+uint32_t	W25qxx_PageToBlock(uint32_t	PageAddress)
+{
+	return ((PageAddress*w25qxx.PageSize)/w25qxx.BlockSize);
+}
+//###################################################################################################################
+uint32_t	W25qxx_SectorToBlock(uint32_t	SectorAddress)
+{
+	return ((SectorAddress*w25qxx.SectorSize)/w25qxx.BlockSize);
+}
+//###################################################################################################################
+uint32_t	W25qxx_SectorToPage(uint32_t	SectorAddress)
+{
+	return (SectorAddress*w25qxx.SectorSize)/w25qxx.PageSize;
+}
+//###################################################################################################################
+uint32_t	W25qxx_BlockToPage(uint32_t	BlockAddress)
+{
+	return (BlockAddress*w25qxx.BlockSize)/w25qxx.PageSize;
+}
+//###################################################################################################################
+bool 	W25qxx_IsEmptyPage(uint32_t Page_Address,uint32_t OffsetInByte,uint32_t NumByteToCheck_up_to_PageSize)
+{
+	while(w25qxx.Lock==1)
+	W25qxx_Delay(1);
+	w25qxx.Lock=1;	
+	if(((NumByteToCheck_up_to_PageSize+OffsetInByte)>w25qxx.PageSize)||(NumByteToCheck_up_to_PageSize==0))
+		NumByteToCheck_up_to_PageSize=w25qxx.PageSize-OffsetInByte;
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx CheckPage:%d, Offset:%d, Bytes:%d begin...\r\n",Page_Address,OffsetInByte,NumByteToCheck_up_to_PageSize);
+	W25qxx_Delay(100);
+	uint32_t	StartTime=HAL_GetTick();
+	#endif		
+	uint8_t	pBuffer[32];
+	uint32_t	WorkAddress;
+	uint32_t	i;
+	for(i=OffsetInByte; i<w25qxx.PageSize; i+=sizeof(pBuffer))
+	{
+		W25QXX_CS_LOW();
+		WorkAddress=(i+Page_Address*w25qxx.PageSize);
+		W25qxx_Spi(0x0B);
+		if(w25qxx.ID>=ID_W25Q256)
+			W25qxx_Spi((WorkAddress & 0xFF000000) >> 24);
+		W25qxx_Spi((WorkAddress & 0xFF0000) >> 16);
+		W25qxx_Spi((WorkAddress & 0xFF00) >> 8);
+		W25qxx_Spi(WorkAddress & 0xFF);
+		W25qxx_Spi(0);
+		HAL_SPI_Receive(&_W25QXX_SPI,pBuffer,sizeof(pBuffer),100);	
+		W25QXX_CS_HIGH();	
+		for(uint8_t x=0;x<sizeof(pBuffer);x++)
+		{
+			if(pBuffer[x]!=0xFF)
+				goto NOT_EMPTY;		
+		}			
+	}	
+	if((w25qxx.PageSize+OffsetInByte)%sizeof(pBuffer)!=0)
+	{
+		i-=sizeof(pBuffer);
+		for( ; i<w25qxx.PageSize; i++)
+		{
+			W25QXX_CS_LOW();
+			WorkAddress=(i+Page_Address*w25qxx.PageSize);
+			W25qxx_Spi(0x0B);
+			if(w25qxx.ID>=ID_W25Q256)
+				W25qxx_Spi((WorkAddress & 0xFF000000) >> 24);
+			W25qxx_Spi((WorkAddress & 0xFF0000) >> 16);
+			W25qxx_Spi((WorkAddress & 0xFF00) >> 8);
+			W25qxx_Spi(WorkAddress & 0xFF);
+			W25qxx_Spi(0);
+			HAL_SPI_Receive(&_W25QXX_SPI,pBuffer,1,100);	
+			W25QXX_CS_HIGH();	
+			if(pBuffer[0]!=0xFF)
+				goto NOT_EMPTY;
+		}
+	}	
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx CheckPage is Empty in %d ms\r\n",HAL_GetTick()-StartTime);
+	W25qxx_Delay(100);
+	#endif	
+	w25qxx.Lock=0;
+	return true;	
+	NOT_EMPTY:
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx CheckPage is Not Empty in %d ms\r\n",HAL_GetTick()-StartTime);
+	W25qxx_Delay(100);
+	#endif	
+	w25qxx.Lock=0;
+	return false;
+}
+//###################################################################################################################
+bool 	W25qxx_IsEmptySector(uint32_t Sector_Address,uint32_t OffsetInByte,uint32_t NumByteToCheck_up_to_SectorSize)
+{
+	while(w25qxx.Lock==1)
+	W25qxx_Delay(1);
+	w25qxx.Lock=1;	
+	if((NumByteToCheck_up_to_SectorSize>w25qxx.SectorSize)||(NumByteToCheck_up_to_SectorSize==0))
+		NumByteToCheck_up_to_SectorSize=w25qxx.SectorSize;
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx CheckSector:%d, Offset:%d, Bytes:%d begin...\r\n",Sector_Address,OffsetInByte,NumByteToCheck_up_to_SectorSize);
+	W25qxx_Delay(100);
+	uint32_t	StartTime=HAL_GetTick();
+	#endif		
+	uint8_t	pBuffer[32];
+	uint32_t	WorkAddress;
+	uint32_t	i;
+	for(i=OffsetInByte; i<w25qxx.SectorSize; i+=sizeof(pBuffer))
+	{
+		W25QXX_CS_LOW();
+		WorkAddress=(i+Sector_Address*w25qxx.SectorSize);
+		W25qxx_Spi(0x0B);
+		if(w25qxx.ID>=ID_W25Q256)
+			W25qxx_Spi((WorkAddress & 0xFF000000) >> 24);
+		W25qxx_Spi((WorkAddress & 0xFF0000) >> 16);
+		W25qxx_Spi((WorkAddress & 0xFF00) >> 8);
+		W25qxx_Spi(WorkAddress & 0xFF);
+		W25qxx_Spi(0);
+		HAL_SPI_Receive(&_W25QXX_SPI,pBuffer,sizeof(pBuffer),100);	
+		W25QXX_CS_HIGH();	
+		for(uint8_t x=0;x<sizeof(pBuffer);x++)
+		{
+			if(pBuffer[x]!=0xFF)
+				goto NOT_EMPTY;		
+		}			
+	}	
+	if((w25qxx.SectorSize+OffsetInByte)%sizeof(pBuffer)!=0)
+	{
+		i-=sizeof(pBuffer);
+		for( ; i<w25qxx.SectorSize; i++)
+		{
+			W25QXX_CS_LOW();
+			WorkAddress=(i+Sector_Address*w25qxx.SectorSize);
+			W25qxx_Spi(0x0B);
+			if(w25qxx.ID>=ID_W25Q256)
+				W25qxx_Spi((WorkAddress & 0xFF000000) >> 24);
+			W25qxx_Spi((WorkAddress & 0xFF0000) >> 16);
+			W25qxx_Spi((WorkAddress & 0xFF00) >> 8);
+			W25qxx_Spi(WorkAddress & 0xFF);
+			W25qxx_Spi(0);
+			HAL_SPI_Receive(&_W25QXX_SPI,pBuffer,1,100);	
+			W25QXX_CS_HIGH();	
+			if(pBuffer[0]!=0xFF)
+				goto NOT_EMPTY;
+		}
+	}	
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx CheckSector is Empty in %d ms\r\n",HAL_GetTick()-StartTime);
+	W25qxx_Delay(100);
+	#endif	
+	w25qxx.Lock=0;
+	return true;	
+	NOT_EMPTY:
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx CheckSector is Not Empty in %d ms\r\n",HAL_GetTick()-StartTime);
+	W25qxx_Delay(100);
+	#endif	
+	w25qxx.Lock=0;
+	return false;
+}
+//###################################################################################################################
+bool 	W25qxx_IsEmptyBlock(uint32_t Block_Address,uint32_t OffsetInByte,uint32_t NumByteToCheck_up_to_BlockSize)
+{
+	while(w25qxx.Lock==1)
+	W25qxx_Delay(1);
+	w25qxx.Lock=1;	
+	if((NumByteToCheck_up_to_BlockSize>w25qxx.BlockSize)||(NumByteToCheck_up_to_BlockSize==0))
+		NumByteToCheck_up_to_BlockSize=w25qxx.BlockSize;
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx CheckBlock:%d, Offset:%d, Bytes:%d begin...\r\n",Block_Address,OffsetInByte,NumByteToCheck_up_to_BlockSize);
+	W25qxx_Delay(100);
+	uint32_t	StartTime=HAL_GetTick();
+	#endif		
+	uint8_t	pBuffer[32];
+	uint32_t	WorkAddress;
+	uint32_t	i;
+	for(i=OffsetInByte; i<w25qxx.BlockSize; i+=sizeof(pBuffer))
+	{
+		W25QXX_CS_LOW();
+		WorkAddress=(i+Block_Address*w25qxx.BlockSize);
+		W25qxx_Spi(0x0B);
+		if(w25qxx.ID>=ID_W25Q256)
+			W25qxx_Spi((WorkAddress & 0xFF000000) >> 24);
+		W25qxx_Spi((WorkAddress & 0xFF0000) >> 16);
+		W25qxx_Spi((WorkAddress & 0xFF00) >> 8);
+		W25qxx_Spi(WorkAddress & 0xFF);
+		W25qxx_Spi(0);
+		HAL_SPI_Receive(&_W25QXX_SPI,pBuffer,sizeof(pBuffer),100);	
+		W25QXX_CS_HIGH();	
+		for(uint8_t x=0;x<sizeof(pBuffer);x++)
+		{
+			if(pBuffer[x]!=0xFF)
+				goto NOT_EMPTY;		
+		}			
+	}	
+	if((w25qxx.BlockSize+OffsetInByte)%sizeof(pBuffer)!=0)
+	{
+		i-=sizeof(pBuffer);
+		for( ; i<w25qxx.BlockSize; i++)
+		{
+			W25QXX_CS_LOW();
+			WorkAddress=(i+Block_Address*w25qxx.BlockSize);
+			W25qxx_Spi(0x0B);
+			if(w25qxx.ID>=ID_W25Q256)
+				W25qxx_Spi((WorkAddress & 0xFF000000) >> 24);
+			W25qxx_Spi((WorkAddress & 0xFF0000) >> 16);
+			W25qxx_Spi((WorkAddress & 0xFF00) >> 8);
+			W25qxx_Spi(WorkAddress & 0xFF);
+			W25qxx_Spi(0);
+			HAL_SPI_Receive(&_W25QXX_SPI,pBuffer,1,100);	
+			W25QXX_CS_HIGH();	
+			if(pBuffer[0]!=0xFF)
+				goto NOT_EMPTY;
+		}
+	}	
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx CheckBlock is Empty in %d ms\r\n",HAL_GetTick()-StartTime);
+	W25qxx_Delay(100);
+	#endif	
+	w25qxx.Lock=0;
+	return true;	
+	NOT_EMPTY:
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx CheckBlock is Not Empty in %d ms\r\n",HAL_GetTick()-StartTime);
+	W25qxx_Delay(100);
+	#endif	
+	w25qxx.Lock=0;
+	return false;
+}
+//###################################################################################################################
+void W25qxx_WriteByte(uint8_t pBuffer, uint32_t WriteAddr_inBytes)
+{
+	while(w25qxx.Lock==1)
+		W25qxx_Delay(1);
+	w25qxx.Lock=1;
+	#if (_W25QXX_DEBUG==1)
+	uint32_t	StartTime=HAL_GetTick();
+	printf("w25qxx WriteByte 0x%02X at address %d begin...",pBuffer,WriteAddr_inBytes);
+	#endif
+	W25qxx_WaitForWriteEnd();
+  W25qxx_WriteEnable();
+  W25QXX_CS_LOW();
+  W25qxx_Spi(0x02);
+	if(w25qxx.ID>=ID_W25Q256)
+		W25qxx_Spi((WriteAddr_inBytes & 0xFF000000) >> 24);
+  W25qxx_Spi((WriteAddr_inBytes & 0xFF0000) >> 16);
+  W25qxx_Spi((WriteAddr_inBytes & 0xFF00) >> 8);
+  W25qxx_Spi(WriteAddr_inBytes & 0xFF);
+  W25qxx_Spi(pBuffer);
+	W25QXX_CS_HIGH();
+  W25qxx_WaitForWriteEnd();
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx WriteByte done after %d ms\r\n",HAL_GetTick()-StartTime);
+	#endif
+	w25qxx.Lock=0;
+}
+//###################################################################################################################
+void 	W25qxx_WritePage(uint8_t *pBuffer	,uint32_t Page_Address,uint32_t OffsetInByte,uint32_t NumByteToWrite_up_to_PageSize)
+{
+	while(w25qxx.Lock==1)
+		W25qxx_Delay(1);
+	w25qxx.Lock=1;
+	if(((NumByteToWrite_up_to_PageSize+OffsetInByte)>w25qxx.PageSize)||(NumByteToWrite_up_to_PageSize==0))
+		NumByteToWrite_up_to_PageSize=w25qxx.PageSize-OffsetInByte;
+	if((OffsetInByte+NumByteToWrite_up_to_PageSize) > w25qxx.PageSize)
+		NumByteToWrite_up_to_PageSize = w25qxx.PageSize-OffsetInByte;
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx WritePage:%d, Offset:%d ,Writes %d Bytes, begin...\r\n",Page_Address,OffsetInByte,NumByteToWrite_up_to_PageSize);
+	W25qxx_Delay(100);
+	uint32_t	StartTime=HAL_GetTick();
+	#endif	
+	W25qxx_WaitForWriteEnd();
+  W25qxx_WriteEnable();
+  W25QXX_CS_LOW();
+  W25qxx_Spi(0x02);
+	Page_Address = (Page_Address*w25qxx.PageSize)+OffsetInByte;	
+	if(w25qxx.ID>=ID_W25Q256)
+		W25qxx_Spi((Page_Address & 0xFF000000) >> 24);
+  W25qxx_Spi((Page_Address & 0xFF0000) >> 16);
+  W25qxx_Spi((Page_Address & 0xFF00) >> 8);
+  W25qxx_Spi(Page_Address&0xFF);
+	HAL_SPI_Transmit(&_W25QXX_SPI,pBuffer,NumByteToWrite_up_to_PageSize,100);	
+	W25QXX_CS_HIGH();
+  W25qxx_WaitForWriteEnd();
+	#if (_W25QXX_DEBUG==1)
+	StartTime = HAL_GetTick()-StartTime; 
+	for(uint32_t i=0;i<NumByteToWrite_up_to_PageSize ; i++)
+	{
+		if((i%8==0)&&(i>2))
+		{
+			printf("\r\n");
+			W25qxx_Delay(10);			
+		}
+		printf("0x%02X,",pBuffer[i]);		
+	}	
+	printf("\r\n");
+	printf("w25qxx WritePage done after %d ms\r\n",StartTime);
+	W25qxx_Delay(100);
+	#endif	
+	W25qxx_Delay(1);
+	w25qxx.Lock=0;
+}
+//###################################################################################################################
+void 	W25qxx_WriteSector(uint8_t *pBuffer	,uint32_t Sector_Address,uint32_t OffsetInByte	,uint32_t NumByteToWrite_up_to_SectorSize)
+{
+	if((NumByteToWrite_up_to_SectorSize>w25qxx.SectorSize)||(NumByteToWrite_up_to_SectorSize==0))
+		NumByteToWrite_up_to_SectorSize=w25qxx.SectorSize;
+	#if (_W25QXX_DEBUG==1)
+	printf("+++w25qxx WriteSector:%d, Offset:%d ,Write %d Bytes, begin...\r\n",Sector_Address,OffsetInByte,NumByteToWrite_up_to_SectorSize);
+	W25qxx_Delay(100);
+	#endif	
+	if(OffsetInByte>=w25qxx.SectorSize)
+	{
+		#if (_W25QXX_DEBUG==1)
+		printf("---w25qxx WriteSector Faild!\r\n");
+		W25qxx_Delay(100);
+		#endif	
+		return;
+	}	
+	uint32_t	StartPage;
+	int32_t		BytesToWrite;
+	uint32_t	LocalOffset;	
+	if((OffsetInByte+NumByteToWrite_up_to_SectorSize) > w25qxx.SectorSize)
+		BytesToWrite = w25qxx.SectorSize-OffsetInByte;
+	else
+		BytesToWrite = NumByteToWrite_up_to_SectorSize;	
+	StartPage = W25qxx_SectorToPage(Sector_Address)+(OffsetInByte/w25qxx.PageSize);
+	LocalOffset = OffsetInByte%w25qxx.PageSize;	
+	do
+	{		
+		W25qxx_WritePage(pBuffer,StartPage,LocalOffset,BytesToWrite);
+		StartPage++;
+		BytesToWrite-=w25qxx.PageSize-LocalOffset;
+		pBuffer+=w25qxx.PageSize;	
+		LocalOffset=0;
+	}while(BytesToWrite>0);		
+	#if (_W25QXX_DEBUG==1)
+	printf("---w25qxx WriteSector Done\r\n");
+	W25qxx_Delay(100);
+	#endif	
+}
+//###################################################################################################################
+void 	W25qxx_WriteBlock	(uint8_t* pBuffer ,uint32_t Block_Address	,uint32_t OffsetInByte	,uint32_t	NumByteToWrite_up_to_BlockSize)
+{
+	if((NumByteToWrite_up_to_BlockSize>w25qxx.BlockSize)||(NumByteToWrite_up_to_BlockSize==0))
+		NumByteToWrite_up_to_BlockSize=w25qxx.BlockSize;
+	#if (_W25QXX_DEBUG==1)
+	printf("+++w25qxx WriteBlock:%d, Offset:%d ,Write %d Bytes, begin...\r\n",Block_Address,OffsetInByte,NumByteToWrite_up_to_BlockSize);
+	W25qxx_Delay(100);
+	#endif	
+	if(OffsetInByte>=w25qxx.BlockSize)
+	{
+		#if (_W25QXX_DEBUG==1)
+		printf("---w25qxx WriteBlock Faild!\r\n");
+		W25qxx_Delay(100);
+		#endif	
+		return;
+	}	
+	uint32_t	StartPage;
+	int32_t		BytesToWrite;
+	uint32_t	LocalOffset;	
+	if((OffsetInByte+NumByteToWrite_up_to_BlockSize) > w25qxx.BlockSize)
+		BytesToWrite = w25qxx.BlockSize-OffsetInByte;
+	else
+		BytesToWrite = NumByteToWrite_up_to_BlockSize;	
+	StartPage = W25qxx_BlockToPage(Block_Address)+(OffsetInByte/w25qxx.PageSize);
+	LocalOffset = OffsetInByte%w25qxx.PageSize;	
+	do
+	{		
+		W25qxx_WritePage(pBuffer,StartPage,LocalOffset,BytesToWrite);
+		StartPage++;
+		BytesToWrite-=w25qxx.PageSize-LocalOffset;
+		pBuffer+=w25qxx.PageSize;	
+		LocalOffset=0;
+	}while(BytesToWrite>0);		
+	#if (_W25QXX_DEBUG==1)
+	printf("---w25qxx WriteBlock Done\r\n");
+	W25qxx_Delay(100);
+	#endif	
+}
+//###################################################################################################################
+void 	W25qxx_ReadByte(uint8_t *pBuffer,uint32_t Bytes_Address)
+{
+	while(w25qxx.Lock==1)
+		W25qxx_Delay(1);
+	w25qxx.Lock=1;
+	#if (_W25QXX_DEBUG==1)
+	uint32_t	StartTime=HAL_GetTick();
+	printf("w25qxx ReadByte at address %d begin...\r\n",Bytes_Address);
+	#endif
+	W25QXX_CS_LOW();
+  W25qxx_Spi(0x0B);
+	if(w25qxx.ID>=ID_W25Q256)
+		W25qxx_Spi((Bytes_Address & 0xFF000000) >> 24);
+  W25qxx_Spi((Bytes_Address & 0xFF0000) >> 16);
+  W25qxx_Spi((Bytes_Address& 0xFF00) >> 8);
+  W25qxx_Spi(Bytes_Address & 0xFF);
+	W25qxx_Spi(0);
+	*pBuffer = W25qxx_Spi(W25QXX_DUMMY_BYTE);
+	W25QXX_CS_HIGH();	
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx ReadByte 0x%02X done after %d ms\r\n",*pBuffer,HAL_GetTick()-StartTime);
+	#endif
+	w25qxx.Lock=0;
+}
+//###################################################################################################################
+void W25qxx_ReadBytes(uint8_t* pBuffer, uint32_t ReadAddr, uint32_t NumByteToRead)
+{
+	while(w25qxx.Lock==1)
+		W25qxx_Delay(1);
+	w25qxx.Lock=1;
+	#if (_W25QXX_DEBUG==1)
+	uint32_t	StartTime=HAL_GetTick();
+	printf("w25qxx ReadBytes at Address:%d, %d Bytes  begin...\r\n",ReadAddr,NumByteToRead);
+	#endif	
+	W25QXX_CS_LOW();
+	W25qxx_Spi(0x0B);
+	if(w25qxx.ID>=ID_W25Q256)
+		W25qxx_Spi((ReadAddr & 0xFF000000) >> 24);
+  W25qxx_Spi((ReadAddr & 0xFF0000) >> 16);
+  W25qxx_Spi((ReadAddr& 0xFF00) >> 8);
+  W25qxx_Spi(ReadAddr & 0xFF);
+	W25qxx_Spi(0);
+	HAL_SPI_Receive(&_W25QXX_SPI,pBuffer,NumByteToRead,2000);	
+	W25QXX_CS_HIGH();
+	#if (_W25QXX_DEBUG==1)
+	StartTime = HAL_GetTick()-StartTime; 
+	for(uint32_t i=0;i<NumByteToRead ; i++)
+	{
+		if((i%8==0)&&(i>2))
+		{
+			printf("\r\n");
+			W25qxx_Delay(10);
+		}
+		printf("0x%02X,",pBuffer[i]);		
+	}
+	printf("\r\n");
+	printf("w25qxx ReadBytes done after %d ms\r\n",StartTime);
+	W25qxx_Delay(100);
+	#endif	
+	W25qxx_Delay(1);
+	w25qxx.Lock=0;
+}
+//###################################################################################################################
+void 	W25qxx_ReadPage(uint8_t *pBuffer,uint32_t Page_Address,uint32_t OffsetInByte,uint32_t NumByteToRead_up_to_PageSize)
+{
+	while(w25qxx.Lock==1)
+		W25qxx_Delay(1);
+	w25qxx.Lock=1;
+	if((NumByteToRead_up_to_PageSize>w25qxx.PageSize)||(NumByteToRead_up_to_PageSize==0))
+		NumByteToRead_up_to_PageSize=w25qxx.PageSize;
+	if((OffsetInByte+NumByteToRead_up_to_PageSize) > w25qxx.PageSize)
+		NumByteToRead_up_to_PageSize = w25qxx.PageSize-OffsetInByte;
+	#if (_W25QXX_DEBUG==1)
+	printf("w25qxx ReadPage:%d, Offset:%d ,Read %d Bytes, begin...\r\n",Page_Address,OffsetInByte,NumByteToRead_up_to_PageSize);
+	W25qxx_Delay(100);
+	uint32_t	StartTime=HAL_GetTick();
+	#endif	
+	Page_Address = Page_Address*w25qxx.PageSize+OffsetInByte;
+	W25QXX_CS_LOW();
+	W25qxx_Spi(0x0B);
+	if(w25qxx.ID>=ID_W25Q256)
+		W25qxx_Spi((Page_Address & 0xFF000000) >> 24);
+  W25qxx_Spi((Page_Address & 0xFF0000) >> 16);
+  W25qxx_Spi((Page_Address& 0xFF00) >> 8);
+  W25qxx_Spi(Page_Address & 0xFF);
+	W25qxx_Spi(0);
+	HAL_SPI_Receive(&_W25QXX_SPI,pBuffer,NumByteToRead_up_to_PageSize,100);	
+	W25QXX_CS_HIGH();
+	#if (_W25QXX_DEBUG==1)
+	StartTime = HAL_GetTick()-StartTime; 
+	for(uint32_t i=0;i<NumByteToRead_up_to_PageSize ; i++)
+	{
+		if((i%8==0)&&(i>2))
+		{
+			printf("\r\n");
+			W25qxx_Delay(10);
+		}
+		printf("0x%02X,",pBuffer[i]);		
+	}	
+	printf("\r\n");
+	printf("w25qxx ReadPage done after %d ms\r\n",StartTime);
+	W25qxx_Delay(100);
+	#endif	
+	W25qxx_Delay(1);
+	w25qxx.Lock=0;
+}
+//###################################################################################################################
+void 	W25qxx_ReadSector(uint8_t *pBuffer,uint32_t Sector_Address,uint32_t OffsetInByte,uint32_t NumByteToRead_up_to_SectorSize)
+{	
+	if((NumByteToRead_up_to_SectorSize>w25qxx.SectorSize)||(NumByteToRead_up_to_SectorSize==0))
+		NumByteToRead_up_to_SectorSize=w25qxx.SectorSize;
+	#if (_W25QXX_DEBUG==1)
+	printf("+++w25qxx ReadSector:%d, Offset:%d ,Read %d Bytes, begin...\r\n",Sector_Address,OffsetInByte,NumByteToRead_up_to_SectorSize);
+	W25qxx_Delay(100);
+	#endif	
+	if(OffsetInByte>=w25qxx.SectorSize)
+	{
+		#if (_W25QXX_DEBUG==1)
+		printf("---w25qxx ReadSector Faild!\r\n");
+		W25qxx_Delay(100);
+		#endif	
+		return;
+	}	
+	uint32_t	StartPage;
+	int32_t		BytesToRead;
+	uint32_t	LocalOffset;	
+	if((OffsetInByte+NumByteToRead_up_to_SectorSize) > w25qxx.SectorSize)
+		BytesToRead = w25qxx.SectorSize-OffsetInByte;
+	else
+		BytesToRead = NumByteToRead_up_to_SectorSize;	
+	StartPage = W25qxx_SectorToPage(Sector_Address)+(OffsetInByte/w25qxx.PageSize);
+	LocalOffset = OffsetInByte%w25qxx.PageSize;	
+	do
+	{		
+		W25qxx_ReadPage(pBuffer,StartPage,LocalOffset,BytesToRead);
+		StartPage++;
+		BytesToRead-=w25qxx.PageSize-LocalOffset;
+		pBuffer+=w25qxx.PageSize;	
+		LocalOffset=0;
+	}while(BytesToRead>0);		
+	#if (_W25QXX_DEBUG==1)
+	printf("---w25qxx ReadSector Done\r\n");
+	W25qxx_Delay(100);
+	#endif	
+}
+//###################################################################################################################
+void 	W25qxx_ReadBlock(uint8_t* pBuffer,uint32_t Block_Address,uint32_t OffsetInByte,uint32_t	NumByteToRead_up_to_BlockSize)
+{
+	if((NumByteToRead_up_to_BlockSize>w25qxx.BlockSize)||(NumByteToRead_up_to_BlockSize==0))
+		NumByteToRead_up_to_BlockSize=w25qxx.BlockSize;
+	#if (_W25QXX_DEBUG==1)
+	printf("+++w25qxx ReadBlock:%d, Offset:%d ,Read %d Bytes, begin...\r\n",Block_Address,OffsetInByte,NumByteToRead_up_to_BlockSize);
+	W25qxx_Delay(100);
+	#endif	
+	if(OffsetInByte>=w25qxx.BlockSize)
+	{
+		#if (_W25QXX_DEBUG==1)
+		printf("w25qxx ReadBlock Faild!\r\n");
+		W25qxx_Delay(100);
+		#endif	
+		return;
+	}	
+	uint32_t	StartPage;
+	int32_t		BytesToRead;
+	uint32_t	LocalOffset;	
+	if((OffsetInByte+NumByteToRead_up_to_BlockSize) > w25qxx.BlockSize)
+		BytesToRead = w25qxx.BlockSize-OffsetInByte;
+	else
+		BytesToRead = NumByteToRead_up_to_BlockSize;	
+	StartPage = W25qxx_BlockToPage(Block_Address)+(OffsetInByte/w25qxx.PageSize);
+	LocalOffset = OffsetInByte%w25qxx.PageSize;	
+	do
+	{		
+		W25qxx_ReadPage(pBuffer,StartPage,LocalOffset,BytesToRead);
+		StartPage++;
+		BytesToRead-=w25qxx.PageSize-LocalOffset;
+		pBuffer+=w25qxx.PageSize;	
+		LocalOffset=0;
+	}while(BytesToRead>0);		
+	#if (_W25QXX_DEBUG==1)
+	printf("---w25qxx ReadBlock Done\r\n");
+	W25qxx_Delay(100);
+	#endif	
+}
+#endif //-----------------------------------USE_W25QXX_SPI------------------------------------------//
+//###################################################################################################################
+

+ 135 - 0
obj/obj_spi_w25qxx.h

@@ -0,0 +1,135 @@
+#ifdef USE_W25QXX_SPI
+
+#ifndef OBJ_SPI_W25QXX_H
+#define OBJ_SPI_W25QXX_H
+
+#include <stdbool.h>
+#include "stm32f7xx_hal.h"
+#include "stm32f7xx_hal_spi.h"
+
+
+#define SPIx                             SPI2
+#define SPIx_CLK_ENABLE()                __HAL_RCC_SPI2_CLK_ENABLE()
+#define SPIx_SCK_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOB_CLK_ENABLE()
+#define SPIx_MISO_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE() 
+#define SPIx_MOSI_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE() 
+#define SPIx_CS_GPIO_CLK_ENABLE()        __HAL_RCC_GPIOD_CLK_ENABLE() 
+
+#define SPIx_FORCE_RESET()               __HAL_RCC_SPI2_FORCE_RESET()
+#define SPIx_RELEASE_RESET()             __HAL_RCC_SPI2_RELEASE_RESET()
+
+/* Definition for SPIx Pins */
+#define SPIx_SCK_PIN                   GPIO_PIN_13
+#define SPIx_SCK_GPIO_PORT             GPIOB
+
+#define SPIx_MISO_PIN                  GPIO_PIN_14
+#define SPIx_MISO_GPIO_PORT            GPIOB
+
+#define SPIx_MOSI_PIN                  GPIO_PIN_15
+#define SPIx_MOSI_GPIO_PORT            GPIOB
+
+#define	_W25QXX_SPI										hspi_w25q
+#if 0
+#define	_W25QXX_CS_GPIO								GPIOD					
+#define	_W25QXX_CS_PIN								GPIO_PIN_8
+#else
+#define	_W25QXX_CS_GPIO								GPIOB					
+#define	_W25QXX_CS_PIN								GPIO_PIN_12
+#endif
+#define	_W25QXX_USE_FREERTOS					0
+#define	_W25QXX_DEBUG									0
+
+#define W25QXX_CS_HIGH()							{HAL_GPIO_WritePin(_W25QXX_CS_GPIO,_W25QXX_CS_PIN,GPIO_PIN_SET);\
+																			 delay_us(1);}while(0);
+#define W25QXX_CS_LOW()								HAL_GPIO_WritePin(_W25QXX_CS_GPIO,_W25QXX_CS_PIN,GPIO_PIN_RESET)
+
+typedef enum
+{
+	ID_W25Q10	=	1,
+	ID_W25Q20,
+	ID_W25Q40,
+	ID_W25Q80,
+	ID_W25Q16,
+	ID_W25Q32,
+	ID_W25Q64,
+	ID_W25Q128,
+	ID_W25Q256,
+	ID_W25Q512,
+	
+}W25QXX_ID_t;
+
+typedef struct
+{
+	W25QXX_ID_t	ID;
+	uint8_t			UniqID[8];
+
+	uint16_t    PageSize;
+	uint32_t		PageCount;
+	uint32_t		SectorSize;
+	uint32_t		SectorCount;
+	uint32_t		BlockSize;
+	uint32_t		BlockCount; // so block trong chip flash
+
+	uint32_t		CapacityInKiloByte;
+	
+	uint8_t	StatusRegister1;
+	uint8_t	StatusRegister2;
+	uint8_t	StatusRegister3;
+	
+	uint8_t	Lock;
+	
+}w25qxx_t;
+
+extern SPI_HandleTypeDef _W25QXX_SPI;
+extern w25qxx_t	w25qxx;
+
+//############################################################################
+// in Page,Sector and block read/write functions, can put 0 to read maximum bytes 
+//############################################################################
+
+bool	W25qxx_Init(void);
+void  W25qxx_Cs_Init(void);
+void  W25qxx_Spi_Init(void);
+
+uint32_t  W25qxx_ReadID(void);
+
+// BLOCK > SECTOR , w25q128 => 16 sector = 1 block
+void	W25qxx_EraseChip(void);
+void 	W25qxx_EraseSector(uint32_t SectorAddr);
+void 	W25qxx_EraseBlock(uint32_t BlockAddr);
+
+
+// tra ve so page trong 1 sector, so page trong 1 block.....
+uint32_t	W25qxx_PageToSector(uint32_t	PageAddress);
+uint32_t	W25qxx_PageToBlock(uint32_t	PageAddress);
+uint32_t	W25qxx_SectorToBlock(uint32_t	SectorAddress);
+uint32_t	W25qxx_SectorToPage(uint32_t	SectorAddress);
+uint32_t	W25qxx_BlockToPage(uint32_t	BlockAddress);
+
+
+// kiem tra bo nho co con trong khong, neu con trong có the ghi data vao
+// offset_in_byte byte bat dau ghi
+// numbyte_to_checkup => so byte can check, ghi trong page/sector/block
+bool 	W25qxx_IsEmptyPage(uint32_t Page_Address,uint32_t OffsetInByte,uint32_t NumByteToCheck_up_to_PageSize);
+bool 	W25qxx_IsEmptySector(uint32_t Sector_Address,uint32_t OffsetInByte,uint32_t NumByteToCheck_up_to_SectorSize);
+bool 	W25qxx_IsEmptyBlock(uint32_t Block_Address,uint32_t OffsetInByte,uint32_t NumByteToCheck_up_to_BlockSize);
+
+
+
+void 	W25qxx_WriteByte(uint8_t pBuffer,uint32_t Bytes_Address);
+void 	W25qxx_WritePage(uint8_t *pBuffer	,uint32_t Page_Address,uint32_t OffsetInByte,uint32_t NumByteToWrite_up_to_PageSize);
+void 	W25qxx_WriteSector(uint8_t *pBuffer,uint32_t Sector_Address,uint32_t OffsetInByte,uint32_t NumByteToWrite_up_to_SectorSize);
+void 	W25qxx_WriteBlock(uint8_t* pBuffer,uint32_t Block_Address,uint32_t OffsetInByte,uint32_t NumByteToWrite_up_to_BlockSize);
+
+void 	W25qxx_ReadByte(uint8_t *pBuffer,uint32_t Bytes_Address);
+void 	W25qxx_ReadBytes(uint8_t *pBuffer,uint32_t ReadAddr,uint32_t NumByteToRead);
+void 	W25qxx_ReadPage(uint8_t *pBuffer,uint32_t Page_Address,uint32_t OffsetInByte,uint32_t NumByteToRead_up_to_PageSize);
+void 	W25qxx_ReadSector(uint8_t *pBuffer,uint32_t Sector_Address,uint32_t OffsetInByte,uint32_t NumByteToRead_up_to_SectorSize);
+void 	W25qxx_ReadBlock(uint8_t* pBuffer,uint32_t Block_Address,uint32_t OffsetInByte,uint32_t	NumByteToRead_up_to_BlockSize);
+//############################################################################
+
+
+
+#endif //----------------------OBJ_SPI_W25QXX_H-----------------------------//
+
+#endif //----------------------USE_W25QXX_SPI-------------------------------//