#include "w25qxx.h"

#define GET_CHIP_SELECT() 	LL_GPIO_IsOutputPinSet(GPIOC,LL_GPIO_PIN_1)

unsigned short W25QXX_TYPE = 1;

unsigned short norflash_get_busy_flag[2] = {0,0};
unsigned short norflash_err_flag[2] = {0,0};

//4Kbytes为一个Sector
//16个扇区为1个Block
//W25Q128
//容量为16M字节,共有256个Block,4096个Sector 

static void wait(void)
{
	__nop();
	__nop();
	__nop();
	__nop();
	__nop();
	__nop();
}

//初始化SPI FLASH的IO口
void W25QXX_Init(void)
{ 
	W25QXX_Write_Enable();
	W25QXX_Reset();
	HAL_Delay(10);
	
	W25QXX_Write_Enable();
 	W25QXX_Qspi_Enable();			//使能QSPI模式
	W25QXX_TYPE = W25QXX_ReadID();	//读取FLASH ID.
	W25QXX_Wait_Busy(); 
	HAL_Delay(2);
}  

void W25QXX_Qspi_Enable(void)
{
	unsigned char stareg2;
	stareg2=W25QXX_ReadSR(2);	
	
	do{
		W25QXX_Write_Enable();
		stareg2 |= 0x02;	
		W25QXX_Write_SR(2,stareg2);
		HAL_Delay(1);
		stareg2 = W25QXX_ReadSR(2);
	}while(!(stareg2 & 0x02));
}

void W25QXX_Qspi_Disable(void)
{ 
	unsigned char stareg2;
	stareg2=W25QXX_ReadSR(2);	
	
	do{
		W25QXX_Write_Enable();
		stareg2 &=~0x02;	
		W25QXX_Write_SR(2,stareg2);
		HAL_Delay(1);
		stareg2 = W25QXX_ReadSR(2);
	}while(stareg2 & 0x02);
}

unsigned char W25QXX_ReadSR(unsigned char regno)   
{  
	unsigned char byte=0,command=0;
	
  switch(regno){
		case 1:
			command = W25Qx_CMD_ReadStatusReg1;    //读状态寄存器1指令
			break;
		case 2:
			command = W25Qx_CMD_ReadStatusReg2;    //读状态寄存器2指令
			break;
		case 3:
			command = W25Qx_CMD_ReadStatusReg3;    //读状态寄存器3指令
			break;
		default:
			command = W25Qx_CMD_ReadStatusReg1;    
			break;
	}
	wait();
	QSPI_Send_CMD(command,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_1_LINE);
	QSPI_Receive(&byte,1);	        
	return byte;   
}   

//写W25QXX状态寄存器
void W25QXX_Write_SR(unsigned char regno,unsigned char sr)   
{   
	unsigned char command=0;
	
	switch(regno){
		case 1:
			command = W25Qx_CMD_WriteStatusReg1;    //写状态寄存器1指令
			break;
		case 2:
			command = W25Qx_CMD_WriteStatusReg2;    //写状态寄存器2指令
			break;
		case 3:
			command = W25Qx_CMD_WriteStatusReg3;    //写状态寄存器3指令
			break;
		default:
			command = W25Qx_CMD_WriteStatusReg1;    
			break;
	}   
	QSPI_Send_CMD(command,0,0, QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_1_LINE);
	QSPI_Transmit(&sr,1);	         	      
}  
 
void W25QXX_Write_Enable(void)   
{
	unsigned char stareg1;
	stareg1=W25QXX_ReadSR(1);		//先读出状态寄存器1的原始值
	do{
		QSPI_Send_CMD(W25Qx_CMD_WriteEnable,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);	
		wait();
		stareg1 = W25QXX_ReadSR(1);
	}while(!(stareg1 & 0x02));		//WE位未使能
} 

void W25QXX_Write_Disable(void)   
{  
	QSPI_Send_CMD(W25Qx_CMD_WriteDisable,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);
} 

//返回值如下:				   
//0XEF13,表示芯片型号为W25Q80  
//0XEF14,表示芯片型号为W25Q16    
//0XEF15,表示芯片型号为W25Q32  
//0XEF16,表示芯片型号为W25Q64 
//0XEF17,表示芯片型号为W25Q128 	  
//0XEF18,表示芯片型号为W25Q256
unsigned short W25QXX_ReadID(void)
{
	unsigned char temp[2];
	unsigned short deviceid;
	QSPI_Send_CMD(W25Qx_CMD_ManufactDeviceID,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_24_BITS,QSPI_DATA_1_LINE);
	QSPI_Receive(temp,2);
	deviceid=(temp[0]<<8)|temp[1];
	return deviceid;
}    

//读取SPI FLASH,仅支持QPI模式  
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(最大32bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(unsigned char* pBuffer,unsigned int ReadAddr,unsigned short NumByteToRead)   
{
	if((ReadAddr + NumByteToRead) > NOR_FLASH_SIZE)return;
	
	QSPI_Send_CMD(W25Qx_CMD_Quad_Read,ReadAddr,8,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_24_BITS,QSPI_DATA_4_LINES);
	QSPI_Receive(pBuffer,NumByteToRead); 
}  

//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(最大32bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!	 
void W25QXX_Write_Page(unsigned char* pBuffer,unsigned int WriteAddr,unsigned short NumByteToWrite)
{
	if((WriteAddr + NumByteToWrite) > NOR_FLASH_SIZE)return;
	
	W25QXX_Write_Enable();	
	QSPI_Send_CMD(W25Qx_CMD_Quad_PageProgram,WriteAddr,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_24_BITS,QSPI_DATA_4_LINES);
	QSPI_Transmit(pBuffer,NumByteToWrite);
	wait();
	W25QXX_Wait_Busy();
} 

void W25QXX_Write_Byte_no_wait(unsigned char* pBuffer,unsigned int WriteAddr,unsigned short NumByteToWrite)
{
	if(((WriteAddr&0xFF) + NumByteToWrite) > NOR_PAGE_SIZE)return;
	
	W25QXX_Write_Enable();
	QSPI_Send_CMD(W25Qx_CMD_Quad_PageProgram,WriteAddr,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_24_BITS,QSPI_DATA_4_LINES);
	QSPI_Transmit(pBuffer,NumByteToWrite);
	wait();
}

//无检验写SPI FLASH 
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能 
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(最大32bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void W25QXX_Write_NoCheck(unsigned char* pBuffer,unsigned int WriteAddr,unsigned short NumByteToWrite)   
{ 			 		 
	unsigned short pageremain;	 
  
	if((WriteAddr + NumByteToWrite) > NOR_FLASH_SIZE)return;
	
	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:开始写入的地址(最大32bit)						
//NumByteToWrite:要写入的字节数(最大65535)   
unsigned char W25QXX_BUFFER[4096];		 
void W25QXX_Write(unsigned char* pBuffer,unsigned int WriteAddr,unsigned short NumByteToWrite)   
{ 
	unsigned int secpos;
	unsigned short secoff;
	unsigned short secremain;	   
 	unsigned short i;    
	unsigned char * W25QXX_BUF;	
  
	if((WriteAddr + NumByteToWrite) > NOR_FLASH_SIZE)return;
	
	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*4096);//擦除这个扇区
			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();   
	QSPI_Send_CMD(W25Qx_CMD_ChipErase,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);
	HAL_Delay(100);
	W25QXX_Wait_Busy();	
} 

void W25QXX_Erase_Chip_no_wait(void)   
{                                   
  W25QXX_Write_Enable();					//SET WEL 
  W25QXX_Wait_Busy();   
	QSPI_Send_CMD(W25Qx_CMD_ChipErase,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);
	__nop();
} 

//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个扇区的最少时间:150ms
void W25QXX_Erase_Sector(unsigned int addr)   
{  
	unsigned int addr_sector = addr & 0x00FFF000;
	
  W25QXX_Write_Enable();                  //SET WEL 	 
  W25QXX_Wait_Busy();  
	QSPI_Send_CMD(W25Qx_CMD_SectorErase_4K,addr_sector,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_24_BITS,QSPI_DATA_NONE);
	HAL_Delay(5);
  W25QXX_Wait_Busy();   
}

void W25QXX_Erase_Sector_4k_no_wait(unsigned int addr)   
{  
	unsigned int addr_sector = addr & 0x00FFF000;
	
  W25QXX_Write_Enable();                  //SET WEL 	 
  W25QXX_Wait_Busy();  
	QSPI_Send_CMD(W25Qx_CMD_SectorErase_4K,addr_sector,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_24_BITS,QSPI_DATA_NONE);
	__nop();
}

void W25QXX_Erase_Sector_32k_no_wait(unsigned int addr)   
{  
	unsigned int addr_sector = addr & 0x00FF8000;
	
  W25QXX_Write_Enable();                  //SET WEL 	 
  W25QXX_Wait_Busy();  
	QSPI_Send_CMD(W25Qx_CMD_SectorErase_32K,addr_sector,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_24_BITS,QSPI_DATA_NONE);
	__nop();
}

void W25QXX_Erase_Sector_64k_no_wait(unsigned int addr)   
{  
	unsigned int addr_sector = addr & 0x00FF0000;
	
  W25QXX_Write_Enable();                  //SET WEL 	 
  W25QXX_Wait_Busy();  
	QSPI_Send_CMD(W25Qx_CMD_SectorErase_64K,addr_sector,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_24_BITS,QSPI_DATA_NONE);
	__nop();
}

//等待空闲
void W25QXX_Wait_Busy(void)   
{   
	while((W25QXX_ReadSR(1)&0x01)==0x01){
		LL_IWDG_ReloadCounter(IWDG1);   // 等待BUSY位清空
		
		norflash_get_busy_flag[GET_CHIP_SELECT()] = 1;
		if(norflash_err_flag[GET_CHIP_SELECT()])return;
			
		wait();
	}
	norflash_get_busy_flag[GET_CHIP_SELECT()] = 0;
} 

void W25QXX_Reset(void)   
{   
	QSPI_Send_CMD(W25Qx_CMD_Reset_Enble,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);	
	QSPI_Send_CMD(W25Qx_CMD_Reset_Memory,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);	
} 
