/*****************************************************************************
** 文件名称：flash.c
** 功    能：
** 修改日志：
******************************************************************************/
#include "w25q128.h"
#include "bsp_os.h"

unsigned short int ID=0;
unsigned char only_id[8] = {0};
static BSP_OS_SEM w25q128_mutex;//共享资源锁

/*******************************************************************************
** 函数名称：void Flash_GPIO_Init(void)
** 功    能：
** 修改日志：
*******************************************************************************/
void Flash_GPIO_Init(void)
{   
    BSP_OS_SemCreate(&w25q128_mutex,1u,"w25q128_mutex");//创建信号量
    
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_GPIOE);
    //MOSI_PIN3,,,,SCK_PIN5
    gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_3| GPIO_PIN_5);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO_PIN_3| GPIO_PIN_5);
    //MISO
    gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_4);
    //CS

    gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_8);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO_PIN_8);

   
    FLASH_CS_DISABLE;
    FLASH_CLK_HIGH;
    FLASH_DI_LOW;	
}

/*****************************************************************************
** 文件名称： Flash_ReadOneByte
** 功    能：
** 修改日志：
******************************************************************************/
static u_int8_t Flash_ReadOneByte(void)
{
    u_int8_t retValue = 0;

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

/*****************************************************************************
** 文件名称：Flash_WriteOneByte
** 功    能：
** 修改日志：
******************************************************************************/

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

/*****************************************************************************
** 文件名称：  Flash_Write_CMD
** 功    能：
** 修改日志：
******************************************************************************/

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

/*****************************************************************************
** 文件名称： u_int16_t Flash_ReadSR
** 功    能：
** 修改日志：
******************************************************************************/

static u_int8_t Flash_ReadSR(void)
{
    u_int8_t retValue = 0;

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

    return retValue;
}


/*****************************************************************************
** 文件名称：Flash_Wait_Busy
** 功    能：
** 修改日志：
******************************************************************************/

static void Flash_Wait_Busy(void)
{
   u_int32_t i =0;

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

/*****************************************************************************
** 文件名称：static void Flash_Write_Enable(void)
** 功    能：
** 修改日志：
******************************************************************************/

static void Flash_Write_Enable(void)
{
    FLASH_CS_ENABLE;
    Flash_WriteOneByte(Flash_WriteEnable_CMD);
    FLASH_CS_DISABLE;
}


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


void Exit4ByteAddrMode(void) 
{ 
    FLASH_CS_ENABLE;                                //使能器件    
    Flash_WriteOneByte(W25Q_Exit4ByteAddrModeEnable);   //进入4Byte地址模式W25Q128以上使用 
    FLASH_CS_DISABLE; 
}
/*****************************************************************************
** 文件名称： void Flash_Erase_Sector(u_int16_t Block_Num,u_int16_t Sector_Number)
** 功    能：
** 修改日志：
******************************************************************************/

void Flash_Erase_Sector(u_int8_t Block_Num,u_int8_t Sector_Number)
{

    BSP_OS_SemWait(&w25q128_mutex, 0u);
    
    u_int8_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();//每次擦除数据都要延时等待写入结束
   
    BSP_OS_SemPost(&w25q128_mutex);
    return ;
}

/*****************************************************************************
** 文件名称：void Flash_Erase_Block(u_int8_t BlockNum)
** 功    能：擦除BLOCK
** 修改日志：
******************************************************************************/

void Flash_Erase_Block(u_int8_t BlockNum)
{
    BSP_OS_SemWait(&w25q128_mutex, 0u);
    
    u_int8_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();      //每次擦除数据都要延时等待写入结束
   
    BSP_OS_SemPost(&w25q128_mutex);
    return ;
}

/*****************************************************************************
** 文件名称：void Flash_Write_Page(u_int8_t *pBuffer, u_int32_t WriteAddr, u_int32_t WriteBytesNum)
** 功    能：
** 修改日志：
******************************************************************************/
void Flash_Write_Page(u_int8_t *pBuffer, u_int32_t WriteAddr, u_int32_t WriteBytesNum)
{
    u_int8_t pcmd[4] = {0};    
                        
    Flash_Write_Enable();
    FLASH_CS_ENABLE;
    Flash_WriteOneByte(Flash_PageProgram_CMD);
#ifdef FLASH_W25Q256    
    pcmd[0] = (u_int8_t)((WriteAddr&0xff000000)>>24);     //new add
    pcmd[1] = (u_int8_t)((WriteAddr&0x00ff0000)>>16);
    pcmd[2] = (u_int8_t)((WriteAddr&0x0000ff00)>>8);
    pcmd[3] = (u_int8_t)WriteAddr;
#endif
    
#ifdef FLASH_W25Q128    
    pcmd[0] = (u_int8_t)((WriteAddr&0x00ff0000)>>16);
    pcmd[1] = (u_int8_t)((WriteAddr&0x0000ff00)>>8);
    pcmd[2] = (u_int8_t)WriteAddr;
#endif    
    
    Flash_Write_CMD(pcmd);

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

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

/*****************************************************************************
** 文件名称：void Flash_Write_MorePage(u_int8_t *pBuffer, u_int32_t WriteAddr, u_int32_t WriteBytesNum)
** 功    能：
** 修改日志：
******************************************************************************/
void Flash_Write_MorePage(u_int8_t *pBuffer, u_int32_t WriteAddr, u_int32_t WriteBytesNum)
{
    BSP_OS_SemWait(&w25q128_mutex, 0u); 
    
    u_int16_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;
            }
        }
    }
    
    BSP_OS_SemPost(&w25q128_mutex);
    
    return;
}

/*****************************************************************************
** 文件名称：void Flash_Read(u_int8_t *pBuffer,u_int32_t ReadAddr,u_int32_t ReadBytesNum)
** 功    能：
** 修改日志：
******************************************************************************/

void Flash_Read(u_int8_t *pBuffer,u_int32_t ReadAddr,u_int32_t ReadBytesNum)
{
    BSP_OS_SemWait(&w25q128_mutex, 0u); 
    
    u_int8_t pcmd[4] = {0}; 
    FLASH_CS_ENABLE; //打开spiflash片选
    Flash_WriteOneByte(Flash_ReadData_CMD);
#ifdef FLASH_W25Q256
    pcmd[0] = (u_int8_t)((ReadAddr&0xff000000)>>24); //new add
    pcmd[1] = (u_int8_t)((ReadAddr&0x00ff0000)>>16);
    pcmd[2] = (u_int8_t)((ReadAddr&0x0000ff00)>>8);
    pcmd[3] = (u_int8_t)ReadAddr;
#endif
    
#ifdef FLASH_W25Q128  
    pcmd[0] = (u_int8_t)((ReadAddr&0x00ff0000)>>16);
    pcmd[1] = (u_int8_t)((ReadAddr&0x0000ff00)>>8);
    pcmd[2] = (u_int8_t)ReadAddr;    
#endif   
    
    Flash_Write_CMD(pcmd);

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

    }
    FLASH_CS_DISABLE;
    
    BSP_OS_SemPost(&w25q128_mutex);
    return ;
}



/*大量数据写入FLASH*/
void write_many_data(int n,unsigned char *p,unsigned int start_address)
{ /*n是数据总长度,*p是要写入数据的地址，start_address是写入Flash的地址，将除了帧头和动作之外的所有数据全部写入FLASH中*/
  unsigned char block;
 if((start_address%65536)==0)
  {
    block=(start_address/65536);
    Flash_Erase_Block(block);
  }
  Flash_Write_MorePage(p,start_address,n);//写入的数据是除开帧头和动作后剩下的所有数据
}






//falsh随机存储函数，即可以在FLASH任意一个地址存储数据
//write_add写入的首地址
//cache_add缓存地址，缓存大小默认为一个sector，即4KB。由于flash写入数据需要将整个sector擦出，因此为了不影响sector其他不需要修改的数据，则需要一个缓存区。
//*data需要写入数据的指针
//len需要写入数据的长度

//随机写入校验码计算
unsigned int w25q128_random_crc(char *data, unsigned int len)
{
  unsigned int i = 0;
  unsigned int crc = 0;
  for(i = 0; i < len; i++)
  {
    crc = crc + data[i];
  }
  return crc;
}


//根据BLOCK和SECTOR计算详细的地址
w25q128_add_inf w25q128_block_sector_to_add(unsigned char block, unsigned char sector)
{
  w25q128_add_inf inf;
  inf.block = block;
  inf.sector = sector;
  
  //计算所处block的首地址
  inf.block_first_add = 64 * 1024 * inf.block;
  //计算所处block的尾地址
  inf.block_tail_add  = 64 * 1024 * (inf.block + 1) - 1;
  
  //计算所处sector的首地址
  inf.sector_first_add = 64 * 1024 * inf.block + 4 * 1024 * inf.sector;
  //计算所处sector的尾地址
  inf.sector_tail_add  = 64 * 1024 * inf.block + 4 * 1024 * (inf.sector + 1) - 1;
  
  inf.add = inf.sector_first_add;//此处返回该sector的首地址
  
  return inf;
}

//通过某个地址，计算该地址对应的block和sector等详细的地址信息
w25q128_add_inf w25q128_add_to_block_sector(unsigned int add)
{
  w25q128_add_inf inf;
  
  inf.add = add;
  
  //计算处于哪个block
  if((add + 1) >= (64 * 1024))
  {
    if((add + 1) % (64 * 1024) == 0)
    {
      inf.block = (add + 1) / (64 * 1024) - 1;
    }
    else
    {
      inf.block = (add + 1) / (64 * 1024);
    }
  }
  else
  {
    inf.block = 0;
  }
  //计算所处block的首地址
  inf.block_first_add = 64 * 1024 * inf.block;
  //计算所处block的尾地址
  inf.block_tail_add  = 64 * 1024 * (inf.block + 1) - 1;
  
  //计算处于哪个sector
  if((add + 1 - inf.block * 64 * 1024) >= (4 * 1024))
  {
    if((add + 1 - inf.block * 64 * 1024) % (4 * 1024) == 0)
    {
      inf.sector = (add + 1 - inf.block * 64 * 1024) / (4 * 1024) - 1;
    }
    else
    {
      inf.sector = (add + 1 - inf.block * 64 * 1024) / (4 * 1024);
    }
  }
  else
  {
    inf.sector = 0;
  }
  
  //计算所处sector的首地址
  inf.sector_first_add = 64 * 1024 * inf.block + 4 * 1024 * inf.sector;
  //计算所处sector的尾地址
  inf.sector_tail_add  = 64 * 1024 * inf.block + 4 * 1024 * (inf.sector + 1) - 1;
  
  return inf;
}




//随机写入数据
//write_add随机写入的地址；cache_add缓存区地址，必须是某个sector的首地址；*data需要写入数据的指针；len需要写入数据的长度
char w25q128_random_write(unsigned int write_add, unsigned int cache_add, char *data, unsigned int len)
{

  unsigned int  save_write_add, save_read_add, cache_write_add, cache_read_add;
  
  unsigned char data_buffer[W25Q128_BUFFER_LEN];
  unsigned int i ,j;
  unsigned int total_byte, replace_len, replace_len_total; //需要从新写入的所有字节数,需要替换的长度
  w25q128_add_inf save_write_inf, save_first_sector_inf, save_tail_sector_inf, cache_inf;

  
  save_first_sector_inf = w25q128_add_to_block_sector(write_add);
  save_tail_sector_inf  = w25q128_add_to_block_sector(write_add + len - 1);
  cache_inf             = w25q128_add_to_block_sector(cache_add);
  //清除缓存区SECTOR的数据
  Flash_Erase_Sector(cache_inf.block, cache_inf.sector);
  
  total_byte = 
    (save_tail_sector_inf.block - save_first_sector_inf.block + 1) * 64 * 1024 - save_first_sector_inf.sector * 4 * 1024 - ( 15 - save_tail_sector_inf.sector) * 4 * 1024;
  
  save_read_add = 
    w25q128_block_sector_to_add(save_first_sector_inf.block, save_first_sector_inf.sector).sector_first_add;//计算需要搬运数据的第一个sector的首地址
  save_write_add = save_read_add;
  
  
  cache_read_add = cache_inf.sector_first_add;
  cache_write_add = cache_read_add;
  
  replace_len_total = 0;

  
  for(i = 0; i < total_byte; i = i + W25Q128_BUFFER_LEN)
  {
    memset(data_buffer, 0, sizeof(data_buffer));
    //读出256字节数据 
    Flash_Read((unsigned char *)data_buffer, save_read_add, W25Q128_BUFFER_LEN);
    
    //判断读出的256个字节有没有需要更改的地方
    if((write_add + replace_len_total) >= save_read_add && (write_add + replace_len_total) <= (save_read_add + W25Q128_BUFFER_LEN - 1))
    {
      //如果尾地址也是在该256个字节中
      if((write_add + len - 1) >= save_read_add && (write_add + len - 1) <= (save_read_add + W25Q128_BUFFER_LEN - 1))
      {
        //计算需要替换的数据长度
        replace_len = (write_add + len - 1) - (write_add + replace_len_total) + 1;
      }
      else
      {
        //计算需要替换的数据长度
        replace_len = (save_read_add + W25Q128_BUFFER_LEN - 1) - (write_add + replace_len_total) + 1;
      }
      //替换数据
      memcpy((data_buffer + (write_add + replace_len_total - save_read_add)), (data + replace_len_total), replace_len);//替换数据
      
      replace_len_total = replace_len_total + replace_len;
    }

    //向缓存区写入数据
    Flash_Write_MorePage(data_buffer, cache_write_add, W25Q128_BUFFER_LEN);
      
    cache_write_add = cache_write_add + W25Q128_BUFFER_LEN;
    save_read_add   = save_read_add + W25Q128_BUFFER_LEN;
    
    //当缓存区的数据写满时，则将数据复制写入到原来的数据存储区中
    if(cache_write_add == cache_inf.sector_tail_add + 1)
    {
      //擦出原来sector中的数据
      save_write_inf = w25q128_add_to_block_sector(save_write_add);
      Flash_Erase_Sector(save_write_inf.block, save_write_inf.sector);
      
      for(j = 0; j < 4 * 1024; j = j + W25Q128_BUFFER_LEN)
      {
        memset(data_buffer, 0, sizeof(data_buffer));
        //读出缓存区中的数据
        Flash_Read((unsigned char *)data_buffer, cache_read_add, W25Q128_BUFFER_LEN);
        //向存储区复写入数据
        Flash_Write_MorePage(data_buffer, save_write_add, W25Q128_BUFFER_LEN);
        
        cache_read_add = cache_read_add + W25Q128_BUFFER_LEN;
        save_write_add = save_write_add + W25Q128_BUFFER_LEN;
      }
      
      Flash_Erase_Sector(cache_inf.block, cache_inf.sector);
      cache_read_add = cache_inf.sector_first_add;
      cache_write_add = cache_read_add;
    }
  }
  return 1;
}


/*****************************************************************************
** 文件名称：void FLASH_BulkErase()
** 功    能：整块擦除
** 修改日志：LiHong@2021.09.29
******************************************************************************/
void FLASH_BulkErase()
{
        BSP_OS_SemWait(&w25q128_mutex, 0u);
        
	Flash_Write_Enable();   //写使能
	Flash_Wait_Busy();
	 FLASH_CS_ENABLE;        //片选拉低
	Flash_WriteOneByte(Flash_Chip_Erase_CMD);
	// 发送完指令后，cs必须拉高，否则指令不执行
	FLASH_CS_DISABLE;
	Flash_Wait_Busy();
        BSP_OS_SemPost(&w25q128_mutex);
        return ;
}



/*****************************************************************************
** 文件名称：void Read_w25q128_ID()
** 功    能：读系列ID
** 修改日志：LiHong@2022.05.20
******************************************************************************/
void Read_w25q128_ID()
{
  FLASH_CS_ENABLE;//片选使能
  Flash_WriteOneByte(Flash_ReadID);
  Flash_WriteOneByte(0x00);
  Flash_WriteOneByte(0x00);
  Flash_WriteOneByte(0x00);
  
  
  ID |= Flash_ReadOneByte() << 8;
  ID |= Flash_ReadOneByte();
  
  FLASH_CS_DISABLE;//片选失能
}

/*****************************************************************************
** 文件名称：void Read_w25q128_only_ID()
** 功    能：读唯一ID
** 修改日志：LiHong@2021.05.20
******************************************************************************/
void Read_w25q128_only_ID()
{
  FLASH_CS_ENABLE;//片选使能
  Flash_WriteOneByte(Flash_ReadID_only);
  Flash_WriteOneByte(0x00);
  Flash_WriteOneByte(0x00);
  Flash_WriteOneByte(0x00);
  Flash_WriteOneByte(0x00);
  
  
  only_id[0] = Flash_ReadOneByte();
  only_id[1] = Flash_ReadOneByte();
  only_id[2] = Flash_ReadOneByte();
  only_id[3] = Flash_ReadOneByte();
  only_id[4] = Flash_ReadOneByte();
  only_id[5] = Flash_ReadOneByte();
  only_id[6] = Flash_ReadOneByte();
  only_id[7] = Flash_ReadOneByte();
  
  FLASH_CS_DISABLE;//片选失能
}

