/**
  * @brief  Program halfword, word or double word at a specified address  with interrupt enabled.
  * @note   The function HAL_FLASH_Unlock() should be called before to unlock the FLASH interface
  *         The function HAL_FLASH_Lock() should be called after to lock the FLASH interface
  *
  * @note   If an erase and a program operations are requested simultaneously,    
  *         the erase operation is performed before the program one.
  *
  * @param  TypeProgram: Indicate the way to program at a specified address.
  *                      This parameter can be a value of @ref FLASH_Type_Program
  * @param  Address:     Specifies the address to be programmed.
  * @param  Data:        Specifies the data to be programmed
  * 
  * @retval HAL_StatusTypeDef HAL Status
  */
HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
{
  HAL_StatusTypeDef status = HAL_OK;
  
  /* Process Locked */
  __HAL_LOCK(&pFlash);

  /* Check the parameters */
  assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));
  assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));

#if defined(FLASH_BANK2_END)
  /* If procedure already ongoing, reject the next one */
  if (pFlash.ProcedureOnGoing != FLASH_PROC_NONE)
  {
    return HAL_ERROR;
  }
  
  if(Address <= FLASH_BANK1_END)
  {
    /* Enable End of FLASH Operation and Error source interrupts */
    __HAL_FLASH_ENABLE_IT(FLASH_IT_EOP_BANK1 | FLASH_IT_ERR_BANK1);

  }else
  {
    /* Enable End of FLASH Operation and Error source interrupts */
    __HAL_FLASH_ENABLE_IT(FLASH_IT_EOP_BANK2 | FLASH_IT_ERR_BANK2);
  }
#else
  /* Enable End of FLASH Operation and Error source interrupts */
  __HAL_FLASH_ENABLE_IT(FLASH_IT_EOP | FLASH_IT_ERR);
#endif /* FLASH_BANK2_END */
  
  pFlash.Address = Address;
  pFlash.Data = Data;

  if(TypeProgram == FLASH_TYPEPROGRAM_HALFWORD)
  {
    pFlash.ProcedureOnGoing = FLASH_PROC_PROGRAMHALFWORD;
    /*Program halfword (16-bit) at a specified address.*/
    pFlash.DataRemaining = 1;
  }
  else if(TypeProgram == FLASH_TYPEPROGRAM_WORD)
  {
    pFlash.ProcedureOnGoing = FLASH_PROC_PROGRAMWORD;
    /*Program word (32-bit : 2*16-bit) at a specified address.*/
    pFlash.DataRemaining = 2;
  }
  else
  {
    pFlash.ProcedureOnGoing = FLASH_PROC_PROGRAMDOUBLEWORD;
    /*Program double word (64-bit : 4*16-bit) at a specified address.*/
    pFlash.DataRemaining = 4;
  }

  /*Program halfword (16-bit) at a specified address.*/
  FLASH_Program_HalfWord(Address, (uint16_t)Data);

  return status;
}
/**
  * @brief  Program byte, halfword, word or double word at a specified address
  * @param  TypeProgram:  Indicate the way to program at a specified address.
  *                           This parameter can be a value of @ref FLASH_Type_Program
  * @param  Address:  specifies the address to be programmed.
  * @param  Data: specifies the data to be programmed
  * 
  * @retval HAL_StatusTypeDef HAL Status
  */
HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
{
  HAL_StatusTypeDef status = HAL_ERROR;
  
  /* Process Locked */
  __HAL_LOCK(&pFlash);

  /* Check the parameters */
  assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
  
  if(status == HAL_OK)
  {
    switch(TypeProgram)
    {
      case FLASH_TYPEPROGRAM_BYTE :
      {
        /*Program byte (8-bit) at a specified address.*/
        FLASH_Program_Byte(Address, (uint8_t) Data);
        break;
      }
      
      case FLASH_TYPEPROGRAM_HALFWORD :
      {
        /*Program halfword (16-bit) at a specified address.*/
        FLASH_Program_HalfWord(Address, (uint16_t) Data);
        break;
      }
      
      case FLASH_TYPEPROGRAM_WORD :
      {
        /*Program word (32-bit) at a specified address.*/
        FLASH_Program_Word(Address, (uint32_t) Data);
        break;
      }
      
      case FLASH_TYPEPROGRAM_DOUBLEWORD :
      {
        /*Program double word (64-bit) at a specified address.*/
        FLASH_Program_DoubleWord(Address, Data);
        break;
      }
      default :
        break;
    }
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
    
    /* If the program operation is completed, disable the PG Bit */
    FLASH->CR &= (~FLASH_CR_PG);
  }

  /* Process Unlocked */
  __HAL_UNLOCK(&pFlash);

  return status;
}
/**
  * @brief   Program byte, halfword, word or double word at a specified address  with interrupt enabled.
  * @param  TypeProgram:  Indicate the way to program at a specified address.
  *                           This parameter can be a value of @ref FLASH_Type_Program
  * @param  Address:  specifies the address to be programmed.
  * @param  Data: specifies the data to be programmed
  * 
  * @retval HAL Status
  */
HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
{
  HAL_StatusTypeDef status = HAL_OK;
  
  /* Process Locked */
  __HAL_LOCK(&pFlash);

  /* Check the parameters */
  assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));

  /* Enable End of FLASH Operation interrupt */
  __HAL_FLASH_ENABLE_IT(FLASH_IT_EOP);
  
  /* Enable Error source interrupt */
  __HAL_FLASH_ENABLE_IT(FLASH_IT_ERR);
  
  /* Clear pending flags (if any) */  
  __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP    | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |\
                         FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR| FLASH_FLAG_ERSERR);  

  pFlash.ProcedureOnGoing = FLASH_PROC_PROGRAM;
  pFlash.Address = Address;
  
  switch(TypeProgram)
  {
    case FLASH_TYPEPROGRAM_BYTE :
    {
      /*Program byte (8-bit) at a specified address.*/
      FLASH_Program_Byte(Address, (uint8_t) Data);
      break;
    }
    
    case FLASH_TYPEPROGRAM_HALFWORD :
    {
      /*Program halfword (16-bit) at a specified address.*/
      FLASH_Program_HalfWord(Address, (uint16_t) Data);
      break;
    }
    
    case FLASH_TYPEPROGRAM_WORD :
    {
      /*Program word (32-bit) at a specified address.*/
      FLASH_Program_Word(Address, (uint32_t) Data);
      break;
    }
    
    case FLASH_TYPEPROGRAM_DOUBLEWORD :
    {
      /*Program double word (64-bit) at a specified address.*/
      FLASH_Program_DoubleWord(Address, Data);
      break;
    }
    default :
      break;
  }
  return status;
}
/**
  * @brief   Program byte, halfword, word or double word at a specified address  with interrupt enabled.
  * @param  TypeProgram:  Indicate the way to program at a specified address.
  *                           This parameter can be a value of @ref FLASH_Type_Program
  * @param  Address:  specifies the address to be programmed.
  * @param  Data: specifies the data to be programmed
  * 
  * @retval HAL Status
  */
HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
{
  HAL_StatusTypeDef status = HAL_OK;
  
  /* Process Locked */
  __HAL_LOCK(&pFlash);

  /* Check the parameters */
  assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));

  /* Enable End of FLASH Operation interrupt */
  __HAL_FLASH_ENABLE_IT(FLASH_IT_EOP);
  
  /* Enable Error source interrupt */
  __HAL_FLASH_ENABLE_IT(FLASH_IT_ERR);

  pFlash.ProcedureOnGoing = FLASH_PROC_PROGRAM;
  pFlash.Address = Address;

  if(TypeProgram == FLASH_TYPEPROGRAM_BYTE)
  {
    /*Program byte (8-bit) at a specified address.*/
      FLASH_Program_Byte(Address, (uint8_t) Data);
  }
  else if(TypeProgram == FLASH_TYPEPROGRAM_HALFWORD)
  {
    /*Program halfword (16-bit) at a specified address.*/
    FLASH_Program_HalfWord(Address, (uint16_t) Data);
  }
  else if(TypeProgram == FLASH_TYPEPROGRAM_WORD)
  {
    /*Program word (32-bit) at a specified address.*/
    FLASH_Program_Word(Address, (uint32_t) Data);
  }
  else
  {
    /*Program double word (64-bit) at a specified address.*/
    FLASH_Program_DoubleWord(Address, Data);
  }

  return status;
}
/**
  * @brief  Program halfword, word or double word at a specified address  with interrupt enabled.
  * @note   The function HAL_FLASH_Unlock() should be called before to unlock the FLASH interface
  *         The function HAL_FLASH_Lock() should be called after to lock the FLASH interface
  *
  * @note   If an erase and a program operations are requested simultaneously,    
  *         the erase operation is performed before the program one.
  *  
  * @param  TypeProgram: Indicate the way to program at a specified address.
  *                      This parameter can be a value of @ref FLASH_Type_Program
  * @param  Address:     Specifies the address to be programmed.
  * @param  Data:        Specifies the data to be programmed
  * 
  * @retval HAL_StatusTypeDef HAL Status
  */
HAL_StatusTypeDef HAL_FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
{
  HAL_StatusTypeDef status = HAL_OK;
  
  /* Process Locked */
  __HAL_LOCK(&pFlash);

  /* Check the parameters */
  assert_param(IS_TYPEPROGRAM(TypeProgram));
  assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));

  /* Enable End of FLASH Operation and Error source interrupts */
  __HAL_FLASH_ENABLE_IT((FLASH_IT_EOP | FLASH_IT_ERR));
  
  pFlash.Address = Address;
  pFlash.Data = Data;

  if(TypeProgram == TYPEPROGRAM_HALFWORD)
  {
    pFlash.ProcedureOnGoing = FLASH_PROC_PROGRAMHALFWORD;
    /*Program halfword (16-bit) at a specified address.*/
    pFlash.DataRemaining = 1;
  }
  else if(TypeProgram == TYPEPROGRAM_WORD)
  {
    pFlash.ProcedureOnGoing = FLASH_PROC_PROGRAMWORD;
    /*Program word (32-bit : 2*16-bit) at a specified address.*/
    pFlash.DataRemaining = 2;
  }
  else
  {
    pFlash.ProcedureOnGoing = FLASH_PROC_PROGRAMDOUBLEWORD;
    /*Program double word (64-bit : 4*16-bit) at a specified address.*/
    pFlash.DataRemaining = 4;
  }

  /*Program halfword (16-bit) at a specified address.*/
  FLASH_Program_HalfWord(Address, (uint16_t)Data);

  return status;
}
/**
  * @brief This function handles FLASH interrupt request.
  * @retval None
  */
void HAL_FLASH_IRQHandler(void)
{
  uint32_t addresstmp;
  /* If the operation is completed, disable the PG, PER and MER Bits */
  CLEAR_BIT(FLASH->CR, (FLASH_CR_PG | FLASH_CR_PER | FLASH_CR_MER));

  /* Check FLASH End of Operation flag  */
  if(__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP))
  {
    /* Clear FLASH End of Operation pending bit */
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP);
    
    if(pFlash.ProcedureOnGoing == FLASH_PROC_PAGEERASE)
    {
      /* Nb of pages to erased can be decreased */
      pFlash.DataRemaining--;

      /* Indicate user which page address has been erased*/
      HAL_FLASH_EndOfOperationCallback(pFlash.Address);

      /* Check if there are still pages to erase*/
      if(pFlash.DataRemaining != 0)
      {
        /* Increment page address to next page */
        pFlash.Address += FLASH_PAGE_SIZE;
        addresstmp = pFlash.Address;
        FLASH_PageErase(addresstmp);
      }
      else
      {
        /*No more pages to Erase*/

        /*Reset Address and stop Erase pages procedure*/
        pFlash.Address = 0xFFFFFFFF;
        pFlash.ProcedureOnGoing = FLASH_PROC_NONE;
      }
    }
    else if(pFlash.ProcedureOnGoing == FLASH_PROC_MASSERASE)
    {
      /*MassErase ended. Return the selected bank*/
      /* FLASH EOP interrupt user callback */
      HAL_FLASH_EndOfOperationCallback(0);

      /* Stop Mass Erase procedure*/
      pFlash.ProcedureOnGoing = FLASH_PROC_NONE;
    }
    else
    {
      /* Nb of 16-bit data to program can be decreased */
      pFlash.DataRemaining--;
      
      /* Check if there are still 16-bit data to program */
      if(pFlash.DataRemaining != 0)
      {
        /* Increment address to 16-bit */
        pFlash.Address += 2;
        addresstmp = pFlash.Address;

        /* Shift to have next 16-bit data */
        pFlash.Data = (pFlash.Data >> 16);

        /*Program halfword (16-bit) at a specified address.*/
        FLASH_Program_HalfWord(addresstmp, (uint16_t)pFlash.Data);
      }
      else
      {
        /*Program ended. Return the selected address*/
        /* FLASH EOP interrupt user callback */
        if (pFlash.ProcedureOnGoing == FLASH_PROC_PROGRAMHALFWORD)
        {
          HAL_FLASH_EndOfOperationCallback(pFlash.Address);
        }
        else if (pFlash.ProcedureOnGoing == FLASH_PROC_PROGRAMWORD)
        {
          HAL_FLASH_EndOfOperationCallback(pFlash.Address-2);
        }
        else 
        {
          HAL_FLASH_EndOfOperationCallback(pFlash.Address-6);
        }

        /* Reset Address and stop Program procedure*/
        pFlash.Address = 0xFFFFFFFF;
        pFlash.ProcedureOnGoing = FLASH_PROC_NONE;
      }
    }
/**
  * @brief This function handles FLASH interrupt request.
  * @retval None
  */
void HAL_FLASH_IRQHandler(void)
{
  uint32_t addresstmp = 0;
  
  /* Check FLASH operation error flags */
#if defined(FLASH_BANK2_END)
  if(__HAL_FLASH_GET_FLAG(FLASH_FLAG_WRPERR_BANK1) || __HAL_FLASH_GET_FLAG(FLASH_FLAG_PGERR_BANK1) || \
    (__HAL_FLASH_GET_FLAG(FLASH_FLAG_WRPERR_BANK2) || __HAL_FLASH_GET_FLAG(FLASH_FLAG_PGERR_BANK2)))
#else
  if(__HAL_FLASH_GET_FLAG(FLASH_FLAG_WRPERR) ||__HAL_FLASH_GET_FLAG(FLASH_FLAG_PGERR))
#endif /* FLASH_BANK2_END */
  {
    /*return the faulty address*/
    addresstmp = pFlash.Address;
    /* Reset address */
    pFlash.Address = 0xFFFFFFFF;
  
    /*Save the Error code*/
    FLASH_SetErrorCode();
    
    /* FLASH error interrupt user callback */
    HAL_FLASH_OperationErrorCallback(addresstmp);

    /* Stop the procedure ongoing*/
    pFlash.ProcedureOnGoing = FLASH_PROC_NONE;
  }

  /* Check FLASH End of Operation flag  */
#if defined(FLASH_BANK2_END)
  if(__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP_BANK1))
  {
    /* Clear FLASH End of Operation pending bit */
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP_BANK1);
#else
  if(__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP))
  {
    /* Clear FLASH End of Operation pending bit */
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP);
#endif /* FLASH_BANK2_END */
    
    /* Process can continue only if no error detected */
    if(pFlash.ProcedureOnGoing != FLASH_PROC_NONE)
    {
      if(pFlash.ProcedureOnGoing == FLASH_PROC_PAGEERASE)
      {
        /* Nb of pages to erased can be decreased */
        pFlash.DataRemaining--;

        /* Check if there are still pages to erase*/
        if(pFlash.DataRemaining != 0)
        {
          addresstmp = pFlash.Address;
          /*Indicate user which sector has been erased*/
          HAL_FLASH_EndOfOperationCallback(addresstmp);

          /*Increment sector number*/
          addresstmp = pFlash.Address + FLASH_PAGE_SIZE;
          pFlash.Address = addresstmp;

          /* If the erase operation is completed, disable the PER Bit */
          CLEAR_BIT(FLASH->CR, FLASH_CR_PER);

          FLASH_PageErase(addresstmp);
        }
        else
        {
          /*No more pages to Erase, user callback can be called.*/
          /*Reset Sector and stop Erase pages procedure*/
          pFlash.Address = addresstmp = 0xFFFFFFFF;
          pFlash.ProcedureOnGoing = FLASH_PROC_NONE;
          /* FLASH EOP interrupt user callback */
          HAL_FLASH_EndOfOperationCallback(addresstmp);
        }
      }
      else if(pFlash.ProcedureOnGoing == FLASH_PROC_MASSERASE)
      {
        /* Operation is completed, disable the MER Bit */
        CLEAR_BIT(FLASH->CR, FLASH_CR_MER);

#if defined(FLASH_BANK2_END)
        /* Stop Mass Erase procedure if no pending mass erase on other bank */
        if (HAL_IS_BIT_CLR(FLASH->CR2, FLASH_CR2_MER))
        {
#endif /* FLASH_BANK2_END */
          /* MassErase ended. Return the selected bank*/
          /* FLASH EOP interrupt user callback */
          HAL_FLASH_EndOfOperationCallback(0);

          /* Stop Mass Erase procedure*/
          pFlash.ProcedureOnGoing = FLASH_PROC_NONE;
        }
#if defined(FLASH_BANK2_END)
      }
#endif /* FLASH_BANK2_END */
      else
      {
        /* Nb of 16-bit data to program can be decreased */
        pFlash.DataRemaining--;
        
        /* Check if there are still 16-bit data to program */
        if(pFlash.DataRemaining != 0)
        {
          /* Increment address to 16-bit */
          pFlash.Address += 2;
          addresstmp = pFlash.Address;
          
          /* Shift to have next 16-bit data */
          pFlash.Data = (pFlash.Data >> 16);
          
          /* Operation is completed, disable the PG Bit */
          CLEAR_BIT(FLASH->CR, FLASH_CR_PG);

          /*Program halfword (16-bit) at a specified address.*/
          FLASH_Program_HalfWord(addresstmp, (uint16_t)pFlash.Data);
        }
        else
        {
          /*Program ended. Return the selected address*/
          /* FLASH EOP interrupt user callback */
          if (pFlash.ProcedureOnGoing == FLASH_PROC_PROGRAMHALFWORD)
          {
            HAL_FLASH_EndOfOperationCallback(pFlash.Address);
          }
          else if (pFlash.ProcedureOnGoing == FLASH_PROC_PROGRAMWORD)
          {
            HAL_FLASH_EndOfOperationCallback(pFlash.Address - 2);
          }
          else 
          {
            HAL_FLASH_EndOfOperationCallback(pFlash.Address - 6);
          }
        
          /* Reset Address and stop Program procedure*/
          pFlash.Address = 0xFFFFFFFF;
          pFlash.ProcedureOnGoing = FLASH_PROC_NONE;
        }
      }
    }
  }