#include <string.h>
#include "w25q256.h"

///@brief   Flash相关引脚初始化
void Flash_GPIO_Init(void)
{
  FLASH_CS_DISABLE;
  FLASH_CLK_HIGH;
  FLASH_MOSI_LOW;
}

///@brief   读取一个字节数据
static uint8_t Flash_ReadOneByte(void)
{
    uint8_t retValue = 0;

    FLASH_CLK_HIGH; //时钟线拉高,恢复时钟线为高电平
    for(uint8_t i= 0; i < 8; i++)
    {
        retValue <<= 1;
        FLASH_CLK_HIGH; //时钟线拉高,恢复时钟线为高电平
        if(FLASH_MISO_READ)
        {
            retValue |= 0x01;
        }
        else
        {
            retValue &= 0xFE;
        }
        FLASH_CLK_LOW; //时钟线拉低,产生下降沿读出数据
    }
    FLASH_CLK_HIGH;
   
    return (retValue);
}

///@brief   写入一个字节
static void Flash_WriteOneByte(uint8_t DataBuffer)
{
    FLASH_CLK_LOW; //时钟线拉低，恢复时钟线为低电平
    for(uint8_t i = 0; i < 8; i++)
    {
        FLASH_CLK_LOW; //时钟线拉低，恢复时钟线为低电平
        if(DataBuffer & 0x80)
        {
            FLASH_MOSI_HIGH;
        }
        else
        {
            FLASH_MOSI_LOW;
        }
        DataBuffer <<= 1;
        FLASH_CLK_HIGH; //时钟线拉高，产生上升沿写入数据
    }
    FLASH_CLK_LOW;
    FLASH_MOSI_HIGH; //一字节数据传送完毕，MOSI数据线置高表示空闲状态
}

///@brief   写指令
static void Flash_Write_CMD(uint8_t *pCMD)
{
#ifdef FLASH_W25Q256  
     for(uint8_t i = 0; i < 4; i++)  //new add 
    {
        Flash_WriteOneByte(pCMD[i]);
    }
#endif
    
#ifdef FLASH_W25Q128  
     for(uint8_t i = 0; i < 3; i++)  //new add 
    {
        Flash_WriteOneByte(pCMD[i]);
    }
#endif        
}

///@brief   读取SR
static uint8_t Flash_ReadSR(void)
{
    uint8_t retValue = 0;

    FLASH_CS_ENABLE;
    Flash_WriteOneByte(Flash_ReadSR_CMD);
    retValue = Flash_ReadOneByte();
    FLASH_CS_DISABLE;

    return retValue;
}

///@brief   等待标志位
static void Flash_Wait_Busy(void)
{
   uint32_t i =0;

   while(((Flash_ReadSR() & FLASH_WRITE_BUSYBIT) == 0x01) && (i<0x1ffff))
   {
      i ++;
   }
}

///@brief   写使能
static void Flash_Write_Enable(void)
{
    FLASH_CS_ENABLE;
    Flash_WriteOneByte(Flash_WriteEnable_CMD);
    FLASH_CS_DISABLE;
}

#if 0
///@brief   进入4字节模式
void Enter4ByteAddrMode(void) 
{ 
    FLASH_CS_ENABLE;                                //使能器件    
    Flash_WriteOneByte(W25Q_4ByteAddrModeEnable);   //进入4Byte地址模式W25Q128以上使用 
    FLASH_CS_DISABLE; 
}

///@brief   退出4字节模式
void Exit4ByteAddrMode(void) 
{ 
    FLASH_CS_ENABLE;                                //使能器件    
    Flash_WriteOneByte(W25Q_Exit4ByteAddrModeEnable);   //进入4Byte地址模式W25Q128以上使用 
    FLASH_CS_DISABLE; 
}
#endif

///@brief   擦除区域
void Flash_Erase_Sector(uint8_t Block_Num,uint8_t Sector_Number)
{    
    uint8_t pcmd[3] = {0};
    Flash_Write_Enable();
    FLASH_CS_ENABLE;
    Flash_WriteOneByte(Flash_SecErase_CMD);

    pcmd[0] = Block_Num;
    pcmd[1] = Sector_Number<<4;
    pcmd[2] = 0;
    Flash_Write_CMD(pcmd);
    FLASH_CS_DISABLE;
    Flash_Wait_Busy();//每次擦除数据都要延时等待写入结束
   
    return ;
}

///@brief   擦除块
void Flash_Erase_Block(uint8_t BlockNum)
{
    
    uint8_t pcmd[3] = {0};
    Flash_Write_Enable();   //写使能
    FLASH_CS_ENABLE;        //片选拉低
    Flash_WriteOneByte(Flash_BlockErase_CMD);  //传输Block擦除指令
    pcmd[0] = BlockNum ;     //传24位地址
    Flash_Write_CMD(pcmd);
    FLASH_CS_DISABLE;
    Flash_Wait_Busy();      //每次擦除数据都要延时等待写入结束
   
    return ;
}

///@brief   页写
void Flash_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint32_t WriteBytesNum)
{
    uint8_t pcmd[4] = {0};    
                        
    Flash_Write_Enable();
    FLASH_CS_ENABLE;
    Flash_WriteOneByte(Flash_PageProgram_CMD);
#ifdef FLASH_W25Q256    
    pcmd[0] = (uint8_t)((WriteAddr&0xff000000)>>24);     //new add
    pcmd[1] = (uint8_t)((WriteAddr&0x00ff0000)>>16);
    pcmd[2] = (uint8_t)((WriteAddr&0x0000ff00)>>8);
    pcmd[3] = (uint8_t)WriteAddr;
#endif
    
#ifdef FLASH_W25Q128    
    pcmd[0] = (uint8_t)((WriteAddr&0x00ff0000)>>16);
    pcmd[1] = (uint8_t)((WriteAddr&0x0000ff00)>>8);
    pcmd[2] = (uint8_t)WriteAddr;
#endif    
    
    Flash_Write_CMD(pcmd);

    for(uint32_t  i = 0;i < WriteBytesNum; i++)
    {

        Flash_WriteOneByte(pBuffer[i]);  //向Flash中写入最大一页256bytes字节数据
    }
    FLASH_CS_DISABLE;
    Flash_Wait_Busy();                   //每次写入一定数量的数据都要延时等待写入结束
    return;
}

///@brief   连续写入多页
void Flash_Write_MorePage(uint8_t *pBuffer, uint32_t WriteAddr, uint32_t WriteBytesNum)
{    
    uint16_t PageByteRemain = 0;
    PageByteRemain = Flash_PAGEBYTE_LENGTH - WriteAddr%Flash_PAGEBYTE_LENGTH;
    if(WriteBytesNum <= PageByteRemain)
    {
        PageByteRemain = WriteBytesNum;
    }
    while(1)
    {
        Flash_Write_Page(pBuffer,WriteAddr,PageByteRemain);
        if(WriteBytesNum == PageByteRemain)
        {
            break;
        }
        else
        {
            pBuffer       += PageByteRemain;
            WriteAddr     += PageByteRemain;
            WriteBytesNum -= PageByteRemain;
            if(WriteBytesNum > Flash_PAGEBYTE_LENGTH)
            {
                PageByteRemain = Flash_PAGEBYTE_LENGTH;
            }
            else
            {
                PageByteRemain = WriteBytesNum;
            }
        }
    }
    
    return;
}

///@brief   连续写入多页
uint8_t W25QXX_BUFFER[4096];
void W25Q128_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint32_t WriteBytesNum)
{    
    // volatile static int timeReadTemp1, timeReadTemp2, timeReadTemp3, timeReadTemp4, timeReadTemp5, timeReadTemp6;

    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; 	  //扇区剩余空间大小
    if (WriteBytesNum <= secremain)
        secremain = WriteBytesNum; 	  //不大于4096个字节
    while (1)
    {        
        // timeReadTemp1 = HAL_GetTick();
        Flash_Read(W25QXX_BUF, secpos * 4096, 4096); 	  //读出整个扇区的内容
        // timeReadTemp2 = HAL_GetTick();
        for (i = 0; i < secremain; i++) //校验数据
        {
            if (W25QXX_BUF[secoff + i] != 0XFF)
                break; //需要擦除
        }
        if (i < secremain) //需要擦除
        {
            Flash_Erase_Block(secpos);		//擦除这个扇区
            // timeReadTemp3 = HAL_GetTick();
            for (i = 0; i < secremain; i++)	   		//复制
            {
                W25QXX_BUF[i + secoff] = pBuffer[i];
            }
            Flash_Write_Page(W25QXX_BUF, secpos * 4096, 4096);	   	//写入整个扇区
            // timeReadTemp4 = HAL_GetTick();
            // timeReadTemp5 = NumByteToWrite;
            // timeReadTemp6 = WriteAddr;
        }
        else
            Flash_Write_Page(pBuffer, WriteAddr, secremain); //写已经擦除了的,直接写入扇区剩余区间.
        if (WriteBytesNum == secremain)
            break; //写入结束了
        else //写入未结束
        {
            secpos++; //扇区地址增1
            secoff = 0; //偏移位置为0

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

///@brief   读取数据
void Flash_Read(uint8_t *pBuffer,uint32_t ReadAddr,uint32_t ReadBytesNum)
{   
    uint8_t pcmd[4] = {0}; 
    FLASH_CS_ENABLE; //打开spiflash片选
    Flash_WriteOneByte(Flash_ReadData_CMD);
#ifdef FLASH_W25Q256
    pcmd[0] = (uint8_t)((ReadAddr&0xff000000)>>24); //new add
    pcmd[1] = (uint8_t)((ReadAddr&0x00ff0000)>>16);
    pcmd[2] = (uint8_t)((ReadAddr&0x0000ff00)>>8);
    pcmd[3] = (uint8_t)ReadAddr;
#endif
    
#ifdef FLASH_W25Q128  
    pcmd[0] = (uint8_t)((ReadAddr&0x00ff0000)>>16);
    pcmd[1] = (uint8_t)((ReadAddr&0x0000ff00)>>8);
    pcmd[2] = (uint8_t)ReadAddr;    
#endif   
    
    Flash_Write_CMD(pcmd);

    for(uint32_t i = 0;i < ReadBytesNum; i++)
    {
       pBuffer[i] = Flash_ReadOneByte();  //读取SPIflash中指定bytes字节数据

    }
    FLASH_CS_DISABLE;
    
    return ;
}