/*
*********************************************************************************************************
*                                       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];
// 写Flash缓冲
uint8_t Flash_wtBuf[FLASH_ERASE_SIZE];

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

static uint32_t GetPage(uint32_t Addr)
{
  uint32_t page = 0;
  
  if (Addr < (FLASH_BASE + FLASH_BANK_SIZE))
  {
    /* Bank 1 */
    page = (Addr - FLASH_BASE) / FLASH_PAGE_SIZE;
  }
  else
  {
    /* Bank 2 */
    page = (Addr - (FLASH_BASE + FLASH_BANK_SIZE)) / FLASH_PAGE_SIZE;
  }
  
  return page;
}

// 校验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_ErasePage(uint32_t Addr)
{
	static FLASH_EraseInitTypeDef EraseInitStruct;
	uint32_t PAGEError = 0;

	HAL_FLASH_Unlock();
	__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_PGSERR); 
	
	EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES;
	EraseInitStruct.Banks       = FLASH_BANK_1;
	EraseInitStruct.Page        = GetPage(ALIGN_PAGE_SIZE(Addr));
	EraseInitStruct.NbPages     = 1;
	HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);	

	HAL_FLASH_Lock();
}

// 写入一页，Addr为页首地址
void Flash_PageWrite(uint32_t Addr, uint8_t *buf)
{
	unsigned long long *buf64 = (unsigned long long *) buf;
	uint32_t i = Addr;
	
	HAL_FLASH_Unlock();
	__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR); 
	
	while(i < Addr + FLASH_ERASE_SIZE)
	{
		HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, i, *buf64++);
		i += 8;
	}

	HAL_FLASH_Lock();
}

void Flash_BufferWrite(uint32_t Addr, uint8_t *buf, uint32_t nbytes)
{
	 uint32_t count, offset;
	
	 while(nbytes > 0)
	 {
		 // 计算页内偏移（只有第一页有可能非0）
	     offset = Addr % FLASH_ERASE_SIZE;

		 // 计算一页内写入字节数
	     count = FLASH_ERASE_SIZE - offset;
	     if(count > nbytes)
	         count = nbytes;
	         
	     // 页对齐
	     Addr = ALIGN_PAGE_SIZE(Addr);
	     // 读本页内数据
	     Flash_BufferRead(Addr, Flash_wtBuf, FLASH_ERASE_SIZE);
	     // 如果数据一致，不用写
	     if(memcmp(Flash_wtBuf + offset, buf, count) != 0)
	     {
			 // 擦除本页
			 Flash_ErasePage(Addr);
			 // 修改数据
			 memcpy(Flash_wtBuf + offset, buf, count);
			 // 写入本页数据
			 Flash_PageWrite(Addr, Flash_wtBuf);
	     } 
	     
	     // 计算下一页
	     Addr += FLASH_ERASE_SIZE;
	     buf += count;
	     nbytes -= count;
	 }
}
