/**
  ******************************************************************************
  * @file    ys32md211_flash.c
  * @author  YSPRING Application Team
  * @version V1.0.1
  * @date    2023.6.13
  * @brief   This file provides firmware functions to manage the following 
  *          functionalities of the FLASH peripheral:
  *            - FLASH Interface configuration
  *            - FLASH Memory Programming
  *            - Option Bytes Programming
  *            - Interrupts and flags management
  * 
  *  @verbatim
 ===============================================================================
                    ##### How to use this driver #####
 ===============================================================================
    [..] This driver provides functions to configure and program the Flash 
         memory of all YS32MD211 devices.
         (#) FLASH Interface configuration functions: this group includes the 
             management of following features:
             (++) Set the latency

         (#) FLASH Memory Programming functions: this group includes all needed 
             functions to erase and program the main memory:
             (++) Lock and Unlock the Flash interface.
             (++) Erase function: Erase Page, erase all pages.
             (++) Program functions(256 Byte): Word write.

         (#) FLASH Option Bytes Programming functions: this group includes all 
             needed functions to:
             (++) Lock and Unlock the Flash Option bytes.
             (++) Launch the Option Bytes loader
             (++) Erase the Option Bytes
             (++) Set/Reset the write protection
             (++) Set the Read protection Level
             (++) Program the user option Bytes
             (++) Set/Reset the BOOT1 bit
             (++) Get the user option bytes
             (++) Get the Write protection
             (++) Get the read protection status

         (#) FLASH Interrupts and flag management functions: this group includes 
             all needed functions to:
             (++) Enable/Disable the flash interrupt sources
             (++) Get flags status
             (++) Clear flags
             (++) Get Flash operation status
             (++) Wait for last flash operation

         (#) FLASH erase and programming operations can only be performed on HSI16M, HSI24M, HSI36M, HSI48M.

 @endverbatim
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "ys32md211_flash.h"

/** @addtogroup YS32MD211_StdPeriph_Driver
  * @{
  */

/** @defgroup FLASH 
  * @brief FLASH driver modules
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/** @defgroup FLASH_Private_Functions
  * @{
  */

/**
 * @brief Select the corresponding FLASH parameter according to the HSI
 * 
 * @retval None
 * 
 */
void FLASH_Trimming(void)
{
  uint32_t Clock;

  Clock = RCC->ICSCR & RCC_ICSCR_HSI_FS;

  if(Clock == RCC_ICSCR_HSI_FS_16Mhz)
  {
    //16MHz		
    FLASH->SEQ0          = ( *(uint32_t *)(0x1FFF7A40)) & 0x1FF;
    FLASH->SEQ1          = ( *(uint32_t *)(0x1FFF7A40)) >> 22;
    FLASH->SEQ2P1        = ((*(uint32_t *)(0x1FFF7A40)) >> 9) & 0x1FF;
    FLASH->SEQ2P3        = ( *(uint32_t *)(0x1FFF7A44)) & 0xFFF;
    FLASH->SEQ3          = ((*(uint32_t *)(0x1FFF7A44)) >> 16) & 0x1FF;
    FLASH->PREPG_SEQ2P2  = ( *(uint32_t *)(0x1FFF7A48)) & 0xFFFF;
    FLASH->PE_SEQ2P2     = ( *(uint32_t *)(0x1FFF7A4C)) & 0x3FFFF;
    FLASH->SME_SEQ2P2    = ( *(uint32_t *)(0x1FFF7A50)) & 0x3FFFF;
    FLASH->PP_SEQ2P2     = ( *(uint32_t *)(0x1FFF7A54)) & 0x1FFFF;
    FLASH->SMP_SEQ2P2    = ( *(uint32_t *)(0x1FFF7A58)) & 0x1FFFF;			
  }
  else if(Clock == RCC_ICSCR_HSI_FS_24Mhz)
  {
    //24MHz
    FLASH->SEQ0          = ( *(uint32_t *)(0x1FFF7A5C)) & 0x1FF;
    FLASH->SEQ1          = ( *(uint32_t *)(0x1FFF7A5C)) >> 22;
    FLASH->SEQ2P1        = ((*(uint32_t *)(0x1FFF7A5C)) >> 9) & 0x1FF;
    FLASH->SEQ2P3        = ( *(uint32_t *)(0x1FFF7A60)) & 0xFFF;
    FLASH->SEQ3          = ((*(uint32_t *)(0x1FFF7A60)) >> 16) & 0x1FF;
    FLASH->PREPG_SEQ2P2  = ( *(uint32_t *)(0x1FFF7A64)) & 0xFFFF;
    FLASH->PE_SEQ2P2     = ( *(uint32_t *)(0x1FFF7A68)) & 0x3FFFF;
    FLASH->SME_SEQ2P2    = ( *(uint32_t *)(0x1FFF7A6C)) & 0x3FFFF;
    FLASH->PP_SEQ2P2     = ( *(uint32_t *)(0x1FFF7A70)) & 0x1FFFF;
    FLASH->SMP_SEQ2P2    = ( *(uint32_t *)(0x1FFF7A74)) & 0x1FFFF;
  }
  else if(Clock == RCC_ICSCR_HSI_FS_36Mhz)
  {
    //36MHz
    FLASH->SEQ0          = ( *(uint32_t *)(0x1FFF7A78)) & 0x1FF;
    FLASH->SEQ1          = ( *(uint32_t *)(0x1FFF7A78)) >> 22;
    FLASH->SEQ2P1        = ((*(uint32_t *)(0x1FFF7A78)) >> 9) & 0x1FF;
    FLASH->SEQ2P3        = ( *(uint32_t *)(0x1FFF7A7C)) & 0xFFF;
    FLASH->SEQ3          = ((*(uint32_t *)(0x1FFF7A7C)) >> 16) & 0x1FF;
    FLASH->PREPG_SEQ2P2  = ( *(uint32_t *)(0x1FFF7A80)) & 0xFFFF;
    FLASH->PE_SEQ2P2     = ( *(uint32_t *)(0x1FFF7A84)) & 0x3FFFF;
    FLASH->SME_SEQ2P2    = ( *(uint32_t *)(0x1FFF7A88)) & 0x3FFFF;
    FLASH->PP_SEQ2P2     = ( *(uint32_t *)(0x1FFF7A8C)) & 0x1FFFF;
    FLASH->SMP_SEQ2P2    = ( *(uint32_t *)(0x1FFF7A90)) & 0x1FFFF;
  }
  else
  {
    //48M
    FLASH->SEQ0          = ( *(uint32_t *)(0x1FFF7A94)) & 0x1FF;
    FLASH->SEQ1          = ( *(uint32_t *)(0x1FFF7A94)) >> 22;
    FLASH->SEQ2P1        = ((*(uint32_t *)(0x1FFF7A94)) >> 9) & 0x1FF;
    FLASH->SEQ2P3        = ( *(uint32_t *)(0x1FFF7A98)) & 0xFFF;
    FLASH->SEQ3          = ((*(uint32_t *)(0x1FFF7A98)) >> 16) & 0x1FF;
    FLASH->PREPG_SEQ2P2  = ( *(uint32_t *)(0x1FFF7A9C)) & 0xFFFF;
    FLASH->PE_SEQ2P2     = ( *(uint32_t *)(0x1FFF7AA0)) & 0x3FFFF;
    FLASH->SME_SEQ2P2    = ( *(uint32_t *)(0x1FFF7AA4)) & 0x3FFFF;
    FLASH->PP_SEQ2P2     = ( *(uint32_t *)(0x1FFF7AA8)) & 0x1FFFF;
    FLASH->SMP_SEQ2P2    = ( *(uint32_t *)(0x1FFF7AAC)) & 0x1FFFF;		
  }
}

/**
 * @brief Flash read operation waits for the clock cycle
 * 
 * @param FLASH_Latency: specifies the FLASH Latency value.
 *          This parameter can be one of the following values:
 *             @arg FLASH_Latency_0: FLASH 0 Latency cycle (HCLK < 48MHz)
 *             @arg FLASH_Latency_1: FLASH 1 Latency cycle (HCLK = 48MHz)
 * @retval None
 */
void FLASH_SetLatency(uint32_t FLASH_Latency)
{
    uint32_t reg = 0;
    
    reg = FLASH->ACR;  
    reg &= (uint32_t) (~((uint32_t)FLASH_ACR_LATENCY));
    reg |= FLASH_Latency;
    FLASH->ACR = reg;
}

/**
  * @brief  Unlocks the FLASH control register and program memory access.
  * @param  FLASH_Key1: key1 value is 0x45670123.
  *         FLASH_Key2: key2 value is 0xCDEF89AB.
  * @retval None
  */
void FLASH_Unlock(uint32_t FLASH_Key1, uint32_t FLASH_Key2)
{
  if((FLASH->CR & FLASH_CR_LOCK) != RESET)
  {
    /* Unlocking the program memory access */
    FLASH->KEYR = FLASH_Key1;
    FLASH->KEYR = FLASH_Key2;
  }
}

/**
  * @brief  Locks the Program memory access.
  * @param  None
  * @retval None
  */
void FLASH_Lock(void)
{
  /* Set the LOCK Bit to lock the FLASH control register and program memory access */
  FLASH->CR |= FLASH_CR_LOCK;
}

/**
  * @brief  Erases a specified page in program memory.
  * @param  Page_Address: The page address in program memory to be erased.
  * @note   A Page is erased in the Program memory only if the address to load 
  *         is the start address of a page (multiple of 1024 bytes).
  * @retval FLASH Status: The returned value can be: 
  *         FLASH_BUSY, FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  */
FLASH_Status FLASH_ErasePage(uint32_t Page_Address)
{
    FLASH_Status status = FLASH_COMPLETE;
    uint32_t reg;
    
    Page_Address &= (~0x03);   //The address is a multiple of 4
    
    /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
  
  if(status == FLASH_COMPLETE)
  { 
    
		FLASH_Unlock(FLASH_KEY1, FLASH_KEY2);

    reg = FLASH->CR;
		reg &= ~(FLASH_CR_PGSTRT | FLASH_CR_MER | FLASH_CR_PG | FLASH_CR_SER | FLASH_CR_OPTSTRT);
    FLASH->CR = reg | (FLASH_CR_PER | FLASH_CR_EOPIE);
    
    *(uint32_t *)Page_Address = 0xFFFFFFFF;

    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
		
		FLASH->SR |= FLASH_SR_EOP;  //CLR EOP
    
    FLASH_Lock();
  }

  /* Return the Erase Status */
  return status;
}

/**
  * @brief  Erases all FLASH pages.
  * @param  FLASH_MEROPT: Select the size of the mass erase operation.
  *            This parameter can be one of the following values:
  *               @arg FLASH_MEROPT_28K: mass erase main flash 
  *               @arg FLASH_MEROPT_32K: mass erase main flash, system memory
  * @retval FLASH Status: The returned value can be: 
  *         FLASH_BUSY, FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  */
FLASH_Status FLASH_EraseAllPages(uint32_t FLASH_MEROPT)
{
  FLASH_Status status = FLASH_COMPLETE;
  uint32_t reg;
	
	assert_param(IS_FLASH_MEROPT(FLASH_MEROPT));
    
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
  
  if(status == FLASH_COMPLETE)
  {
    
    FLASH_Unlock(FLASH_KEY1, FLASH_KEY2);
    reg = FLASH->CR;
    reg &= ~(FLASH_CR_PGSTRT | FLASH_CR_PG | FLASH_CR_PER | FLASH_CR_SER | FLASH_CR_OPTSTRT);		
    FLASH->CR = reg | (FLASH_CR_MER | FLASH_CR_EOPIE); 
		
		if (FLASH_MEROPT != FLASH_MEROPT_28K)
		{
			/* Select the 32K mass erase operation for flash */
			FLASH->CR |= FLASH_CR_MEROPT;
		}
		else
		{
			/* Select the 28K mass erase operation for flash */
			FLASH->CR &= (~FLASH_CR_MEROPT);
		}		
    
    *(uint32_t *)0x08001000 = 0xFFFFFFFF;

    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
		
		FLASH->SR |= FLASH_SR_EOP;  //CLR EOP		

    FLASH_Lock();
  }

  /* Return the Erase Status */
  return status;
}

/**
  * @brief Flash Erase Sector
  * @param Address: Erase Address
  * @retval FLASH Status: The returned value can be: 
  *         FLASH_BUSY, FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  * 
  */
FLASH_Status FLASH_EraseSector(uint32_t Address)
{
  FLASH_Status status = FLASH_COMPLETE;
  uint32_t reg;
  
  Address &= (~0x03);   //The address is a multiple of 4
    
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
  
  if(status == FLASH_COMPLETE)
  {
    
    FLASH_Unlock(FLASH_KEY1, FLASH_KEY2);
    reg = FLASH->CR;
		reg &= ~(FLASH_CR_PGSTRT | FLASH_CR_MER | FLASH_CR_PER | FLASH_CR_PG | FLASH_CR_OPTSTRT);
    FLASH->CR = reg | (FLASH_CR_SER | FLASH_CR_EOPIE);
    
    *(uint32_t *)Address = 0xFFFFFFFF;

    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
		
		FLASH->SR |= FLASH_SR_EOP;  //CLR EOP	
    
    FLASH_Lock();
  }

  /* Return the Erase Status */
  return status;
}

/**
  * @brief Flash Program Page
  * @param Address: Flash address
  * 
  * @param Buffer: 32-bit array pointer
  * 
  * @param Length: Array length, maximum 64 words
  * 
  * @note  Less than 256 bytes are padded with 0xFF
  * @retval FLASH Status: The returned value can be: 
  *         FLASH_BUSY, FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  * 
  */
FLASH_Status FLASH_ProgramPage(uint32_t Address, uint32_t * Buffer, uint32_t Length)
{
  FLASH_Status status = FLASH_COMPLETE;
  uint32_t Last_Data;
  uint8_t i;
  uint32_t reg;
  assert_param(IS_FLASH_Page_Length(Length));
  
  Address &= (~0x03);   //The address is a multiple of 4
  
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
  
  if(status == FLASH_COMPLETE)
  {
    FLASH_Unlock(FLASH_KEY1, FLASH_KEY2);

    reg = FLASH->CR;
		reg &= ~(FLASH_CR_PGSTRT | FLASH_CR_MER | FLASH_CR_PER | FLASH_CR_SER | FLASH_CR_OPTSTRT);
    FLASH->CR = reg | (FLASH_CR_PG | FLASH_CR_EOPIE); 
    
    for(i=0; i<63; i++)
    {
      if(i < Length)
      {
        *(uint32_t *)(Address+(i*4)) = Buffer[i];
      }
      else
      {
        *(uint32_t *)(Address+(i*4)) = 0xFFFFFFFF;
      }
    }
    if(Length < 64)
    {
      Last_Data = 0xFFFFFFFF;
    }
    else
    {
      Last_Data = Buffer[i];
    }

    FLASH->CR |= FLASH_CR_PGSTRT;
    *(uint32_t *)(Address+(63*4)) = Last_Data;
    
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
		
		FLASH->SR |= FLASH_SR_EOP;  //CLR EOP	
    
    FLASH->CR &= (~FLASH_CR_PG);
    FLASH_Lock();
  }

  /* Return the Program Status */
  return status;   
}

/**
 * @brief read Flash Datas(32Bit)
 * 
 * @param Address: Flash address.The address is a multiple of 4.
 * @retval 32bit flash data
 * 
 */
uint32_t FLASH_Read32(uint32_t Address)
{   
  return *(uint32_t *)(Address);
}

/**
* @brief read Flash Datas(8Bit).The address must be a multiple of 4!
 * 
 * @param Address: Flash address
 * @retval 8bit flash data
 * 
 */
uint8_t FLASH_Read8(uint32_t Address)
{
  return *(uint8_t *)Address;
}

/**
  * @brief  Unlocks the option bytes block access.
  * @param  FLASH_OptKey1: OptKey1 value is 0x08192A3B
  *         FLASH_OptKey2: OptKey2 value is 0x4C5D6E7F
  * @note   The LOCK bit must be cleared by FLASH_Unlock() before
            unlocking option bytes
  * @retval None
  */
void FLASH_OB_Unlock(uint32_t FLASH_OptKey1, uint32_t FLASH_OptKey2)
{
  if((FLASH->CR & FLASH_CR_OPTLOCK) != RESET)
  { 
    /* Unlocking the option bytes block access */
    FLASH->OPTKEYR = FLASH_OptKey1;
    FLASH->OPTKEYR = FLASH_OptKey2;
  }
}

/**
  * @brief  Locks the option bytes block access.
  * @param  None
  * @retval None
  */
void FLASH_OB_Lock(void)
{
  /* Set the OPTLOCK Bit to lock the option bytes block access */
  FLASH->CR |= FLASH_CR_OPTLOCK;
}

/**
  * @brief  Launch the option byte loading.
  * @param  None
  * @retval None
  */
void FLASH_OB_Launch(void)
{
  /* Set the OBL_Launch bit to launch the option byte loading */
  FLASH->CR |= FLASH_CR_OBL_LAUNCH;
}

/**
  * @brief  Erases the FLASH option bytes.
  * @note   This functions erases all option bytes except the Read protection (RDP) and nBOOT1.
  * @param  None
  * @retval FLASH Status: The returned value can be: 
  *         FLASH_BUSY, FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  */
FLASH_Status FLASH_OB_Erase(void)
{
  uint32_t rdptmp = 0;
  FLASH_Status status = FLASH_COMPLETE;
  uint32_t reg;

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);

  rdptmp = FLASH->OPTR & (~0x7F00);

  if(status == FLASH_COMPLETE)
  {   
    FLASH_Unlock(FLASH_KEY1, FLASH_KEY2);
    FLASH_OB_Unlock(FLASH_OPTKEY1, FLASH_OPTKEY2);
    
    //RST GPIO, hardware watchdog, hardware IWDG, read protection 
    FLASH->OPTR = rdptmp | FLASH_OPTR_NRST_MODE | FLASH_OPTR_WWDG_SW | FLASH_OPTR_BOR_LEV | FLASH_OPTR_IWDG_SW | FLASH_OPTR_BOR_EN;  
    FLASH->PCROPR = FLASH->PCROPR;
    FLASH->WRPR = FLASH->WRPR;
    
    reg = FLASH->CR;
    reg &= ~(FLASH_CR_ERRIE | FLASH_CR_EOPIE | FLASH_CR_PGSTRT | FLASH_CR_SER | FLASH_CR_MER | FLASH_CR_PER | FLASH_CR_PG);
    FLASH->CR = reg | (FLASH_CR_OPTSTRT | FLASH_CR_EOPIE); 
    
    *(uint32_t *)0x1FFF5000 = 0xFFFFFFFF;
    
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
    
    FLASH->SR |= FLASH_SR_EOP;  //CLR EOP
    
    if(status != FLASH_TIMEOUT)
    {
      /* If the program operation is completed, disable the OPTPG Bit */
      FLASH->CR &= ~FLASH_CR_PGSTRT;
    }

    FLASH_Lock();
  }
  /* Return the Option Byte Data Program Status */
  return status;
}

/**
  * @brief  Write protects the desired pages
  * @param  OB_WRP: specifies the address of the pages to be write protected.
  *          This parameter can be:
  *             @arg OB_WRP_Pages0to7 ... OB_WRP_Pages24to27
  *             @arg OB_WRP_AllPages
  * @retval FLASH Status: The returned value can be: 
  *         FLASH_BUSY, FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  */
FLASH_Status FLASH_OB_EnableWRP(uint32_t OB_WRP)
{
  FLASH_Status status = FLASH_COMPLETE;
  uint32_t reg;

  /* Check the parameters */
  assert_param(IS_OB_WRP(OB_WRP));
  
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);

  if(status == FLASH_COMPLETE)
  {
    FLASH_Unlock(FLASH_KEY1, FLASH_KEY2);
    FLASH_OB_Unlock(FLASH_OPTKEY1, FLASH_OPTKEY2);
    
    FLASH->OPTR = FLASH->OPTR;
    FLASH->PCROPR = FLASH->PCROPR;
    FLASH->WRPR = (~OB_WRP) & 0xF;
    
    reg = FLASH->CR;
    reg &= ~(FLASH_CR_ERRIE | FLASH_CR_EOPIE | FLASH_CR_PGSTRT | FLASH_CR_SER | FLASH_CR_MER | FLASH_CR_PER | FLASH_CR_PG);
    FLASH->CR = reg | (FLASH_CR_OPTSTRT | FLASH_CR_EOPIE); 
    
    *(uint32_t *)0x1FFF5000 = 0xFFFFFFFF;
    
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
    
    FLASH->SR |= FLASH_SR_EOP;  //CLR EOP
    
    if(status != FLASH_TIMEOUT)
    {
      /* If the program operation is completed, disable the OPTPG Bit */
      FLASH->CR &= ~FLASH_CR_PGSTRT;
    }

    FLASH_Lock();
  }
  /* Return the Option Byte Data Program Status */
  return status;
}

/**
  * @brief  Write protects the desired pages
  * @note   Each bit corresponds to a STEP of 4Kbytes
  * @param  Start: This parameter can be 0x02 ~ 0x05
  * @param  End:   This parameter can be 0x02 ~ 0x05
  * @retval FLASH Status: The returned value can be: 
  *         FLASH_BUSY, FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  */
FLASH_Status FLASH_OB_EnableSDK(uint32_t Start, uint32_t End)
{
  FLASH_Status status = FLASH_COMPLETE;
  uint32_t reg;

  /* Check the parameters */
  assert_param(IS_OB_SDK(Start));
  assert_param(IS_OB_SDK(End));
  
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);

  if(status == FLASH_COMPLETE)
  {
    FLASH_Unlock(FLASH_KEY1, FLASH_KEY2);
    FLASH_OB_Unlock(FLASH_OPTKEY1, FLASH_OPTKEY2);
    
    FLASH->OPTR = FLASH->OPTR;
    FLASH->PCROPR = (End<<8) | Start;
    FLASH->WRPR = FLASH->WRPR;
    
    reg = FLASH->CR;
    reg &= ~(FLASH_CR_ERRIE | FLASH_CR_EOPIE | FLASH_CR_PGSTRT | FLASH_CR_SER | FLASH_CR_MER | FLASH_CR_PER | FLASH_CR_PG);
    FLASH->CR = reg | (FLASH_CR_OPTSTRT | FLASH_CR_EOPIE); 
    
    *(uint32_t *)0x1FFF5000 = 0xFFFFFFFF;
    
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
    
    FLASH->SR |= FLASH_SR_EOP;  //CLR EOP
    
    if(status != FLASH_TIMEOUT)
    {
      /* If the program operation is completed, disable the OPTPG Bit */
      FLASH->CR &= ~FLASH_CR_PGSTRT;
    }

    FLASH_Lock();
  }
  /* Return the Option Byte Data Program Status */
  return status;
}

/**
  * @brief  Enables or disables the read out protection.
  * @param  OB_RDP: specifies the read protection level. 
  *          This parameter can be:
  *             @arg OB_RDP_Level_0: No protection
  *             @arg OB_RDP_Level_1: Read protection of the memory
  * @retval FLASH Status: The returned value can be: 
  *         FLASH_BUSY, FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  */
FLASH_Status FLASH_OB_RDPConfig(uint32_t OB_RDP)
{
  FLASH_Status status = FLASH_COMPLETE;
  uint32_t reg;
  uint32_t tmp;
  
  /* Check the parameters */
  assert_param(IS_OB_RDP(OB_RDP));
  status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
  
  if(status == FLASH_COMPLETE)
  {
    FLASH_Unlock(FLASH_KEY1, FLASH_KEY2);
    FLASH_OB_Unlock(FLASH_OPTKEY1, FLASH_OPTKEY2);
    
    tmp = FLASH->OPTR & 0xFF00;
    FLASH->OPTR = tmp | OB_RDP;
    FLASH->PCROPR = FLASH->PCROPR;
    FLASH->WRPR = FLASH->WRPR;
    
    reg = FLASH->CR;
    reg &= ~(FLASH_CR_ERRIE | FLASH_CR_EOPIE | FLASH_CR_PGSTRT | FLASH_CR_SER | FLASH_CR_MER | FLASH_CR_PER | FLASH_CR_PG);
    FLASH->CR = reg | (FLASH_CR_OPTSTRT | FLASH_CR_EOPIE); 
    
    *(uint32_t *)0x1FFF5000 = 0xFFFFFFFF;
    
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
    
    FLASH->SR |= FLASH_SR_EOP;  //CLR EOP
    
    if(status != FLASH_TIMEOUT)
    {
      /* If the program operation is completed, disable the OPTPG Bit */
      FLASH->CR &= ~FLASH_CR_PGSTRT;
    }

    FLASH_Lock();
  }
  /* Return the Option Byte Data Program Status */
  return status;
}

/**
  * @brief  Sets or resets the BOOT1 option bit.
  * @param  OB_BOOT1: Set or Reset the BOOT1 option bit.
  *          This parameter can be one of the following values:
  *             @arg OB_BOOT1_RESET: BOOT1 option bit reset
  *             @arg OB_BOOT1_SET: BOOT1 option bit set
  * @retval FLASH Status: The returned value can be: 
  *         FLASH_BUSY, FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  */
FLASH_Status FLASH_OB_BOOTConfig(uint32_t OB_BOOT1)
{
  FLASH_Status status = FLASH_COMPLETE; 
  uint32_t reg;
  uint32_t tmp;

  /* Check the parameters */
  assert_param(IS_OB_BOOT1(OB_BOOT1));

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
  
  if(status == FLASH_COMPLETE)
  {  
    FLASH_Unlock(FLASH_KEY1, FLASH_KEY2);
    FLASH_OB_Unlock(FLASH_OPTKEY1, FLASH_OPTKEY2);
    
    tmp = FLASH->OPTR & (~FLASH_OPTR_nBOOT1);
    FLASH->OPTR = tmp | OB_BOOT1;
    FLASH->PCROPR = FLASH->PCROPR;
    FLASH->WRPR = FLASH->WRPR;
    
    reg = FLASH->CR;
    reg &= ~(FLASH_CR_ERRIE | FLASH_CR_EOPIE | FLASH_CR_PGSTRT | FLASH_CR_SER | FLASH_CR_MER | FLASH_CR_PER | FLASH_CR_PG);
    FLASH->CR = reg | (FLASH_CR_OPTSTRT | FLASH_CR_EOPIE); 
    
    *(uint32_t *)0x1FFF5000 = 0xFFFFFFFF;
    
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
    
    FLASH->SR |= FLASH_SR_EOP;  //CLR EOP
    
    if(status != FLASH_TIMEOUT)
    {
      /* If the program operation is completed, disable the OPTPG Bit */
      FLASH->CR &= ~FLASH_CR_PGSTRT;
    }

    FLASH_Lock();
  }
  /* Return the Option Byte Data Program Status */
  return status;
}

/**
  * @brief  Programs the FLASH User Option Byte: nBOOT1, NRST_MODE, WWDG_SW, IWDG_SW, BOR_LEV[2:0], BOR_EN
  * @param  OB_USER: Selects all user option bytes
  *          This parameter is a combination of the following values:
  *             @arg OB_BOOT1_RESET / OB_BOOT1_SET:                BOOT1 Reset / Set 
  *             @arg OB_NRST_MODE_RESET / OB_NRST_MODE_SET:        GPIO / Input Reset 
  *             @arg OB_WWDG_SW / OB_WWDG_HW:                      Software WWDG / Hardware WWDG
  *             @arg OB_IWDG_SW / OB_IWDG_HW:                      Software IWDG / Hardware IWDG
  *             @arg OB_BOR_LEV_1d8_1d7 ... OB_BOR_LEV_3d2_3d1 :   Rising threshold, falling threshold
  *             @arg OB_BOR_EN / OB_BOR_DIS:                       BOR EN / BOR DIS
  * @retval FLASH Status: The returned value can be: 
  *         FLASH_BUSY, FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  */
FLASH_Status FLASH_OB_WriteUser(uint32_t OB_USER)
{
  FLASH_Status status = FLASH_COMPLETE;
  uint32_t reg;
  uint32_t tmp;

  status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);

  if(status == FLASH_COMPLETE)
  {
    FLASH_Unlock(FLASH_KEY1, FLASH_KEY2);
    FLASH_OB_Unlock(FLASH_OPTKEY1, FLASH_OPTKEY2);
    
    tmp = FLASH->OPTR & 0x00FF; 
    FLASH->OPTR = tmp | OB_USER; 
    FLASH->PCROPR = FLASH->PCROPR; 
    FLASH->WRPR = FLASH->WRPR; 
    
    reg = FLASH->CR;
    reg &= ~(FLASH_CR_ERRIE | FLASH_CR_EOPIE | FLASH_CR_PGSTRT | FLASH_CR_SER | FLASH_CR_MER | FLASH_CR_PER | FLASH_CR_PG);
    FLASH->CR = reg | (FLASH_CR_OPTSTRT | FLASH_CR_EOPIE); 
    
    *(uint32_t *)0x1FFF5000 = 0xFFFFFFFF;
    
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
    
    FLASH->SR |= FLASH_SR_EOP;  //CLR EOP
    
    if(status != FLASH_TIMEOUT)
    {
      /* If the program operation is completed, disable the OPTPG Bit */
      FLASH->CR &= ~FLASH_CR_PGSTRT;
    }

    FLASH_Lock();
  }
  /* Return the Option Byte Data Program Status */
  return status;

}

/**
  * @brief  Programs a half word at a specified Option Byte Data address.
  * @param  OPTR: specifies the data to be programmed(OPTR).
  * @param  PCROPR: specifies the data to be programmed(SDKR).
  * @param  WRPR: specifies the data to be programmed(WRPR).
  * @retval FLASH Status: The returned value can be: 
  *         FLASH_BUSY, FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  */
FLASH_Status FLASH_OB_ProgramData(uint32_t OPTR, uint32_t PCROPR, uint32_t WRPR)
{
  FLASH_Status status = FLASH_COMPLETE;
  uint32_t reg;

  status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);

  if(status == FLASH_COMPLETE)
  {
    FLASH_Unlock(FLASH_KEY1, FLASH_KEY2);
    FLASH_OB_Unlock(FLASH_OPTKEY1, FLASH_OPTKEY2);
    
    FLASH->OPTR = OPTR; 
    FLASH->PCROPR = PCROPR; 
    FLASH->WRPR = WRPR; 
    
    reg = FLASH->CR;
    reg &= ~(FLASH_CR_ERRIE | FLASH_CR_EOPIE | FLASH_CR_PGSTRT | FLASH_CR_SER | FLASH_CR_MER | FLASH_CR_PER | FLASH_CR_PG);
    FLASH->CR = reg | (FLASH_CR_OPTSTRT | FLASH_CR_EOPIE); 
    
    *(uint32_t *)0x1FFF5000 = 0xFFFFFFFF;
    
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
    
    FLASH->SR |= FLASH_SR_EOP;  //CLR EOP
    
    if(status != FLASH_TIMEOUT)
    {
      /* If the program operation is completed, disable the OPTPG Bit */
      FLASH->CR &= ~FLASH_CR_PGSTRT;
    }

    FLASH_Lock();
  }
  /* Return the Option Byte Data Program Status */
  return status;
}

/**
  * @brief  Returns the FLASH User Option Bytes values.
  * @param  None
  * @retval The FLASH User Option Bytes .
  */
uint32_t FLASH_OB_GetUser(void)
{
  /* Return the User Option Byte */
  return (uint32_t)(FLASH->OPTR & 0xFF00);
}

/**
  * @brief  Returns the FLASH Write Protection Option Bytes value.
  * @param  None
  * @retval The FLASH Write Protection Option Bytes value
  */
uint32_t FLASH_OB_GetWRP(void)
{
  /* Return the FLASH write protection Register value */
  return (uint32_t)((~FLASH->WRPR) & 0xF);
}

/**
  * @brief  Returns the FLASH SDK protected areas
  * @param  None
  * @retval The FLASH Write SDK protected areas value
  */
uint32_t FLASH_OB_GetSDK(void)
{
  return (uint32_t)(FLASH->PCROPR);
}

/**
  * @brief  Checks whether the FLASH Read out Protection Status is set or not.
  * @param  None
  * @retval FLASH ReadOut Protection Status(SET or RESET)
  */
FlagStatus FLASH_OB_GetRDP(void)
{
  FlagStatus readstatus = RESET;
  
  if((FLASH->OPTR & FLASH_OPTR_RDP) != 0xAA)
  {
    readstatus = SET;
  }
  else
  {
    readstatus = RESET;
  }
  return readstatus;
}

/**
  * @brief  Enables or disables the specified FLASH interrupts.
  * @param  FLASH_IT: specifies the FLASH interrupt sources to be enabled or 
  *         disabled.
  *          This parameter can be any combination of the following values:
  *             @arg FLASH_IT_EOP: FLASH end of programming Interrupt
  *             @arg FLASH_IT_ERR: FLASH Error Interrupt
  * @param  NewState: ENABLE or DISABLE
  * @retval None 
  */
void FLASH_ITConfig(uint32_t FLASH_IT, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_FLASH_IT(FLASH_IT)); 
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  
  if(NewState != DISABLE)
  {
    /* Enable the interrupt sources */
    FLASH->CR |= FLASH_IT;
  }
  else
  {
    /* Disable the interrupt sources */
    FLASH->CR &= ~(uint32_t)FLASH_IT;
  }
}

/**
  * @brief  Checks whether the specified FLASH flag is set or not.
  * @param  FLASH_FLAG: specifies the FLASH flag to check.
  *          This parameter can be one of the following values:
  *             @arg FLASH_FLAG_BSY: FLASH write/erase operations in progress flag 
  *             @arg FLASH_FLAG_WRPERR: FLASH Write protected error flag
  *             @arg FLASH_FLAG_EOP: FLASH End of Programming flag
  *             @arg FLASH_FLAG_OPTVERR: FLASH OB Programming error flag 
  * @retval The new state of FLASH_FLAG (SET or RESET).
  */
FlagStatus FLASH_GetFlagStatus(uint32_t FLASH_FLAG)
{
  FlagStatus bitstatus = RESET;

  /* Check the parameters */
  assert_param(IS_FLASH_GET_FLAG(FLASH_FLAG));

  if((FLASH->SR & FLASH_FLAG) != (uint32_t)RESET)
  {
    bitstatus = SET;
  }
  else
  {
    bitstatus = RESET;
  }
  /* Return the new state of FLASH_FLAG (SET or RESET) */
  return bitstatus; 
}

/**
  * @brief  Clears the FLASH's pending flags.
  * @param  FLASH_FLAG: specifies the FLASH flags to clear.
  *          This parameter can be any combination of the following values:
  *             @arg FLASH_FLAG_WRPERR: FLASH Write protected error flag
  *             @arg FLASH_FLAG_EOP: FLASH End of Programming flag
  *             @arg FLASH_FLAG_OPTVERR: FLASH OB Programming error flag
  * @retval None
  */
void FLASH_ClearFlag(uint32_t FLASH_FLAG)
{
  /* Check the parameters */
  assert_param(IS_FLASH_CLEAR_FLAG(FLASH_FLAG));
  
  /* Clear the flags */
  FLASH->SR = FLASH_FLAG;
}

/**
  * @brief  Returns the FLASH Status.
  * @param  None
  * @retval FLASH Status: The returned value can be: 
  *         FLASH_BUSY, FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP or FLASH_COMPLETE.
  */
FLASH_Status FLASH_GetStatus(void)
{
  FLASH_Status FLASHstatus = FLASH_COMPLETE;
  
  if((FLASH->SR & FLASH_SR_BSY) == FLASH_SR_BSY) 
  {
    FLASHstatus = FLASH_BUSY;
  }
  else 
  {  
    if((FLASH->SR & (uint32_t)FLASH_SR_WRPERR)!= (uint32_t)0x00)
    { 
      FLASHstatus = FLASH_ERROR_WRP;
    }
    else 
    {
      FLASHstatus = FLASH_COMPLETE;
    }
  }
  /* Return the FLASH Status */
  return FLASHstatus;
}


/**
  * @brief  Waits for a FLASH operation to complete or a TIMEOUT to occur.
  * @param  Timeout: FLASH programming Timeout
  * @retval FLASH Status: The returned value can be: 
  *         FLASH_BUSY, FLASH_ERROR_PROGRAM, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  */
FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout)
{ 
  FLASH_Status status = FLASH_COMPLETE;
   
  /* Check for the FLASH Status */
  status = FLASH_GetStatus();
  
  /* Wait for a FLASH operation to complete or a TIMEOUT to occur */
  while((status == FLASH_BUSY) && (Timeout != 0x00))
  {
    status = FLASH_GetStatus();
    Timeout--;
    __NOP();
    __NOP();
    __NOP();
  }
  
  if(Timeout == 0x00 )
  {
    status = FLASH_TIMEOUT;
  }
  /* Return the operation status */
  return status;
}

/**
  * @}
  */

/**
  * @}
  */

 /**
  * @}
  */
