/*
*********************************************************************************************************
*                                       IAR Development Kits
*                                              on the
*
*                                    M451
*
* Filename      : fmc_flash.h
* Version       : V1.00
* Programmer(s) : Qian Xianghong
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                             INCLUDE FILES
*********************************************************************************************************
*/

#include "includes.h"

// 读Flash全局缓冲
uint8_t Flash_rdBuf[FLASH_ERASE_SIZE];

// 将地址按4字节对齐
#define ALIGN_DWORD_SIZE(addr) ((addr) & 0xFFFFFFFCul)

/* Base address of the Flash sectors */

#define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000) /* Base address of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000) /* Base address of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000) /* Base address of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000) /* Base address of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000) /* Base address of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000) /* Base address of Sector 5, 128 Kbytes */
#define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000) /* Base address of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000) /* Base address of Sector 7, 128 Kbytes */
#define ADDR_FLASH_SECTOR_8     ((uint32_t)0x08080000) /* Base address of Sector 8, 128 Kbytes */
#define ADDR_FLASH_SECTOR_9     ((uint32_t)0x080A0000) /* Base address of Sector 9, 128 Kbytes */
#define ADDR_FLASH_SECTOR_10    ((uint32_t)0x080C0000) /* Base address of Sector10, 128 Kbytes */
#define ADDR_FLASH_SECTOR_11    ((uint32_t)0x080E0000) /* Base address of Sector11, 128 Kbytes */
#define ADDR_FLASH_SECTOR_12    ((uint32_t)0x08100000) /* Base address of Sector12, 128 Kbytes */
#define ADDR_FLASH_SECTOR_13    ((uint32_t)0x08120000) /* Base address of Sector13, 128 Kbytes */
#define ADDR_FLASH_SECTOR_14    ((uint32_t)0x08140000) /* Base address of Sector14, 128 Kbytes */
#define ADDR_FLASH_SECTOR_15    ((uint32_t)0x08160000) /* Base address of Sector15, 128 Kbytes */

/**
  * @brief  Gets the sector of a given address
  * @param  None
  * @retval The sector of a given address
  */
static uint32_t GetSector(uint32_t Address)
{
  uint32_t sector = 0;

  if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0))
  {
    sector = FLASH_SECTOR_0;
  }
  else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1))
  {
    sector = FLASH_SECTOR_1;
  }
  else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2))
  {
    sector = FLASH_SECTOR_2;
  }
  else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3))
  {
    sector = FLASH_SECTOR_3;
  }
  else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4))
  {
    sector = FLASH_SECTOR_4;
  }
  else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5))
  {
    sector = FLASH_SECTOR_5;
  }
  else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6))
  {
    sector = FLASH_SECTOR_6;
  }
  else /* (Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_7) */
  {
    sector = FLASH_SECTOR_7;
  }
  return sector;
}

/**
  * @brief  Gets sector Size
  * @param  None
  * @retval The size of a given sector
  */
static uint32_t GetSectorSize(uint32_t Sector)
{
  uint32_t sectorsize = 0x00;
  if((Sector == FLASH_SECTOR_0) || (Sector == FLASH_SECTOR_1) || (Sector == FLASH_SECTOR_2) || (Sector == FLASH_SECTOR_3))
  {
    sectorsize = 16 * 1024;
  }
  else if(Sector == FLASH_SECTOR_4)
  {
    sectorsize = 64 * 1024;
  }
  else
  {
    sectorsize = 128 * 1024;
  }  
  return sectorsize;
}

/**
  * @brief  Gets sector start Address
  * @param  None
  * @retval The start address of a given sector
  */
static uint32_t GetSectorAddr(uint32_t Sector)
{
  uint32_t sectoraddr = 0x00;
  if((Sector == FLASH_SECTOR_0) || (Sector == FLASH_SECTOR_1) || (Sector == FLASH_SECTOR_2) || (Sector == FLASH_SECTOR_3))
  {
    sectoraddr = ADDR_FLASH_SECTOR_0 + 16 * 1024 * (Sector - FLASH_SECTOR_0);
  }
  else if(Sector == FLASH_SECTOR_4)
  {
    sectoraddr = ADDR_FLASH_SECTOR_0 + 64 * 1024;
  }
  else
  {
    sectoraddr = ADDR_FLASH_SECTOR_0 + 128 * 1024 * (Sector - FLASH_SECTOR_5);
  }  
  return sectoraddr;
}

// 校验flash内容
uint8_t Flash_BufferVerify(uint32_t Addr, uint8_t *buf, uint32_t nbytes)
{
	// 将地址按4字节对齐
	uint32_t Addr4Start = ALIGN_DWORD_SIZE(Addr);
	uint32_t Addr4End = ALIGN_DWORD_SIZE(Addr + nbytes + 3);
	uint32_t u32Data, i, j = 0;

    for(i = Addr4Start; i < Addr4End; i += 4)
    {
        u32Data = *(__IO uint32_t *)(i);
		if(i >= Addr && i < Addr + nbytes && buf[j++] != (u32Data & 0xFF))
			return 0;
		if(i + 1 >= Addr && i + 1 < Addr + nbytes && buf[j++] != ((u32Data >> 8) & 0xFF))
			return 0;
		if(i + 2 >= Addr && i + 2 < Addr + nbytes && buf[j++] != ((u32Data >> 16) & 0xFF))
			return 0;
		if(i + 3 >= Addr && i + 3 < Addr + nbytes && buf[j++] != (u32Data >> 24))
			return 0;
    }

	return 1;
}

// 将flash内容读入缓冲区
void Flash_BufferRead(uint32_t Addr, uint8_t *buf, uint32_t nbytes)
{
	// 将地址按4字节对齐
	uint32_t Addr4Start = ALIGN_DWORD_SIZE(Addr);
	uint32_t Addr4End = ALIGN_DWORD_SIZE(Addr + nbytes + 3);
	uint32_t u32Data, i, j = 0;

    for(i = Addr4Start; i < Addr4End; i += 4)
    {
        u32Data = *(__IO uint32_t *)(i);
		
		if(i >= Addr && i < Addr + nbytes)
			buf[j++] = (u32Data & 0xFF);
		if(i + 1 >= Addr && i + 1 < Addr + nbytes)
			buf[j++] = ((u32Data >> 8) & 0xFF);
		if(i + 2 >= Addr && i + 2 < Addr + nbytes)
			buf[j++] = ((u32Data >> 16) & 0xFF);
		if(i + 3 >= Addr && i + 3 < Addr + nbytes)
			buf[j++] = (u32Data >> 24);
    }
}

// 擦除一页
void Flash_EraseSector(uint32_t Addr)
{
	static FLASH_EraseInitTypeDef EraseInitStruct;
	uint32_t PAGEError = 0;

	EraseInitStruct.TypeErase   = FLASH_TYPEERASE_SECTORS;
	EraseInitStruct.VoltageRange  = FLASH_VOLTAGE_RANGE_3;
	EraseInitStruct.Sector = GetSector(Addr);
	EraseInitStruct.NbSectors     = 1;
	HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);	
}

// 写入一页，Addr为首地址
void Flash_SectorWrite(uint32_t Addr, uint8_t *buf, uint32_t count)
{
	unsigned long *buf32 = (unsigned long *) buf;
	uint32_t i = 0;
	
	while(i < count)
	{
		HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Addr + i, *buf32++);
		i += 4;
	}
}

void Flash_BufferWrite(uint32_t Addr, uint8_t *buf, uint32_t nbytes)
{
	uint32_t count, offset;
	uint32_t sector, sectorSize, sectorAddr;
	
	// 第1页（Bootloader）不允许擦除
	static uint8_t sectorErased[16] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

	while(nbytes > 0)
	{
		sector = GetSector(Addr);
		sectorSize = GetSectorSize(sector);
		sectorAddr = GetSectorAddr(sector);
		
		// 计算页内偏移（只有第一页有可能非0）
		offset = Addr - sectorAddr;

		// 计算一页内写入字节数
		count = sectorSize - offset;
		if(count > nbytes)
			count = nbytes;
		 
		// 如果第一次写入某个扇区，则擦除
		if(!sectorErased[sector])
		{
			// 擦除本页
			Flash_EraseSector(Addr);
			sectorErased[sector] = 1;
		}
		
		// 写入本页数据
		Flash_SectorWrite(Addr, buf, count);

		// 计算下一页
		Addr += count;
		buf += count;
		nbytes -= count;
	}
}
