#include "stFlash.h" 
#include "utask.h"

extern SPI_HandleTypeDef hspi2;

/**
  * : ӴFlashȡһֽ
  * : 
  *   ֵ: uint8_tȡ
  * ˵    This function must be used only if the Start_Read_Sequence
  *           function has been previously called.
  */
uint8_t SPI_FLASH_ReadByte(void)
{
  uint8_t d_read,d_send=Dummy_Byte;
  if(HAL_SPI_TransmitReceive(&hspi2,&d_send,&d_read,1,0xFFFFFF)!=HAL_OK)
    d_read=Dummy_Byte;
  
  return d_read;    
}

/**
  * : Flashȡдһֽݲһֽ
  * : byte
  *   ֵ: uint8_tյ
  * ˵    
  */
uint8_t SPI_FLASH_SendByte(uint8_t byte)
{
  uint8_t d_read,d_send=byte;
  if(HAL_SPI_TransmitReceive(&hspi2,&d_send,&d_read,1,0xFFFFFF)!=HAL_OK)
    d_read=Dummy_Byte;
  
  return d_read; 
}

/**
  * : 
  * : SectorAddrַҪΪ4096
  *   ֵ: 
  * ˵    FlashССΪ4KB(4096ֽ)һСҪ
  *           Ϊ4096FlashоƬд֮ǰҪȲռ䡣
  */
void SPI_FLASH_SectorErase(uint32_t SectorAddr)
{
  /* FLASHдʹ */
  SPI_FLASH_WriteEnable();
  SPI_FLASH_WaitForWriteEnd();
  /*  */
  /* ѡFLASH: CS͵ƽ */
  FLASH_SPI_CS_ENABLE();
  /* ָ*/
  SPI_FLASH_SendByte(W25X_SectorErase);
  /*Ͳַĸλ*/
  SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
  /* Ͳַλ */
  SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
  /* Ͳַĵλ */
  SPI_FLASH_SendByte(SectorAddr & 0xFF);
  /* ôFLASH: CS ߵƽ */
  FLASH_SPI_CS_DISABLE();
  /* ȴ*/
  SPI_FLASH_WaitForWriteEnd();
}

/**
  * : Ƭ
  * : 
  *   ֵ: 
  * ˵    FlashƬռ
  */
void SPI_FLASH_BulkErase(void)
{
 /* FLASHдʹ */
  SPI_FLASH_WriteEnable();

  /* Ƭ Erase */
  /* ѡFLASH: CS͵ƽ */
  FLASH_SPI_CS_ENABLE();
  /* Ƭָ*/
  SPI_FLASH_SendByte(W25X_ChipErase);
  /* ôFLASH: CSߵƽ */
  FLASH_SPI_CS_DISABLE();

  /* ȴ*/
  SPI_FLASH_WaitForWriteEnd();
}

/**
  * : ʹܴFlashд
  * : 
  *   ֵ: 
  * ˵    
  */
void SPI_FLASH_WriteEnable(void)
{
  /* ѡFLASH: CS͵ƽ */
  FLASH_SPI_CS_ENABLE();

  /* дʹ */
  SPI_FLASH_SendByte(W25X_WriteEnable);

  /* ôFlashCSߵƽ */
  FLASH_SPI_CS_DISABLE();
}

/**
  * : ȴд
  * : 
  *   ֵ: 
  * ˵    Polls the status of the Write In Progress (WIP) flag in the
  *           FLASH's status  register  and  loop  until write  opertaion
  *           has completed.
  */
void SPI_FLASH_WaitForWriteEnd(void)
{
  uint8_t FLASH_Status = 0;

  /* Select the FLASH: Chip Select low */
  FLASH_SPI_CS_ENABLE();

  /* Send "Read Status Register" instruction */
  SPI_FLASH_SendByte(W25X_ReadStatusReg1);

  /* Loop as long as the memory is busy with a write cycle */
  do
  {
    /* Send a dummy byte to generate the clock needed by the FLASH
    and put the value of the status register in FLASH_Status variable */
    FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);	 
  }
  while ((FLASH_Status & WIP_Flag) == SET); /* Write in progress */

  /* Deselect the FLASH: Chip Select high */
  FLASH_SPI_CS_DISABLE();
}

/**
  * : FLASHҳдݣñдǰҪȲ
  * : pBufferдݵָ
  *           WriteAddrдַ
  *           NumByteToWriteдݳȣСڵSPI_FLASH_PerWritePageSize
  *   ֵ: 
  * ˵    FlashÿҳСΪ256ֽ
  */
void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
  /* FLASHдʹ */
  SPI_FLASH_WriteEnable();

   /* ѰҴFLASH: CS͵ƽ */
  FLASH_SPI_CS_ENABLE();
  /* ддָ*/
  SPI_FLASH_SendByte(W25X_PageProgram);
  /*дַĸλ*/
  SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
  /*дַλ*/
  SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
  /*дַĵλ*/
  SPI_FLASH_SendByte(WriteAddr & 0xFF);

  if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
  {
     NumByteToWrite = SPI_FLASH_PerWritePageSize;
     //printf("Err: SPI_FLASH_PageWrite too large!\n");
  }

  /* д*/
  while (NumByteToWrite--)
  {
     /* ͵ǰҪдֽ */
    SPI_FLASH_SendByte(*pBuffer);
     /* ָһֽ */
    pBuffer++;
  }

  /* ôFLASH: CS ߵƽ */
  FLASH_SPI_CS_DISABLE();

  /* ȴд*/
  SPI_FLASH_WaitForWriteEnd();
}

/**
  * : FLASHдݣñдǰҪȲ
  * : pBufferдݵָ
  *           WriteAddrдַ
  *           NumByteToWriteдݳ
  *   ֵ: 
  * ˵    úдݳ
  */
void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
  uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

  Addr = WriteAddr % SPI_FLASH_PageSize;
  count = SPI_FLASH_PageSize - Addr;
  NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
  NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

  if (Addr == 0) /* ַ SPI_FLASH_PageSize   */
  {
    if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
    {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    {
      while (NumOfPage--)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
        WriteAddr +=  SPI_FLASH_PageSize;
        pBuffer += SPI_FLASH_PageSize;
      }

      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
    }
  }
  else /* ַ SPI_FLASH_PageSize  */
  {
    if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
    {
      if (NumOfSingle > count) /* (NumByteToWrite + WriteAddr) > SPI_FLASH_PageSize */
      {
        temp = NumOfSingle - count;

        SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
        WriteAddr +=  count;
        pBuffer += count;

        SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
      }
      else
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
      }
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    {
      NumByteToWrite -= count;
      NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
      NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

      SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
      WriteAddr +=  count;
      pBuffer += count;

      while (NumOfPage--)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
        WriteAddr +=  SPI_FLASH_PageSize;
        pBuffer += SPI_FLASH_PageSize;
      }

      if (NumOfSingle != 0)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
      }
    }
  }
}

/**
  * : ӴFlashȡ
  * : pBufferŶȡݵָ
  *           ReadAddrȡĿַ
  *           NumByteToReadȡݳ
  *   ֵ: 
  * ˵    úȡݳ
  */
void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
  /* ѡFLASH: CS͵ƽ */
  FLASH_SPI_CS_ENABLE();

  /*   ָ */
  SPI_FLASH_SendByte(W25X_ReadData);

  /*   ַλ */
  SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  /*   ַλ */
  SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  /*   ַλ */
  SPI_FLASH_SendByte(ReadAddr & 0xFF);

  while (NumByteToRead--) /* ȡ */
  {
     /* ȡһֽ*/
    *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
    /* ָһֽڻ */
    pBuffer++;
  }

  /* ôFLASH: CS ߵƽ */
  FLASH_SPI_CS_DISABLE();
}

/**
  * : ȡFlashͺŵID
  * : 
  *   ֵ: uint32_tFlashͺID
  * ˵      FLASH_ID      ICͺ      洢ռС         
                0xEF3015      W25X16        2M byte
                0xEF4015	    W25Q16        4M byte
                0XEF4017      W25Q64        8M byte
                0XEF4018      W25Q128       16M byte  (YS-F1ProĬ)
  */
uint32_t SPI_FLASH_ReadID(void)
{
  uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

  /* ѡFLASH: CS͵ƽ */
  FLASH_SPI_CS_ENABLE();

  /* ȡоƬͺID */
  SPI_FLASH_SendByte(W25X_JedecDeviceID);

  /* ӴFlashȡһֽ */
  Temp0 = SPI_FLASH_SendByte(Dummy_Byte);

  /* ӴFlashȡһֽ */
  Temp1 = SPI_FLASH_SendByte(Dummy_Byte);

  /* ӴFlashȡһֽ */
  Temp2 = SPI_FLASH_SendByte(Dummy_Byte);

  /* ôFlashCSߵƽ */
  FLASH_SPI_CS_DISABLE();
  
  Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
  return Temp;
}

/**
  * : ȡFlash豸ID
  * : 
  *   ֵ: uint32_tFlash豸ID
  * ˵    0x13
  */
uint32_t SPI_FLASH_ReadManufactDeviceID(void)
{
  uint32_t Temp = 0, Temp0 = 0, Temp1 = 0;

  /* ѡFLASH: CS͵ƽ */
  FLASH_SPI_CS_ENABLE();

  /* ȡоƬ豸ID * */
  SPI_FLASH_SendByte(W25X_ManufactDeviceID);
  SPI_FLASH_SendByte(0x00);
  SPI_FLASH_SendByte(0x00);
  SPI_FLASH_SendByte(0x00);
  
  /* ӴFlashȡһֽ */
  Temp0 = SPI_FLASH_SendByte(Dummy_Byte);
	
	/* ӴFlashȡһֽ */
  Temp1 = SPI_FLASH_SendByte(Dummy_Byte);

  /* ôFlashCSߵƽ */
  FLASH_SPI_CS_DISABLE();

	Temp = (Temp0 << 8) | Temp1;
  return Temp;
}

/**
  * : ȡݴ
  * : ReadAddrȡַ
  *   ֵ: 
  * ˵    Initiates a read data byte (READ) sequence from the Flash.
  *           This is done by driving the /CS line low to select the device,
  *           then the READ instruction is transmitted followed by 3 bytes
  *           address. This function exit and keep the /CS line low, so the
  *           Flash still being selected. With this technique the whole
  *           content of the Flash is read with a single READ instruction.
  */
void SPI_FLASH_StartReadSequence(uint32_t ReadAddr)
{
  /* Select the FLASH: Chip Select low */
  FLASH_SPI_CS_ENABLE();

  /* Send "Read from Memory " instruction */
  SPI_FLASH_SendByte(W25X_ReadData);

  /* Send the 24-bit address of the address to read from -----------------------*/
  /* Send ReadAddr high nibble address byte */
  SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  /* Send ReadAddr medium nibble address byte */
  SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  /* Send ReadAddr low nibble address byte */
  SPI_FLASH_SendByte(ReadAddr & 0xFF);
}

/**
  * : ģʽ
  * : 
  *   ֵ: 
  * ˵    
  */
void SPI_Flash_PowerDown(void)   
{ 
  /* Select the FLASH: Chip Select low */
  FLASH_SPI_CS_ENABLE();

  /* Send "Power Down" instruction */
  SPI_FLASH_SendByte(W25X_PowerDown);

  /* Deselect the FLASH: Chip Select high */
  FLASH_SPI_CS_DISABLE();
}   

/**
  * : ѴFlash
  * : 
  *   ֵ: 
  * ˵    
  */
void SPI_Flash_WakeUp(void)   
{
  /* Select the FLASH: Chip Select low */
  FLASH_SPI_CS_ENABLE();

  /* Send "Power Down" instruction */
  SPI_FLASH_SendByte(W25X_ReleasePowerDown);

  /* Deselect the FLASH: Chip Select high */
  FLASH_SPI_CS_DISABLE(); 
}

