/******************************************************************************
 * @file     data_manege.c
 * @version  V1.00
 *
 * @brief    Application for data save/access with flash
 *           This application can manage flash access efficiently and extend flash life
 *           (1) This application extend flash life(erase times) from 100K times to 1500K times
 *           (2) It provides a well designed flash algorithm to avoid erase sector frequently.
 *           (3) Easy to use : Only 3 steps
 *               <1> Users define own data structure in struct UserData_t
 *               <2> Call the initial function DATA_AccessInit and set the sector adress 
 *                   (We have prepared a valid default address for you)
 *               <3> Call DATA_Save(&DataPack_t) and  DATA_Read(&DataPack_t) to access flash
 * 
 *           Please notice the following issue
 *           (1) Max data length in UserData_t is 256 byte.
 *           (2) User defined data type in DataPack_t.UserData_t
 *           (3) Please initial DataPack_t with DATA_AccessInit() before Data save/read
 *               Be sure to assign correct Flash address(32 bit) that align with the start of the sector
 *           (4) User can use DATA_Save(&DataPack_t) and  DATA_Read(&DataPack_t) to access flash
 *           (5) We also provide a basic byte series(array) function to access flash.
 *           (6) Every 15 times save, it will cost more about 27ms to erase sector
 *           
 *
 * @note
 * Copyright (C) 2016 Spintrol Technology Corp. All rights reserved.
*****************************************************************************/



#include "data_manage.h"
#include<stdio.h>




/******************************************************************************
 * @brief      Initializes the control structure that used in data management 
 *
 * @param[in]  myDataPack   : Datapacket pointer
 * @param[in]  u32SectorAddr: It should be the 32bit abolute address which is 
 *             the start adress of this sector. Keep in mind that the 12bits LSB should be zero.
 *             Example : 0x41000, 0x42000
 *
 * @return Success Flag     : 1 = Sector adress is correct
 *                            0 = Sector adress is illeagal
 *
 *******************************************************************************/
int32_t DATA_AccessInit(struct DataPack_t* myDataPack,uint32_t u32SectorAddr32BitLocal)
{
  myDataPack->u16DataLength = sizeof(struct UserData_t);
  myDataPack->u32SectorAddr32Bit = u32SectorAddr32BitLocal;
  myDataPack->u32SectorNumber = u32SectorAddr32BitLocal/FLASH_SECTOR_SIZE;
  if(0x000UL != (u32SectorAddr32BitLocal & 0xfff))
  {
    return 0;  //Error
  }
  else
  {
    return 1;  //Success
  }
}




/******************************************************************************
 * @brief      Read data index from Page0 in this Sector
 *
 * @param[in]  u32SectorAddr : It should be the 32bit abolute address which is 
 *             the start adress of this sector. Keep in mind that the 12bits LSB should be zero.
 *             Example : 0x41000, 0x42000
 *
 * @return     Success Flag   : -1  = The index pattern information in Page0 is not right
 *                              N   = The index of data
 *
 *******************************************************************************/
int32_t DATA_ReadIndex(uint32_t u32PageStartAddr)
{
  uint32_t au32ReadBuffer[1];
	int32_t count = 0;
	do
	{
		count++;
		FLASH_Read(au32ReadBuffer, u32PageStartAddr + (count * 4), 1);
	}while(au32ReadBuffer[0] == FLASH_INDEX_FLAG);
	
	return count;
}




/******************************************************************************
 * @brief      Read data index pattern stored in Page0 this Sector
 *
 * @param[in]  u32SectorAddr: It should be the 32bit abolute address which is 
 *             the start adress of this sector. Keep in mind that the 12bits LSB should be zero.
 *             Example : 0x41000, 0x42000
 * @param[in]  u32DataIndex : Index is from N=0~15 which tell the program that now Data save/read
 *             at/from Page(N+1).
 *
 * @return     Success Flag : 0  = There is a error when program page
 *                            1  = Program is successful
 *
 *******************************************************************************/
int DATA_WriteOffset(uint32_t u32SectorStartAddr,uint32_t u32DataIndex)
{
	ErrorStatus EStatus;
	
	EStatus = FLASH_ProgramWord(FLASH_INDEX_FLAG, u32SectorStartAddr + (u32DataIndex * 4));
	if(EStatus == ERROR)
		return 0;  //Error
	else
		return 1; //SUCCESS
}




/******************************************************************************
 * @brief      Save byte array from buffer(RAM) to flash at corresponding position(index)
 *
 * @param[in]  u32SectorAddr32Bit: Sector start adress 
 * @param[in]  pu32Buffer        : Pointer to array buffer 
 * @param[in]  u32DataLength     : Length of buffer array
 *
 * @return     Success Flag      : 0  = There is a error when program page
 *                                 1  = Read is successful
 *
 *******************************************************************************/
uint32_t DATA_ByteSeriesSave(uint32_t u32SectorAddr32Bit,uint32_t *pu32Buffer,uint32_t u32DataLength)
{
  int32_t i32DataIndex, i;
  ErrorStatus EStatus;
  
  i32DataIndex = DATA_ReadIndex(u32SectorAddr32Bit);
  
  /* 
	 * The largest offset that store in one sector is 15(16-1), 'i32DataIndex' max value should not be large then 128,
	 * which is the num of words in one sector.
	*/
  if(i32DataIndex>=16)
  {
    for(i = 0; i < (FLASH_EXTEND_SECTOR_SIZE / FLASH_REAL_SECTOR_SIZE); i++)
    {
      /* Must keep that the erase sector addr do not exceedes the last flash sector addr */
      if((u32SectorAddr32Bit + (i * FLASH_REAL_SECTOR_SIZE)) <= (DATA_FLASH_ACCESS_MAX_ADDR - FLASH_REAL_SECTOR_SIZE))
      {
        /* Erase sector, input abolute address */
        FLASH_EraseSector(u32SectorAddr32Bit + (i * FLASH_REAL_SECTOR_SIZE));
      }
    }
    i32DataIndex=0x1;
  }
  /* 
   * Error process, if index pattern is illeagal such as : 110100 but not 111100. 
   * myDataPack->i32DataIndex will return -1 
   */
  else if(i32DataIndex<=1)
  {
    for(i = 0; i < (FLASH_EXTEND_SECTOR_SIZE / FLASH_REAL_SECTOR_SIZE); i++)
    {
      /* Must keep that the erase sector addr do not exceedes the last flash sector addr */
      if((u32SectorAddr32Bit + (i * FLASH_REAL_SECTOR_SIZE)) <= (DATA_FLASH_ACCESS_MAX_ADDR - FLASH_REAL_SECTOR_SIZE))
      {
        /* Erase sector, input abolute address */
        FLASH_EraseSector(u32SectorAddr32Bit + (i * FLASH_REAL_SECTOR_SIZE));
      }
    }
    i32DataIndex=0x1;
  }

  /*
   * Write new data.
   *
   * pu8Buffer:
   *           User defined data pointer (point to struct that user defined )
   * u32SectorAddr32Bit+(1+i32DataIndex)*(FLASH_PAGE_SIZE):
   *           Data store at the Nth page from sector that N=1+myDataPack->i32DataIndex
   * u32DataLength:
   *           Data length(bytes) that user defined in sUserData
   */
  EStatus = FLASH_Program(pu32Buffer, u32SectorAddr32Bit+(i32DataIndex)*(FLASH_REAL_SECTOR_SIZE), u32DataLength);
  if(EStatus == ERROR)
    return 0;  //Error
  
  /* Update index pattern */
  DATA_WriteOffset(u32SectorAddr32Bit,i32DataIndex);
  
  return 1;  //Success
}




/******************************************************************************
 * @brief      Read byte array from flash at corresponding position(index) to buffer 
 *
 * @param[in]  u32SectorAddr32Bit: Sector start adress 
 * @param[in]  pu8Buffer         : Pointer to array buffer 
 * @param[in]  u32DataLength     : Length of buffer array
 *
 * @return     none
 *
 *******************************************************************************/
void DATA_ByteSeriesRead(uint32_t u32SectorAddr32Bit,uint32_t *pu32Buffer,uint32_t u32DataLength)
{
  int32_t i32DataIndex;
  
  i32DataIndex=DATA_ReadIndex(u32SectorAddr32Bit);

	/* valide data index is : i32DataIndex - 1 */
  FLASH_Read( pu32Buffer, u32SectorAddr32Bit+(i32DataIndex - 1)*(FLASH_REAL_SECTOR_SIZE), u32DataLength);
}




/******************************************************************************
 * @brief      Save data into flash at corresponding position(index)
 *
 * @param[in]  myDataPack : Datapacket pointer
 *             myDataPack->sUserData will be stored in Flash with corresponding position that
 *             indicated by Index pattern in Page0.
 *
 *             Note!! that is should be initialized with 
 *                DATA_AccessInit(struct DataPack_t* myDataPack,uint32_t u32SectorAddr32Bit)
 *             User should also initial the correct Sector adress or it would be wrong
 *
 * @return     Success Flag : 0  = There is a error when program page
 *                            1  = Program is successful
 *
 *******************************************************************************/
int32_t DATA_Save(struct DataPack_t* myDataPack)
{
  return DATA_ByteSeriesSave(myDataPack->u32SectorAddr32Bit,(uint32_t *)(&myDataPack->sUserData),myDataPack->u16DataLength);
}




/******************************************************************************
 * @brief      Save data into flash at corresponding position(index)
 *
 * @param[in]  myDataPack : Datapacket pointer
 *             User defined data in Flash will read to myDataPack->sUserData
 *
 *             Note!! that is should be initialized with 
 *                DATA_AccessInit(struct DataPack_t* myDataPack,uint32_t u32SectorAddr32Bit)
 *             User should also initial the correct Sector adress or it would be wrong
 *
 * @return     none
 *
 *******************************************************************************/
void DATA_Read(struct DataPack_t* myDataPack)
{
  DATA_ByteSeriesRead(myDataPack->u32SectorAddr32Bit,(uint32_t *)(&myDataPack->sUserData),myDataPack->u16DataLength);
}


/*** (C) COPYRIGHT 2016 Spintrol Technology Corp. ***/
