#include "sr_spi.h"
#include "sr_shell.h"
#include "sr_log.h"
#include "sr_gpio.h"

#include <string.h>

//W25q128测试程序

//指令表
#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 


void W25QXX_Erase_Sector(SR_DEVICE_T *dev,uint32_t Dst_Addr);  


//读取芯片ID
//返回值如下:				   
//0XEF13,表示芯片型号为W25Q80  
//0XEF14,表示芯片型号为W25Q16    
//0XEF15,表示芯片型号为W25Q32  
//0XEF16,表示芯片型号为W25Q64 
//0XEF17,表示芯片型号为W25Q128 	  
//0XEF18,表示芯片型号为W25Q256
uint16_t W25QXX_ReadID(SR_DEVICE_T *dev)
{
	uint16_t Temp = 0;	  
	uint32_t cmd = 0x90000000;  
	uint8_t rxBuf[2]={0}; 
  int ret =-1;	
	

	ret= SR_HalSpiReadReg(dev, cmd, 4, rxBuf, 2);
	SR_LOG(INFO,"ReadReg,ret=%d ",ret);
	
	Temp|=rxBuf[0]<<8;  
	Temp|=rxBuf[1];	
			    
	return Temp;
}   
//读取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(SR_DEVICE_T *dev,uint8_t regno)   
{  
	uint8_t rxBuf=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;
    }    

	SR_HalSpiReadReg(dev, command, 1, &rxBuf, 1);
	return rxBuf;   
} 


//W25QXX写使能	
//将WEL置位   
void W25QXX_Write_Enable(SR_DEVICE_T *dev)   
{
  uint32_t cmd = W25X_WriteEnable;     
	SR_HalSpiWriteReg(dev, cmd, 1, SR_NULL, 0);  //发送写使能                        	      
} 

//等待空闲
void W25QXX_Wait_Busy(SR_DEVICE_T *dev)   
{   
	
	while((W25QXX_ReadSR(dev,1)&0x01)==0x01);   // 等待BUSY位清空
}  

//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!	 
void W25QXX_Write_Page(SR_DEVICE_T *dev,uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
	uint8_t  cmd =0;
	uint8_t addr[4]={0};
	W25QXX_Write_Enable(dev);             //SET WEL 

	cmd = W25X_PageProgram;	
	SR_HalSpiWriteRead(dev,&cmd,SR_NULL,1,SR_SPI_CS_SEL); //发送写页命令

	addr[0]= (uint8_t)((WriteAddr)>>16);  //发送24bit地址 
	addr[1]= (uint8_t)((WriteAddr)>>8);
	addr[2]= (uint8_t)(WriteAddr);

	SR_HalSpiWriteRead(dev,addr,SR_NULL,3,0);
	 
	SR_HalSpiWriteRead(dev,pBuffer,SR_NULL,NumByteToWrite,SR_SPI_CS_RLS);//循环写数

	W25QXX_Wait_Busy(dev);					   //等待写入结束
} 


//读取SPI FLASH  
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(SR_DEVICE_T *dev,uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)   
{ 
 	uint8_t  cmd =0;
	uint8_t addr[4]={0};
	
	cmd = W25X_ReadData;	
	SR_HalSpiWriteRead(dev,&cmd,SR_NULL,1,SR_SPI_CS_SEL); //发送读取命令	

	addr[0]= (uint8_t)((ReadAddr)>>16);  //发送24bit地址 
	addr[1]= (uint8_t)((ReadAddr)>>8);
	addr[2]= (uint8_t)(ReadAddr); 
	SR_HalSpiWriteRead(dev,addr,SR_NULL,3,0);

	SR_HalSpiWriteRead(dev,SR_NULL,pBuffer,NumByteToRead,SR_SPI_CS_RLS);//循环读数 
		
}  

//无检验写SPI FLASH 
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能 
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void W25QXX_Write_NoCheck(SR_DEVICE_T *dev,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(dev,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(SR_DEVICE_T *dev,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(dev,W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
		for(i=0;i<secremain;i++)//校验数据
		{
			if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除  	  
		}
		if(i<secremain)//需要擦除
		{
			W25QXX_Erase_Sector(dev,secpos);//擦除这个扇区
			for(i=0;i<secremain;i++)	   //复制
			{
				W25QXX_BUF[i+secoff]=pBuffer[i];	  
			}
			W25QXX_Write_NoCheck(dev,W25QXX_BUF,secpos*4096,4096);//写入整个扇区  

		}else W25QXX_Write_NoCheck(dev,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;			//下一个扇区可以写完了
		}	 
	};	 
}

//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个扇区的最少时间:150ms
void W25QXX_Erase_Sector(SR_DEVICE_T *dev,uint32_t Dst_Addr)   
{  
	uint8_t addr[4]={0};  
	Dst_Addr*=4096;
	W25QXX_Write_Enable(dev);               //SET WEL 	 
	W25QXX_Wait_Busy(dev);   

	uint8_t cmd = W25X_SectorErase;     
	SR_HalSpiWriteRead(dev,&cmd,SR_NULL,1,SR_SPI_CS_SEL);  //发送扇区擦除指令


	addr[0]= (uint8_t)((Dst_Addr)>>16);  //发送24bit地址 
	addr[1]= (uint8_t)((Dst_Addr)>>8);
	addr[2]= (uint8_t)(Dst_Addr);
	SR_HalSpiWriteRead(dev,addr,SR_NULL,3,SR_SPI_CS_RLS);
					
	W25QXX_Wait_Busy(dev);   				    //等待擦除完成 
}  


SR_DEVICE_T  *spiDev=SR_NULL;


//spi flash初始化
static int w25_init(int argc, char *argv[])
{

	SR_SPI_CFG_T spiCfg={0};
	int ret =-1;
	

	spiDev = SR_HalSpiOpen(SR_SPI1); 
	if(!spiDev) 
	{
		SR_LOG(INFO,"dev not exist");
		return -1;
	}
	
	spiCfg.master = SR_SPI_MASTER;
	spiCfg.cpol = SR_SPI_CPOL_HIGH;
	spiCfg.cpha = SR_SPI_CPHA_2EDGE;
	spiCfg.speed = 42000000;
	spiCfg.order = SR_SPI_MSB;
	spiCfg.dw = SR_SPI_DW_8BIT;
	spiCfg.csId = SR_GET_GPIO_ID(PG,8);
	spiCfg.csPol = SR_SPI_CS_LOW;
	ret = SR_HalSpiConfig(spiDev, &spiCfg);
	if(ret)
	{
		SR_LOG(INFO,"init fail,ret=%d ",ret);
		return -1;
	}


	uint16_t id =0;
	id = W25QXX_ReadID(spiDev);
	SR_LOG(INFO,"id=0x%x",id);

	return 0;
}
SR_SHELL_CMD_EXPORT(w25Init, w25_init);


//spi flash读
uint8_t rBuf[1024]={0};
static int w25_read(int argc, char *argv[])
{
	uint16_t len=100;
	uint32_t addr=0;
	if(argc<3)
	{
	  return -1;
	}
	
	addr = atoi(argv[1]);
	len = atoi(argv[2]);
	
	//读数据
	W25QXX_Read(spiDev,rBuf,addr,len); 
	
	//打印数据
	ShellShowData(rBuf,len,addr);
	
	return 0;
	
}
SR_SHELL_CMD_EXPORT(w25Read, w25_read);


//spi flash擦除
static int w25_erase(int argc, char *argv[])
{
	uint32_t addr=0;

	addr = atoi(argv[1]);
	
	W25QXX_Erase_Sector(spiDev,addr);  
	
	return 0;

}
SR_SHELL_CMD_EXPORT(w25Erase, w25_erase);


//spi flash写
static int w25_write(int argc, char *argv[])
{
	uint32_t addr=0;
	uint8_t wBuf[100]={0};
	
	if(argc<3)
	{
	  return -1;
	}
	
	addr = atoi(argv[1]);
	strcpy((char *)wBuf,argv[2]);
	
	
	//写数据
	W25QXX_Write(spiDev,wBuf,addr,strlen((char *)wBuf));  

	return 0;
}
SR_SHELL_CMD_EXPORT(w25Write, w25_write);
