/*!
 * @file        Flash_Emulation_Eeprom.c
 *
 * @brief       This file provides a flash emulated eeprom read/write interface
 *
 * @version     V1.0.0
 *
 * @date        2023-03-30
 *
 * @attention
 *
 *  Copyright (C) 2023 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */
 
/* Includes */
#include "Flash_Emulation_Eeprom.h"
#include "apm32a0xx_fmc.h"

/** @addtogroup Examples
  @{
  */

/** @addtogroup Flash_Emulation_Eeprom
  @{
  */ 

/** @defgroup Flash_Emulation_Eeprom_Macros Macros
  @{
*/

/**@} end of group Flash_Emulation_Eeprom_Macros*/

/** @defgroup Flash_Emulation_Eeprom_Variables Variables
  @{
*/

/* The buffer that write or erase sector data */
static uint16_t Flash_EE_Ram_Buffer[FLASH_SECTOR_SIZE];

/**@} end of group Flash_Emulation_Eeprom_Variables */


/** @defgroup Flash_Emulation_Eeprom_Functions Functions
  @{
*/

/*!
 * @brief     flash read half word.
 *
 * @param     Addr:  flash address.
 *
 * @retval    the data of assign address.
 */
static uint8_t Flash_EE_ReadHalfWord(uint32_t Addr)
{
    return (*(__IO uint16_t*)Addr); 
}

/*!
 * @brief     Read the specified length of data from the specified address.
 *
 * @param     ReadAddr: read address.
 *
 * @param     pData: save the read data.
 *
 * @param     len: read data length.
 *
 * @retval    None. 
 *
 */
void Flash_EE_Read(uint32_t ReadAddr, uint16_t* pData, uint32_t len)
{
    /* illegal address direct return */
    if ((ReadAddr < FLASH_EE_START_ADDR) || ((ReadAddr + len) > FLASH_EE_END_ADDR))
        return;
    
    /* illegal pointer direct return */
    if (pData == NULL)
        return;
    
    /* read data */
    for (int i = 0; i < len; i++)
    {
        pData[i] = Flash_EE_ReadHalfWord(ReadAddr);
        ReadAddr += 2;
    }
}

/*!
 * @brief     Write the specified length of data from the specified address.
 *            Can be written across sectors without checking.
 *
 * @param     WriteAddr: write address.
 *
 * @param     pData: save the write data.
 *
 * @param     len: write data length.
 *
 * @retval    None.
 *
 */
void Flash_EE_WriteData(uint32_t WriteAddr, uint16_t* pData, uint32_t len)
{
    uint16_t i;
      for(i = 0; i < len; i++)
      {
            FMC_ProgramHalfWord(WriteAddr,pData[i]);
              WriteAddr += 2;
        }
}

/*!
 * @brief     Write the specified length of data from the specified address.
 *            Can be written across sectors.
 *
 * @param     WriteAddr: write address.
 *
 * @param     pData: save the write data.
 *
 * @param     len: write data length.
 *
 * @retval    None.
 *
 * @note      The example must be performed in the sectors 1~3.
 *
 */
void Flash_EE_Write(uint32_t WriteAddr, uint16_t* pData, uint32_t len)
{
      int i;
    uint32_t sectorPos;     //sector address
      uint16_t sectorOffset;  //offset address within sector
      uint16_t secremain;     //remaining addresses in the sector
      uint32_t offsetAddr;    //remove the remaining addresses of the 0x8000000
      
      /* illegal address direct return */
    if ((WriteAddr < FLASH_EE_START_ADDR) || ((WriteAddr + len) > FLASH_EE_END_ADDR))
        return;
        
        FMC_Unlock();
        
        offsetAddr = WriteAddr - FLASH_EE_START_ADDR;
        sectorPos = offsetAddr / FLASH_SECTOR_SIZE;
        sectorOffset = (offsetAddr % FLASH_SECTOR_SIZE) / 2;
        secremain = FLASH_SECTOR_SIZE / 2 - sectorOffset;
        
        if(len <= secremain)
    {
            secremain = len;
        }
        
        while(1)
        {
            Flash_EE_Read(sectorPos * FLASH_SECTOR_SIZE + FLASH_EE_START_ADDR,Flash_EE_Ram_Buffer,FLASH_SECTOR_SIZE / 2);
              
              for( i = 0; i < secremain; i++)
            {
                    if(Flash_EE_Ram_Buffer[i+sectorOffset] != 0xffff)
                        {
                            break;
                        }                            
                }
                if(i < secremain)
                {
                    FMC_ErasePage(sectorPos * FLASH_SECTOR_SIZE + FLASH_EE_START_ADDR);
                      for(i = 0; i < secremain; i++)
                      {
                            Flash_EE_Ram_Buffer[i + sectorOffset] = pData[i];
                        }
                        Flash_EE_WriteData(sectorPos*FLASH_SECTOR_SIZE+FLASH_EE_START_ADDR,Flash_EE_Ram_Buffer,FLASH_SECTOR_SIZE / 2);
                        
                }else{
                      Flash_EE_WriteData(WriteAddr,Flash_EE_Ram_Buffer,secremain);
                    
                }
                
                if(len == secremain)
                {
                    break;
                }else{
                    sectorPos++;
                      sectorOffset = 0;
                      pData += secremain;
                      WriteAddr += secremain;
                      len -= secremain;
                      
                      if(len > (FLASH_SECTOR_SIZE / 2))
                        {
                            secremain = FLASH_SECTOR_SIZE / 2;
                        }else{
                            secremain = len;                  
                        }      
                }    
        }
        
        FMC_Lock();
}

/**@} end of group Flash_Emulation_Eeprom_Functions */
/**@} end of group Flash_Emulation_Eeprom */
/**@} end of group Examples */
