#include "spiflash.h"
#include "spi.h"

uint32_t spi_flash_size = 0x1000000;
uint32_t spi_flash_sectorsize = flah_sector_size;

uint8_t gtmpbuff[4096] = {0};
uint32_t const temp_id[]={
    0x372010,
    0x372020,
    0x373010,
    0x1F6500,
    0x1c2010,
    0x1c3110,
    0xC84010,
    0xC22010,
    0x7F9D7B,
    0x7F9D7F,
    0xBF2501,
    0x202010,
    0x1F6601,//128k
    0x372011,
    0x372021,
    0x373011,
    0x1c2011,
    0x1c3111,
    0xC84011,
    0xC22011,
    0x7F9D7C,
    0x010210,
    0xBF2502,
    0x208011,
    0x202011,
    0xEF3011,
    0x202012,//256
    0x1c3112,
    0x1F4300,
    0x372012,
    0x372022,
    0x373012,
    0xC22012,
    0x7F9D7D,
    0xC84012,
    0xC84112,
    0x010211,
    0xBF2503,
    0x4A2013,
    0x8C8C8C,
    0x8C2013,
    0x208012,
    0xEF3012,
    0x202013,
    0xC22013,
    0xBF2504,
    0xBF258D,
    0xC2013,
    0x373013,
    0x372013,
    0x1F6604,
    0x1F,
    0x1F4401,
    0x1F0400,
    0x1F4400,
    0x7F9D7E,
    0x010212,
    0xC84013,
    0xC86013,
    0xC82013,
    0x1c3113,
    0xC22533,
    0xC22553,
    0x208013,
    0x514013,
    0xEF4013,
    0xEF3013,
    0x208014,
    0xEF3014,
    0xEF4014,
    0xBF258E,
    0x202014,
    0x010213,
    0x4A2014,
    0x1F4501,
    0x372014,
    0x7F3720,
    0x373015,
    0x373014,
    0x1c3014,
    0x1c2014,
    0x1c5114,
    0x1c3114,
    0xC22014,
    0xC22554,
    0xC22534,
    0x8C2014,
    0xC82014,
    0xC84014,
    0xC2014,
    0xC22615,
    0x208015,
    0xEF3015,
    0xBF2601,
    0x207115,
    0x202015,
    0x1c5115,
    0x8C2015,
    0x8C1414,
    0x1F4601,
    0x1F4600,
    0x1F4602,
    0xC84015,
    0xC22415,
    0xC22515,
    0xC22015,
    0xC22535,
    0xC2015,
    0xBF2541,
    0x010214,
    0x4A2015,
    0x372015,
    0x372025,
    0x1c2015,
    0x1c3115,
    0x1c3015,
    0x1c7015,
    0xEF4015,
    0xEF3016,
    0xEF4016,
    0x207116,
    0xBF2602,
    0x202016,
    0xBF254A,
    0x010215,
    0xC84016,
    0x8C2016,
    0x8C4016,
    0x1F4700,
    0x1c2016,
    0x1c3116,
    0x1c3316,
    0x1c7016,
    0x1F4701,
    0xC25E16,
    0xC22536,
    0xC22016,
    0x373016,
    0x374016,
    0xBF254B,
    0x1c2017,
    0x1c3017,
    0x1c7017,
    0x1F4800,
    0x8C4117,
    0xC84017,
    0xC22617,
    0xC22017,
    0x010216,
    0x207117,
    0x202017,
    0xEF4017,
    0xEF3017,
    0xEF4018,
    0xC84018,
    0xC22018,
    0xC22538,
    0xC22618,
    0x1c3018,
    0x012017,
    0xEF3018,
    0x202018,
    0xC84019,
    0xC22019,
    0xC22539,
    0xEF4019,
    0xC2253A,
    0xC2201A,
    0xC84020
};

void spiflash_gpioinit(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);

    GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(FLASH_SPI_CS_GPIO, &GPIO_InitStructure);
    GPIO_SetBits(FLASH_SPI_CS_GPIO, FLASH_SPI_CS_PIN);
}




volatile uint32_t  Flash_ID = 0x00;

void spiflash_init(void)
{
    spiflash_gpioinit();
    Flash_ID= flash_spi_getid();
    for(int i=0;i<164;i++)
    {
        if (Flash_ID == temp_id[i] )
        {
            if(i<12)spi_flash_size = 0x10000; /*64KB*/
            else if(i<26)spi_flash_size = 0x20000; /*128KB*/
            else if(i<43)spi_flash_size = 0x40000; /*256KB*/
            else if(i<67)spi_flash_size = 0x80000; /*512KB*/
            else if(i<91)spi_flash_size = 0x100000; /*1MB*/
            else if(i<118)spi_flash_size = 0x200000; /*2m*/
            else if(i<139)spi_flash_size = 0x400000; /*4m*/
            else if(i<153)spi_flash_size = 0x800000; /*8m*/
            else if(i<162)spi_flash_size = 0x1000000; /*16m*/
            else if(i<166)spi_flash_size = 0x2000000; /*32m*/
            else spi_flash_size = 0x4000000; /*64m*/
              printf( "spi_flash_size is :%d KB\r\n\n", spi_flash_size/1024);
            return;
        }
    }
}

/**
  * @brief  Reads a byte from the SPI Flash.
  * @note   This function must be used only if the Start_Read_Sequence function
  *         has been previously called.
  * @param  None
  * @retval Byte Read from the SPI Flash.
  */
static uint8_t flash_spi_readbyte(void)
{
    return (SPI1_ReadWriteByte(flash_dummy_byte));
}

/**
  * @brief  Enables the write access to the FLASH.
  * @param  None
  * @retval None
  */
static void flash_spi_writeenable(void)
{
    /*!< Select the FLASH: Chip Select low */
    flash_spi_cs_low();

    /*!< Send "Write Enable" instruction */
    SPI1_ReadWriteByte(FLASH_CMD_WREN);

    /*!< Deselect the FLASH: Chip Select high */
    flash_spi_cs_high();
}

/**
  * @brief  Polls the status of the Write In Progress (WIP) flag in the FLASH's
  *         status register and loop until write opertaion has completed.
  * @param  None
  * @retval None
  */
static void flash_spi_waitforwriteend(void)
{
    uint8_t flashstatus = 0;
    /*!< Select the FLASH: Chip Select low */
    flash_spi_cs_low();

    /*!< Send "Read Status Register" instruction */
    SPI1_ReadWriteByte(FLASH_CMD_READSTS);

    /*!< 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 */
        flashstatus = SPI1_ReadWriteByte(flash_dummy_byte);

    }
    while ((flashstatus & 0x1) == SET); /* Write in progress */

    /*!< Deselect the FLASH: Chip Select high */
    flash_spi_cs_high();
}

/**
  * @brief  Erases the specified FLASH sector.
  * @param  SectorAddr: address of the sector to erase.
  * @retval None
  */
void flash_spi_erasesector(uint32_t sectoraddr)
{
	/*!< send write enable instruction */
	flash_spi_writeenable();

	/*!< sector erase */
	/*!< select the flash: chip select low */
	flash_spi_cs_low();
	/*!< send sector erase instruction */
	SPI1_ReadWriteByte(FLASH_CMD_ERASE_SECTOR);
	/*!< send sectoraddr high nibble address byte */
	SPI1_ReadWriteByte((sectoraddr & 0xff0000) >> 16);
	/*!< send sectoraddr medium nibble address byte */
	SPI1_ReadWriteByte((sectoraddr & 0xff00) >> 8);
	/*!< send sectoraddr low nibble address byte */
	SPI1_ReadWriteByte(sectoraddr & 0xff);
	/*!< deselect the flash: chip select high */
	flash_spi_cs_high();

	/*!< wait the end of flash writing */
	flash_spi_waitforwriteend();
}


/**
  * @brief  Writes more than one byte to the FLASH with a single WRITE cycle 
  *         (Page WRITE sequence).
  * @note   The number of byte can't exceed the FLASH page size.
  * @param  pBuffer: pointer to the buffer  containing the data to be written
  *         to the FLASH.
  * @param  WriteAddr: FLASH's internal address to write to.
  * @param  NumByteToWrite: number of bytes to write to the FLASH, must be equal
  *         or less than "sFLASH_PAGESIZE" value.
  * @retval None
  */
static void flash_spi_writepage(uint8_t* pbuffer, uint32_t writeaddr, uint16_t numbytetowrite)
{
    /*!< enable the write access to the flash */
    flash_spi_writeenable();

    /*!< select the flash: chip select low */
    flash_spi_cs_low();
    /*!< send "write to memory " instruction */
    SPI1_ReadWriteByte(FLASH_CMD_WRITEPAGE);
    /*!< send writeaddr high nibble address byte to write to */
    SPI1_ReadWriteByte((writeaddr & 0xff0000) >> 16);
    /*!< send writeaddr medium nibble address byte to write to */
    SPI1_ReadWriteByte((writeaddr & 0xff00) >> 8);
    /*!< send writeaddr low nibble address byte to write to */
    SPI1_ReadWriteByte(writeaddr & 0xff);
    /*!< while there is data to be written on the flash */

#if SPI_FLASH_USE_DMA == 0
    while (numbytetowrite--)
    {
        /*!< send the current byte */
        SPI1_ReadWriteByte(*pbuffer);
        /*!< point on the next byte to be written */
        pbuffer++;
    }
#else
    spi_dma_transmit(pbuffer, numbytetowrite);
#endif

    /*!< deselect the flash: chip select high */
    flash_spi_cs_high();

    /*!< wait the end of flash writing */
    flash_spi_waitforwriteend();
}

/**
  * @brief  Writes block of data to the FLASH. In this function, the number of
  *         WRITE cycles are reduced, using Page WRITE sequence.
  * @param  pBuffer: pointer to the buffer  containing the data to be written
  *         to the FLASH.
  * @param  WriteAddr: FLASH's internal address to write to.
  * @param  NumByteToWrite: number of bytes to write to the FLASH.
  * @retval None
  */
void flash_spi_writebuffer(uint8_t* pbuffer, uint32_t writeaddr, uint16_t numbytetowrite)
{
    uint16_t pager;
    pager = flah_page_size - (writeaddr % flah_page_size);
    if ( numbytetowrite <= pager )
    {
      pager = numbytetowrite;
    }

    while ( 1 )
    {
        flash_spi_writepage(pbuffer, writeaddr, pager);
        if ( pager == numbytetowrite )
          break;
        else
        {
          pbuffer += pager;
          writeaddr += pager;
          numbytetowrite -= pager;
          if ( numbytetowrite > flah_page_size )
            pager = flah_page_size;
          else
            pager = numbytetowrite;
        }
    }
}

/**
  * @brief  Reads a block of data from the FLASH.
  * @param  pBuffer: pointer to the buffer that receives the data read from the FLASH.
  * @param  ReadAddr: FLASH's internal address to read from.
  * @param  NumByteToRead: number of bytes to read from the FLASH.
  * @retval None
  */
uint32_t flash_spi_read(uint8_t* pbuffer, uint32_t readaddr, uint16_t numbytetoread)
{
    /*!< select the flash: chip select low */
    flash_spi_cs_low();

    /*!< send "read from memory " instruction */
    SPI1_ReadWriteByte(FLASH_CMD_READPAGE);

    /*!< send readaddr high nibble address byte to read from */
    SPI1_ReadWriteByte((readaddr & 0xff0000) >> 16);
    /*!< send readaddr medium nibble address byte to read from */
    SPI1_ReadWriteByte((readaddr& 0xff00) >> 8);
    /*!< send readaddr low nibble address byte to read from */
    SPI1_ReadWriteByte(readaddr & 0xff);
    while (numbytetoread--) /*!< while there is data to be read */
    {
        /*!< read a byte from the flash */
        *pbuffer = SPI1_ReadWriteByte(flash_dummy_byte);
        /*!< point to the next location where the byte read will be saved */
        pbuffer++;
    }
    /*!< deselect the flash: chip select high */
    flash_spi_cs_high();
    return 0;
}



/**
  * @brief  Writes block of data to the FLASH. In this function, the number of
  *         WRITE cycles are reduced, using Page WRITE sequence.
  * @param  pBuffer: pointer to the buffer  containing the data to be written
  *         to the FLASH.
  * @param  WriteAddr: FLASH's internal address to write to.
  * @param  NumByteToWrite: number of bytes to write to the FLASH.
  * @retval None
  */
uint32_t flash_spi_write(uint8_t* pbuffer, uint32_t writeaddr, uint16_t numbytetowrite)
{
  uint32_t cnt = 0;
  uint32_t sectornum, sectoroffset, sectorremain;
  
  sectornum = writeaddr / flah_sector_size;
  sectoroffset = writeaddr % flah_sector_size;
  sectorremain = flah_sector_size - sectoroffset;
  
  if ( numbytetowrite <= sectorremain)
  {
    sectorremain = numbytetowrite;
  }
  
  while ( 1 )
  {
    /*read all sector data*/
    flash_spi_read(gtmpbuff, sectornum * flah_sector_size, flah_sector_size);
    
    /*check data is 0xff ?*/
    for ( cnt = 0; cnt < sectorremain; cnt ++ )
    {
      if ( gtmpbuff[sectoroffset + cnt] != 0xff)
        break;
    }
    
    if ( cnt < sectorremain ) /*need sector erase*/
    {
      flash_spi_erasesector(sectornum * flah_sector_size);
      for ( cnt = 0; cnt < sectorremain; cnt ++ )
        gtmpbuff[cnt + sectoroffset] = pbuffer[cnt];
      flash_spi_writebuffer(gtmpbuff, sectornum * flah_sector_size, flah_sector_size);
    }
    else
    {
      flash_spi_writebuffer(pbuffer, writeaddr, sectorremain);
    }

    if ( sectorremain == numbytetowrite )
      break;
    
    else
    {
      sectornum ++;
      sectoroffset = 0;
      pbuffer += sectorremain;
      writeaddr += sectorremain;
      numbytetowrite -= sectorremain;
      if ( numbytetowrite > flah_sector_size )
      {
        sectorremain = flah_sector_size;
      }
      else
      {
        sectorremain = numbytetowrite;
      }
    }
    
  }
  return 0;
}

/**
  * @brief  Reads FLASH identification.
  * @param  None
  * @retval FLASH identification
  */
uint32_t flash_spi_getid(void)
{
	/*用户可根据不同flash采用不同的指令获取id*/	
	uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
	/* Select the FLASH: Chip Select low */
	flash_spi_cs_low();
	/* Send "RDID " instruction */
	SPI1_ReadWriteByte(FLASH_GETID);
	/* Read a byte from the FLASH */
	Temp0 = SPI1_ReadWriteByte(Dummy_Byte);
	/* Read a byte from the FLASH */
	Temp1 = SPI1_ReadWriteByte(Dummy_Byte);
	/* Read a byte from the FLASH */
	Temp2 = SPI1_ReadWriteByte(Dummy_Byte);
	/* Deselect the FLASH: Chip Select high */
	flash_spi_cs_high();
	Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
	return Temp;	
}


/**
  * @brief  Get FLASH SIZE.
  * @param  None
  * @retval FLASH identification
  */
uint32_t flash_spi_getsize(void)
{
    return spi_flash_size; 
}


/**
  * @brief  Get FLASH Sector SIZE.
  * @param  None
  * @retval FLASH identification
  */
uint32_t flash_spi_sectorsize(void)
{
    return spi_flash_sectorsize; /*4k*/
}

/**
  * @brief  Get FLASH Page SIZE.
  * @param  None
  * @retval FLASH identification
  */
uint32_t flash_spi_pagesize(void)
{
    //return flah_page_size; /*256byte*/
    return 4096; /*可根据代码配置usb buffer 一次可以接受多少数据， 最大4k*/
}


/*******************************************************************************
* Function Name  : FLASH_RD_Block_Start
* Description    : FLASH start block read
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void FLASH_RD_Block_Start( uint32_t address )
{
    flash_spi_cs_low();
    SPI1_ReadWriteByte( FLASH_CMD_READPAGE );
    SPI1_ReadWriteByte( (uint8_t)( address >> 16 ) );
    SPI1_ReadWriteByte( (uint8_t)( address >> 8 ) );
    SPI1_ReadWriteByte( (uint8_t)address );
}

/*******************************************************************************
* Function Name  : FLASH_RD_Block
* Description    : FLASH read block
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void FLASH_RD_Block( uint8_t *pbuf, uint32_t len )
{
    while( len-- )
    {
        *pbuf++ = SPI1_ReadWriteByte( flash_dummy_byte);
    }
}

/*******************************************************************************
* Function Name  : FLASH_RD_Block_End
* Description    : FLASH end block read
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void FLASH_RD_Block_End( void )
{
    flash_spi_cs_high();
}


