/**
  ******************************************************************************
  * @file     W25Q_Flash.c
  * @author   SINOMCU-AE
  * @brief    W25Q128 opration
  *    
  *          This file provides functions to operation W28Q128:
  *    SPI_Flash_ReadID(void);
  *    SPI_FLASH_Read();  
  *    SPI_FLASH_Write_Page();
  *    SPI_FLASH_Erase_Sector(); 
  *         
  ******************************************************************************
  * @attention 
  *                           DISCLAIMER
  * The present firmware which is for guidance only aims at providing customers
  * with coding information regarding their products in order for them to save
  * time. As a result, SinoMCU shall not be held liable for any direct,indirect
  * or consequential damages with respect to any claims arising from the content 
  * of such firmware and/or the use made by customers of the coding information 
  * contained herein in connection with their products.
  *
  * <h2><center>&copy; Copyright (c) 2021 sinomcu All rights reserved.</center></h2>
  *
  ******************************************************************************
  */
  
/* Includes ------------------------------------------------------------------*/
#include "SPI_CFG.h"
#include "W25Q_Flash.h"

#define SPI_FLASH_CS_Low()     MS32_GPIO_ResetOutputPin(SPICS_GPIO_Port, SPICS_Pin)
#define SPI_FLASH_CS_High()    MS32_GPIO_SetOutputPin(SPICS_GPIO_Port, SPICS_Pin)

// W25Q128JV
// one page  :256 Byte
// one sector:4K Byte
// one block :16 sector

/**
  * @brief  read or write one byte to SPI Flash
  * @param  write data
  * @retval read data
  *         for read,need send one byte
  */
uint8_t SPI_ReadWriteByte(uint8_t txdata) {
  uint32_t timer_out =0;  
  uint8_t rxdata =0;
  
  MS32_SPI_TransmitData8(SPI1, txdata);

  while (1) {
    if(MS32_SPI_IsActiveFlag_TXE(SPI1)) {   // wait txe,finish send
      break;
    }
    else{
      timer_out++;                          // if timer_out great than MAX,Error  
    }
  }

  while (1) {
    if(MS32_SPI_IsActiveFlag_RXNE(SPI1)) {
      rxdata =MS32_SPI_ReceiveData8(SPI1);  // wait rxne set,read data
      break;
    }
    else{
      timer_out++;                          // if timer_out great than MAX,Error  
    }
  }  
  return rxdata;
}

/**
  * @brief  read SPI Flash Manufact Device ID
  * @param  none
  * @retval 16bit Device ID
  *         W25Q128 0xEF17
  */
uint16_t SPI_Flash_ReadID(void) {
  uint16_t return_val = 0;
  
  SPI_FLASH_CS_Low();
  SPI_ReadWriteByte(W25X_ManufactDeviceID);
  SPI_ReadWriteByte(0x00);
  SPI_ReadWriteByte(0x00);
  SPI_ReadWriteByte(0x00);
  return_val |= (SPI_ReadWriteByte(0xFF)<<8);
  return_val |= SPI_ReadWriteByte(0xFF);
  SPI_FLASH_CS_High();
  
  return return_val;
}

/**
  * @brief  read SPI Flash status reg
  * @param  none
  * @retval 8bit status reg
  *     BIT7  6   5   4   3   2   1   0
  *     SPR   RV  TB BP2 BP1 BP0 WEL BUSY
  *     WEL:write enable lock
  *     BUSY:1,busy;0,idle
  *     default:0x00
  */
uint8_t SPI_FLASH_ReadSR(void) {  
  uint8_t byte=0; 
  
  SPI_FLASH_CS_Low();                             
  SPI_ReadWriteByte(W25X_ReadStatusReg);   
  byte =SPI_ReadWriteByte(0XFF);            
  SPI_FLASH_CS_High(); 
  
  return byte;   
} 

/**
  * @brief  write SPI Flash status reg
  * @param  status reg
  *     only SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2) can be write
  * @retval none
 */
void SPI_FLASH_Write_SR(uint8_t StatusRegVal) {   
  SPI_FLASH_CS_Low();            
  SPI_ReadWriteByte(W25X_WriteStatusReg);  
  SPI_ReadWriteByte(StatusRegVal);            
  SPI_FLASH_CS_High();       
}   

/**
  * @brief  wait for not busy
  * @param  none
  * @retval none
  * @note   if you need,go on complete time out program
 */
void SPI_FLASH_Busy_Wait(void) {   
  uint32_t timer_out =0;
  while((SPI_FLASH_ReadSR() & SPI_FLASH_SR_BUSY_MASK) == SPI_FLASH_SR_BUSY_MASK) {
    timer_out++;                            // if timer_out great than MAX,Error 
  } 
}  

/**
  * @brief  SPI Flash write enable
  * @param  none
  * @retval none
  */  
void SPI_FLASH_Write_Enable(void) {
  SPI_FLASH_CS_Low();                         
  SPI_ReadWriteByte(W25X_WriteEnable);   
  SPI_FLASH_CS_High();                        
}

/**
  * @brief  SPI Flash write Disable
  * @param  none
  * @retval none
  */ 
void SPI_FLASH_Write_Disable(void) {  
  SPI_FLASH_CS_Low();                         
  SPI_ReadWriteByte(W25X_WriteDisable);    
  SPI_FLASH_CS_High();                                 
}     
          
/**
  * @brief  SPI Flash read data
  * @param  ReadAddr:24 bit,SPI Flash address 
  *         pBuffer :storage data pointer
  *         NumByteToRead:16bit,read data number
  * @retval none
  */
void SPI_FLASH_Read(uint32_t ReadAddr,uint8_t* pBuffer,uint16_t NumByteToRead)  { 
  uint16_t count;  

  SPI_FLASH_CS_Low();                          
  SPI_ReadWriteByte(W25X_ReadData);    
  SPI_ReadWriteByte((uint8_t)((ReadAddr)>>16));       // send 24bit address  
  SPI_ReadWriteByte((uint8_t)((ReadAddr)>>8));   
  SPI_ReadWriteByte((uint8_t)ReadAddr);   
  for(count=0;count < NumByteToRead;count++)  { 
    pBuffer[count]=SPI_ReadWriteByte(0XFF);           // read data from flash to sram
  }
  SPI_FLASH_CS_High();                      
}  

/**
  * @brief  SPI Flash page write
  * @param  WriteAddr:24 bit,SPI Flash address 
  *         pBuffer :data pointer
  *         NumByteToRead:8bit,write data number
  * @note   (WriteAddr & 0xFFFFFF00) + NumByteToWrite must be less than or equal to one page(256Byte)
  * @retval none
  */
void SPI_FLASH_Write_Page(uint32_t WriteAddr,uint8_t* pBuffer,uint8_t NumByteToWrite) {
  uint16_t  count;  

  SPI_FLASH_Write_Enable();        
  SPI_FLASH_CS_Low();                 
  SPI_ReadWriteByte(W25X_PageProgram);     
  SPI_ReadWriteByte((uint8_t)((WriteAddr)>>16));      // send 24bit address
  SPI_ReadWriteByte((uint8_t)((WriteAddr)>>8));   
  SPI_ReadWriteByte((uint8_t)WriteAddr);   
  for( count=0; count < NumByteToWrite; count++) {
    SPI_ReadWriteByte(pBuffer[count]);                // write buff data to flash
  }
  SPI_FLASH_CS_High(); 
  SPI_FLASH_Busy_Wait(); 
}

/**
  * @brief  SPI Flash sector erase
  * @param  Sector_Addr:24 bit,SPI Flash address 
  * @note   (Sector_Addr & 0xFFFFF000) Sector will be erased
  *         W25Q128JV 45-400ms
  * @retval none
  */
void SPI_FLASH_Erase_Sector(uint32_t Sector_Addr) {  
  Sector_Addr  &= 0xFFFFF000;                         // sector address
  SPI_FLASH_Write_Enable();              
  SPI_FLASH_Busy_Wait();  

  SPI_FLASH_CS_Low();             
  SPI_ReadWriteByte(W25X_SectorErase);     
  SPI_ReadWriteByte((uint8_t)((Sector_Addr)>>16));    // send 24bit address
  SPI_ReadWriteByte((uint8_t)((Sector_Addr)>>8));   
  SPI_ReadWriteByte((uint8_t)Sector_Addr);  
  SPI_FLASH_CS_High();             
  SPI_FLASH_Busy_Wait();   
}  

/******************************** END OF FILE *********************************/
