/*******************************************************************************
* Function Name  : P25Q40H FLASH -- 4M bit == 512K*8bit
* Description    : Flash驱动程序
*******************************************************************************/

#include "includes.h"

/*******************************************************************************
* Function Name  : Flash_Init
* Description    : P25Q40H Flash初始化
* Input          : None
* Return         : None
*******************************************************************************/
void Flash_Init(void)
{
#if (USE_CORE_TYPE == 1)    //使用C1F核心板

	//设置SPI0 GPIO
	//GPIOA_ModeCfg(GPIO_Pin_3, GPIO_ModeOut_PP_5mA);						//WP
	//GPIOA_ModeCfg(GPIO_Pin_12, GPIO_ModeOut_PP_20mA);					//CS
   GPIOA_ModeCfg(GPIO_Pin_13|GPIO_Pin_14, GPIO_ModeOut_PP_5mA);	//SCK,MOSI
	 GPIOA_ModeCfg(GPIO_Pin_15, GPIO_ModeIN_PU);							//MISO
   //SPI 主机模式
   //SPI0_MasterDefInit( );   

   //Flash_WP_H;		//硬件写保护引脚拉高
	Flash_CS_H;
	
#elif (USE_CORE_TYPE == 2)   //使用C1核心板

	//设置SPI0 GPIO
	GPIOA_ModeCfg(GPIO_Pin_3, GPIO_ModeOut_PP_5mA);						//WP
	GPIOB_ModeCfg(GPIO_Pin_22, GPIO_ModeOut_PP_20mA);					//CS
   GPIOA_ModeCfg(GPIO_Pin_13|GPIO_Pin_14, GPIO_ModeOut_PP_5mA);	//SCK,MOSI
	GPIOA_ModeCfg(GPIO_Pin_15, GPIO_ModeIN_PU);							//MISO
   //SPI 主机模式
   //SPI0_MasterDefInit( );   

   Flash_WP_H;		//硬件写保护引脚拉高
	Flash_CS_H;
	
#endif  //USE_CORE_TYPE == CORE_TYPE_C1F
	
}

/*******************************************************************************
* Function Name  : Flash_ReadSR
* Description    : P25Q40H Flash读取状态寄存器
* Input          : None
* Return         : P25Q40H Flash状态寄存器值
		BIT7  6   5   4   3   2   1   0
		SPR0  BP4 BP3 BP2 BP1 BP0 WEL WIP
		SPR:默认0,状态寄存器保护位,配合WP使用
		BP4,BP3,BP2,BP1,BP0:FLASH区域写保护设置
		WEL:写使能锁定
		BUSY:忙标记位(1,忙;0,空闲)
		默认:0x00
*******************************************************************************/
uint8_t Flash_ReadSR(void)
{
	uint8_t byte = 0;
	Flash_CS_L;
	SPI0_MasterSendByte(P24Q40H_ReadStatusReg);		//发送读取状态寄存器命令
	byte = SPI0_MasterRecvByte();
	Flash_CS_H;
	return byte;
}

/*******************************************************************************
* Function Name  : Flash_WriteSR
* Description    : P25Q40H Flash写状态寄存器
* Input          : 
	sr_val:写入状态寄存器的值
		BIT7  6   5   4   3   2   1   0
		SPR0  BP4 BP3 BP2 BP1 BP0 WEL WIP

		只有SPR0,BP3,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
* Return         : None
*******************************************************************************/
void Flash_WriteSR(uint8_t sr_val)
{
	Flash_CS_L;
	SPI0_MasterSendByte(P24Q40H_WriteStatusReg);
	SPI0_MasterSendByte(sr_val);
	Flash_CS_H;
}

/*******************************************************************************
* Function Name  : Flash_Write_Enable
* Description    : P25Q40H 写使能 -- 将WEL置位 
* Input          : None
* Return         : None
*******************************************************************************/
void Flash_Write_Enable(void)
{
	Flash_CS_L;
	SPI0_MasterSendByte(P24Q40H_WriteEnable);
	Flash_CS_H;
}

/*******************************************************************************
* Function Name  : Flash_Write_Disable
* Description    : P25Q40H 写禁止 -- 将WEL清零
* Input          : None
* Return         : None
*******************************************************************************/
void Flash_Write_Disable(void)
{
	Flash_CS_L;
	SPI0_MasterSendByte(P24Q40H_WriteDisable);
	Flash_CS_H;
}

/*******************************************************************************
* Function Name  : Flash_ReadID
* Description    : P25Q40H Flash 读取芯片ID
* Input          : None
* Return         : 返回值如下：
		0x8512:表示芯片型号为P25Q40H
		0x8511:表示芯片型号为P25Q20H
		0x8510:表示芯片型号为P25Q10H
		0x8509:表示芯片型号为P25Q05H
*******************************************************************************/
uint16_t Flash_ReadID(void)
{
	uint16_t temp=0;
	Flash_CS_L;
	SPI0_MasterSendByte(P24Q40H_ReadManufactureID);
	SPI0_MasterRecvByte();
	SPI0_MasterRecvByte();
	SPI0_MasterRecvByte();
	temp |= SPI0_MasterRecvByte();
	temp |= SPI0_MasterRecvByte()<<8;
	Flash_CS_H;
	return temp;
}

/*******************************************************************************
* Function Name  : Flash_Wait_Busy
* Description    : 等待空闲
* Input          : None
* Return         : 	1：等待超时，处于繁忙状态
							0：空闲状态
*******************************************************************************/
uint8_t Flash_Wait_Busy(void)
{
	uint8_t temp=0;
	uint16_t i=0;
	temp = Flash_ReadSR();
	while((temp&0x01)==0x01)
	{
		DelayUs(100);
		temp = Flash_ReadSR();
		i++;
		if(i>3000) return 1;		
	};
	return 0;
}

/*******************************************************************************
* Function Name  : Flash_PowerDown
* Description    : Flash 进入掉电模式
* Input          : None
* Return         : None
*******************************************************************************/
void Flash_PowerDown(void)
{
	Flash_CS_L;
	SPI0_MasterSendByte(P24Q40H_PowerDown);
	DelayUs(3);
	Flash_CS_H;
}

/*******************************************************************************
* Function Name  : Flash_PowerDown
* Description    : Flash 唤醒掉电模式
* Input          : None
* Return         : None
*******************************************************************************/
void Flash_Wakeup(void)
{
	Flash_CS_L;
	SPI0_MasterSendByte(P24Q40H_ReleasePowerDown);
	DelayUs(3);
	Flash_CS_H;
}

/*******************************************************************************
* Function Name  : Flash_Erase_Chip
* Description    : 擦除整个芯片
* Input          : None
* Return         : None
*******************************************************************************/
void Flash_Erase_Chip(void)
{
	Flash_Write_Enable();
	Flash_Wait_Busy();
	Flash_CS_L;
	SPI0_MasterSendByte(P24Q40H_ChipErase);
	Flash_CS_H;
	Flash_Wait_Busy();
}

/*******************************************************************************
* Function Name  : Flash_Erase_Block
* Description    : 擦除块
* Input          : BLK_ID：块号(0~31) 2M
* Return         : None
*******************************************************************************/
void Flash_Erase_Block(uint32_t BLK_ID)
{
	BLK_ID*=0x10000;		//64K
	Flash_Write_Enable();
	Flash_Wait_Busy();
	Flash_CS_L;
	SPI0_MasterSendByte(P24Q40H_BlockErase);
	SPI0_MasterSendByte((uint8_t)((BLK_ID)>>16));
	SPI0_MasterSendByte((uint8_t)((BLK_ID)>>8));
	SPI0_MasterSendByte((uint8_t)BLK_ID);
	Flash_CS_H;
	
	Flash_Wait_Busy();
}

/*******************************************************************************
* Function Name  : Flash_Erase_Sector
* Description    : 擦除扇区
* Input          : DST_Addr：扇区号(0~511) 2M
* Return         : None
*******************************************************************************/
void Flash_Erase_Sector(uint32_t DST_ID)
{
	DST_ID*=4096;
	Flash_Write_Enable();
	Flash_Wait_Busy();
	Flash_CS_L;
	SPI0_MasterSendByte(P24Q40H_SectorErase);
	SPI0_MasterSendByte((uint8_t)((DST_ID)>>16));
	SPI0_MasterSendByte((uint8_t)((DST_ID)>>8));
	SPI0_MasterSendByte((uint8_t)DST_ID);
	Flash_CS_H;
	
	Flash_Wait_Busy();
}

/*******************************************************************************
* Function Name  : Flash_Erase_Page
* Description    : 擦除页区
* Input          : Page_ID：页号(0~8191)
* Return         : None
*******************************************************************************/
void Flash_Erase_Page(uint32_t Page_ID)
{
	Page_ID*=256;
	Flash_Write_Enable();
	Flash_Wait_Busy();
	Flash_CS_L;
	SPI0_MasterSendByte(P24Q40H_PageErase);
	SPI0_MasterSendByte((uint8_t)((Page_ID)>>16));
	SPI0_MasterSendByte((uint8_t)((Page_ID)>>8));
	SPI0_MasterSendByte((uint8_t)Page_ID);
	Flash_CS_H;
	Flash_Wait_Busy();
}

/*******************************************************************************
* Function Name  : Flash_Erase_Page
* Description    : 擦除页区
* Input          : Page_addr:地址
* Return         : None
*******************************************************************************/
void Flash_Erase_Pageaddr(uint32_t Page_addr)
{
	Flash_Write_Enable();
	Flash_Wait_Busy();
	Flash_CS_L;
	SPI0_MasterSendByte(P24Q40H_PageErase);
	SPI0_MasterSendByte((uint8_t)((Page_addr)>>16));
	SPI0_MasterSendByte((uint8_t)((Page_addr)>>8));
	SPI0_MasterSendByte((uint8_t)Page_addr);
	Flash_CS_H;
	Flash_Wait_Busy();
}

/*******************************************************************************
* Function Name  : Flash_Read
* Description    : P25Q40H Flash 指定地址开始读取指定长度的数据
* Input          : 
		pBuffer：数据存储区
		NumByteToRead：要读取的字节数(最大65535)
		ReadAddr：读取起始地址(24bit)
* Return         : None
*******************************************************************************/
void Flash_Read(uint8_t* pBuffer,uint16_t NumByteToRead,uint32_t ReadAddr)
{
	Flash_CS_L;
	SPI0_MasterSendByte(P24Q40H_ReadData);
	SPI0_MasterSendByte((uint8_t)((ReadAddr)>>16));
	SPI0_MasterSendByte((uint8_t)((ReadAddr)>>8));
	SPI0_MasterSendByte((uint8_t)ReadAddr);
	SPI0_MasterRecv(pBuffer,NumByteToRead);
	Flash_CS_H;
}

/*******************************************************************************
* Function Name  : Flash_Write_Page
* Description    : P25Q40H Flash 指定地址开始写指定长度的数据
* Input          : 
		pBuffer：数据存储区
		NumByteToRead：要写的字节数(最大256),该数不应该超过该页的剩余字节数!!!	 
		ReadAddr：读取起始地址(24bit)
* Return         : None
*******************************************************************************/
void Flash_Write_Page(uint8_t* pBuffer,uint16_t NumByteToWrite,uint32_t writeAddr)
{
	Flash_Write_Enable();
	Flash_CS_L;
	SPI0_MasterSendByte(P24Q40H_PageProgram);
	SPI0_MasterSendByte((uint8_t)((writeAddr)>>16));
	SPI0_MasterSendByte((uint8_t)((writeAddr)>>8));
	SPI0_MasterSendByte((uint8_t)writeAddr);
	SPI0_MasterTrans(pBuffer,NumByteToWrite);
	
	Flash_CS_H;
	Flash_Wait_Busy();
}

/*******************************************************************************
* Function Name  : Flash_Write_NoCheck
* Description    : 无检验写P25Q40H FLASH
注意：必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
		具有自动换页功能
		在指定地址开始写入指定长度的数据,但是要确保地址不越界!
* Input          : 
		pBuffer：数据存储区
		NumByteToRead：要写的字节数(最大65535)	 
		ReadAddr：读取起始地址(24bit)
* Return         : None
*******************************************************************************/
void Flash_Write_NoCheck(uint8_t* pBuffer,uint16_t NumByteToWrite,uint32_t writeAddr)
{
	uint16_t pageremain;
	pageremain=256-writeAddr%256;		//单页剩余的字节数
	if(NumByteToWrite<=pageremain)	pageremain=NumByteToWrite;//不大于256个字节
	while(1)
	{
		Flash_Write_Page(pBuffer,pageremain,writeAddr);
		if(pageremain == NumByteToWrite) break;	//写入完成
		else {
			pBuffer+=pageremain;
			writeAddr+=pageremain;
			
			NumByteToWrite-=pageremain;
			if(NumByteToWrite>256)	pageremain=256;
			else pageremain=NumByteToWrite;
		}
	};
}

/*
由于本项目空间的本地SRAM空间有限，因此为较少SRAM的开销将数据BUFF从4096改为256
以下两个一个是以扇区为单位进行擦除，另一个是以页为单位进行擦除
*/
#if 1
/*******************************************************************************
* Function Name  : Flash_Write
* Description    : 无检验写P25Q40H FLASH
注意：必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
		具有自动换页功能
		在指定地址开始写入指定长度的数据,但是要确保地址不越界!
* Input          : 
		pBuffer：数据存储区
		NumByteToRead：要写的字节数(最大65535)	 
		ReadAddr：读取起始地址(24bit)
* Return         : None
*******************************************************************************/
uint8_t Temp_Flash_Buff[4200];
void Flash_Write(uint8_t* pBuffer,uint16_t NumByteToWrite,uint32_t WriteAddr)
{
	uint32_t secpos;
	uint16_t secoff,secremain,i;
	uint8_t* Write_Buff;
	
	if(NumByteToWrite <= 256*2)
	{
		Write_Buff = Temp_Flash_Buff;
		
		secpos = WriteAddr/256;		//页区地址
		secoff = WriteAddr%256;		//在扇区内的偏移
		secremain = 256 - secoff;		//扇区剩余空间
		
		if(NumByteToWrite<=secremain) secremain = NumByteToWrite;	//当前页区剩余空间可以存放
		
		while(1)
		{
			WDT_Feed();
			
			Flash_Read(Write_Buff,256,secpos*256);	//读取整个扇区的内容

			for(i=0;i<secremain;i++)						//校验数据，判断是否是要擦除扇区
			{
				if(Write_Buff[secoff+i]!=0xFF)break;	
			}

			if(i<secremain)	//需要擦除页区
			{
				Flash_Erase_Page(secpos);	//擦除整个页区
				for(i=0;i<secremain;i++)		//复制
				{
					Write_Buff[i+secoff]=pBuffer[i];	
				}
				Flash_Write_NoCheck(Write_Buff,256,secpos*256);			//写入整个页区
			}else {
				if(secremain == 256)
				{
					Flash_Write_NoCheck(pBuffer,256,secpos*256);			//写入整个页区
					
				}else if(secremain < 256){
					Flash_Write_NoCheck(pBuffer,secremain,WriteAddr);	//不需要擦除可以直接，写入整个页区
				}
				
			}
			if(NumByteToWrite == secremain) break;		//写入完成
			else 	//写入未结束
			{
				secpos++;								//页区地址增1
				secoff = 0;								//页区偏移位置归零
				pBuffer += secremain;				//数据指针偏移
				WriteAddr += secremain;				//数据保存地址偏移
				NumByteToWrite -= secremain;		//剩余写入字节数递减
				if(NumByteToWrite > 256)	secremain = 256;		//下一个页区还是写不完
				else secremain = NumByteToWrite;						//下一个页区可以写完
			}
		}
			
	}
	else
	{
		Write_Buff = Temp_Flash_Buff;
		
		secpos = WriteAddr/4096;		//扇区地址
		secoff = WriteAddr%4096;		//在扇区内的偏移
		secremain = 4096 - secoff;		//扇区剩余空间
		
		if(NumByteToWrite<=secremain) secremain = NumByteToWrite;	//当前扇区剩余空间可以存放
		
		while(1)
		{
			WDT_Feed();
			
			Flash_Read(Write_Buff,2048,secpos*4096);	//读取整个扇区的内容
			Flash_Read(Write_Buff+2048,2048,secpos*4096+2048);	//读取整个扇区的内容

			for(i=0;i<secremain;i++)						//校验数据，判断是否是要擦除扇区
			{
				if(Write_Buff[secoff+i]!=0xFF)break;	
			}

			if(i<secremain)	//需要擦除扇区
			{
				Flash_Erase_Sector(secpos);	//擦除整个扇区
				for(i=0;i<secremain;i++)		//复制
				{
					Write_Buff[i+secoff]=pBuffer[i];	
				}
				Flash_Write_NoCheck(Write_Buff,2048,secpos*4096);			//写入整个扇区
				Flash_Write_NoCheck(Write_Buff+2048,2048,secpos*4096+2048);			//写入整个扇区
			}else {
				if(secremain == 4096)
				{
					Flash_Write_NoCheck(pBuffer,2048,secpos*4096);			//写入整个扇区
					Flash_Write_NoCheck(pBuffer+2048,2048,secpos*4096+2048);			//写入整个扇区
				}else if(secremain < 4096){
					Flash_Write_NoCheck(pBuffer,secremain,WriteAddr);	//不需要擦除可以直接，写入整个扇区
				}
				
			}
			if(NumByteToWrite == secremain) break;		//写入完成
			else 	//写入未结束
			{
				secpos++;								//扇区地址增1
				secoff = 0;								//扇区偏移位置归零
				pBuffer += secremain;				//数据指针偏移
				WriteAddr += secremain;				//数据保存地址偏移
				NumByteToWrite -= secremain;		//剩余写入字节数递减
				if(NumByteToWrite > 4096)	secremain = 4096;		//下一个扇区还是写不完
				else secremain = NumByteToWrite;						//下一个扇区可以写完
			}
		}
	}
	
		
}
#else 

/*******************************************************************************
* Function Name  : Flash_Write
* Description    : 无检验写P25Q40H FLASH
注意：必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
		具有自动换页功能
		在指定地址开始写入指定长度的数据,但是要确保地址不越界!
* Input          : 
		pBuffer：数据存储区
		NumByteToRead：要写的字节数(最大65535)	 
		ReadAddr：读取起始地址(24bit)
* Return         : None
*******************************************************************************/
uint8_t Flash_Buffer[256];
void Flash_Write(uint8_t* pBuffer,uint16_t NumByteToWrite,uint32_t WriteAddr)
{
	uint32_t secpos;
	uint16_t secoff,secremain,i;
	uint8_t*	Write_Buff;
	Write_Buff = Flash_Buffer;
	
	secpos = WriteAddr/256;		//页区地址
	secoff = WriteAddr%256;		//在页区内的偏移
	secremain = 256 - secoff;	//页区剩余空间
	
	if(NumByteToWrite<=secremain) secremain = NumByteToWrite;	//当前页区剩余空间可以存放
	
	while(1)
	{
		WDT_Feed();
		
		Flash_Read(Write_Buff,256,secpos*256);	//读取整个页区的内容
		for(i=0;i<secremain;i++)						//校验数据，判断是否是要擦除页区
		{
			if(Write_Buff[secoff+i]!=0xFF)break;	
		}
		if(i<secremain)	//需要擦除页区
		{
			Flash_Erase_Page(secpos);	//擦除整个页区
			for(i=0;i<secremain;i++)		//复制
			{
				Write_Buff[i+secoff]=pBuffer[i];	
			}
			Flash_Write_Page(Write_Buff,256,secpos*256);			//写入整个页区
		}else Flash_Write_Page(pBuffer,secremain,WriteAddr);	//不需要擦除可以直接，写入整个页区
		
		if(NumByteToWrite == secremain) break;		//写入完成
		else 	//写入未结束
		{
			secpos++;								//页区地址增1
			secoff = 0;								//页区偏移位置归零
			pBuffer += secremain;				//数据指针偏移
			WriteAddr += secremain;				//数据保存地址偏移
			NumByteToWrite -= secremain;		//剩余写入字节数递减
			if(NumByteToWrite > 256)	secremain = 256;		//下一个页区还是写不完
			else secremain = NumByteToWrite;						//下一个页区可以写完
		}
	};
		
}

#endif

/*******************************************************************************
* Function Name  : Flash_APPDATA_Erase
* Description    : Flash APP区数据全部擦除 APP代码中空间为218K
* Input          : 				
*******************************************************************************/
void Flash_APPDATA_Erase(void)
{
	/*APP固定区域：0x08000 ~ 0x3FFFF*/
	/*擦除代码区全片区域 32K*/
	for(uint8_t i =8;i<16;i++)
	{
		WDT_Feed();
		Flash_Erase_Sector(i);		
	}
	/*擦除代码区全片区域 192K*/
	for(uint8_t i =1;i<4;i++)
	{
		WDT_Feed();
		Flash_Erase_Block(i);
	}
}

/*******************************************************************************
* Function Name  : Flash_LOGICDATA_Erase
* Description    : Flash LOGIC区数据全部擦除 LOGIC代码中空间为448K
* Input          : 				
*******************************************************************************/
void Flash_LOGICDATA_Erase(void)
{
	/*LOGIC固定区域：0x90000 ~ 0xFFFFF */
	/*擦除逻辑文件区域 448K*/
	for(uint8_t i =9;i<16;i++)
	{
		WDT_Feed();
		Flash_Erase_Block(i);
	}
}


