STM32G4x FLASH 读写(修改HAL库驱动)

2023-12-21 14:29:51

主要工作就是把HAL的超时用LL库延时替代,保留了中断擦写模式、轮询等待擦写,待验证哈。

直接给代码。

/**
  ******************************************************************************
  * @file       flash.h
  * @author     Amos
  * @brief      Header for flash.c file.
  * @version    v1.0.0
  ******************************************************************************
  * @attention
  ******************************************************************************
  */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef FLASH_H__
#define FLASH_H__

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Exported constants --------------------------------------------------------*/
/**
  * @brief  FLASH_Error FLASH Error
  */
#define FLASH_ERROR_NONE        0x00000000U
#define FLASH_ERROR_OP          FLASH_FLAG_OPERR
#define FLASH_ERROR_PROG        FLASH_FLAG_PROGERR
#define FLASH_ERROR_WRP         FLASH_FLAG_WRPERR
#define FLASH_ERROR_PGA         FLASH_FLAG_PGAERR
#define FLASH_ERROR_SIZ         FLASH_FLAG_SIZERR
#define FLASH_ERROR_PGS         FLASH_FLAG_PGSERR
#define FLASH_ERROR_MIS         FLASH_FLAG_MISERR
#define FLASH_ERROR_FAST        FLASH_FLAG_FASTERR
#define FLASH_ERROR_RD          FLASH_FLAG_RDERR
#define FLASH_ERROR_OPTV        FLASH_FLAG_OPTVERR
#define FLASH_ERROR_ECCC        FLASH_FLAG_ECCC
#define FLASH_ERROR_ECCD        FLASH_FLAG_ECCD
#if defined (FLASH_OPTR_DBANK)
#define FLASH_ERROR_ECCC2       FLASH_FLAG_ECCC2
#define FLASH_ERROR_ECCD2       FLASH_FLAG_ECCD2
#endif

/**
  * @brief  FLASH_Type_Erase FLASH Erase Type
  */
#define FLASH_TYPEERASE_PAGES       0x00U     /*!<Pages erase only*/
#define FLASH_TYPEERASE_MASSERASE   0x01U     /*!<Flash mass erase activation*/

/**
  * @brief  FLASH_Banks FLASH Banks
  */
#define FLASH_BANK_1            0x00000001U                   /*!< Bank 1   */
#if defined (FLASH_OPTR_DBANK)
#define FLASH_BANK_2            0x00000002U                   /*!< Bank 2   */
#define FLASH_BANK_BOTH         (FLASH_BANK_1 | FLASH_BANK_2) /*!< Bank1 and Bank2  */
#else
#define FLASH_BANK_BOTH         FLASH_BANK_1                  /*!< Bank 1   */
#endif


/**
  * @brief  FLASH_Type_Program FLASH Program Type
  */
#define FLASH_TYPEPROGRAM_DOUBLEWORD        0x00U   /*!< Program a double-word (64-bit) at a specified address.*/
#define FLASH_TYPEPROGRAM_FAST              0x01U   /*!< Fast program a 32 row double-word (64-bit) at a specified address.
                                                        And another 32 row double-word (64-bit) will be programmed */
#define FLASH_TYPEPROGRAM_FAST_AND_LAST     0x02U   /*!< Fast program a 32 row double-word (64-bit) at a specified address.
                                                        And this is the last 32 row double-word (64-bit) programmed */
/**
  * @brief  FLASH_OB_Type FLASH Option Bytes Type
  */
#define OPTIONBYTE_WRP          0x01U   /*!< WRP option byte configuration */
#define OPTIONBYTE_RDP          0x02U   /*!< RDP option byte configuration */
#define OPTIONBYTE_USER         0x04U   /*!< USER option byte configuration */
#define OPTIONBYTE_PCROP        0x08U   /*!< PCROP option byte configuration */
#define OPTIONBYTE_BOOT_LOCK    0x10U   /*!< Boot lock option byte configuration */
#define OPTIONBYTE_SEC          0x20U   /*!< Securable memory option byte configuration */

/**
  * @brief  FLASH_OB_WRP_Area FLASH WRP Area
  */
#define OB_WRPAREA_BANK1_AREAA  0x00U   /*!< Flash Bank 1 Area A */
#define OB_WRPAREA_BANK1_AREAB  0x01U   /*!< Flash Bank 1 Area B */
#if defined (FLASH_OPTR_DBANK)
#define OB_WRPAREA_BANK2_AREAA  0x02U   /*!< Flash Bank 2 Area A */
#define OB_WRPAREA_BANK2_AREAB  0x04U   /*!< Flash Bank 2 Area B */
#endif

/**
  * @brief  FLASH_OB_Boot_Lock FLASH Boot Lock
  */
#define OB_BOOT_LOCK_DISABLE    0x00000000U             /*!< Boot Lock Disable */
#define OB_BOOT_LOCK_ENABLE     FLASH_SEC1R_BOOT_LOCK   /*!< Boot Lock Enable */

/**
  * @brief  FLASH_OB_Read_Protection FLASH Option Bytes Read Protection
  */
#define OB_RDP_LEVEL_0          0xAAU
#define OB_RDP_LEVEL_1          0xBBU
#define OB_RDP_LEVEL_2          0xCCU   /*!< Warning: When enabling read protection level 2 
                                            it's no more possible to go back to level 1 or 0 */
/**
  * @brief  FLASH_OB_USER_Type FLASH Option Bytes User Type
  */
#define OB_USER_BOR_LEV         0x00000001U     /*!< BOR reset Level */
#define OB_USER_nRST_STOP       0x00000002U     /*!< Reset generated when entering the stop mode */
#define OB_USER_nRST_STDBY      0x00000004U     /*!< Reset generated when entering the standby mode */
#define OB_USER_IWDG_SW         0x00000008U     /*!< Independent watchdog selection */
#define OB_USER_IWDG_STOP       0x00000010U     /*!< Independent watchdog counter freeze in stop mode */
#define OB_USER_IWDG_STDBY      0x00000020U     /*!< Independent watchdog counter freeze in standby mode */
#define OB_USER_WWDG_SW         0x00000040U     /*!< Window watchdog selection */
#if defined (FLASH_OPTR_DBANK)
#define OB_USER_BFB2            0x00000080U     /*!< Dual-bank boot */
#define OB_USER_DBANK           0x00000100U     /*!< Single bank with 128-bits data or two banks with 64-bits data */
#endif
#if defined (FLASH_OPTR_PB4_PUPEN)
#define OB_USER_PB4_PUPEN       0x00000100U     /*!< USB power delivery dead-battery/TDI pull-up */
#endif
#define OB_USER_nBOOT1          0x00000200U     /*!< Boot configuration */
#define OB_USER_SRAM_PE         0x00000400U     /*!< SRAM parity check enable (first 32kB of SRAM1 + CCM SRAM) */
#define OB_USER_CCMSRAM_RST     0x00000800U     /*!< CCMSRAM Erase when system reset */
#define OB_USER_nRST_SHDW       0x00001000U     /*!< Reset generated when entering the shutdown mode */
#define OB_USER_nSWBOOT0        0x00002000U     /*!< Software BOOT0 */
#define OB_USER_nBOOT0          0x00004000U     /*!< nBOOT0 option bit */
#define OB_USER_NRST_MODE       0x00008000U     /*!< Reset pin configuration */
#define OB_USER_IRHEN           0x00010000U     /*!< Internal Reset Holder enable */

/**
  * @brief  FLASH_OB_USER_BOR_LEVEL FLASH Option Bytes User BOR Level
  */
#define OB_BOR_LEVEL_0          FLASH_OPTR_BOR_LEV_0    /*!< Reset level threshold is around 1.7V */
#define OB_BOR_LEVEL_1          FLASH_OPTR_BOR_LEV_1    /*!< Reset level threshold is around 2.0V */
#define OB_BOR_LEVEL_2          FLASH_OPTR_BOR_LEV_2    /*!< Reset level threshold is around 2.2V */
#define OB_BOR_LEVEL_3          FLASH_OPTR_BOR_LEV_3    /*!< Reset level threshold is around 2.5V */
#define OB_BOR_LEVEL_4          FLASH_OPTR_BOR_LEV_4    /*!< Reset level threshold is around 2.8V */

/**
  * @brief  FLASH_OB_USER_nRST_STOP FLASH Option Bytes User Reset On Stop
  */
#define OB_STOP_RST             0x00000000U             /*!< Reset generated when entering the stop mode */
#define OB_STOP_NORST           FLASH_OPTR_nRST_STOP    /*!< No reset generated when entering the stop mode */

/**
  * @brief  FLASH_OB_USER_nRST_STANDBY FLASH Option Bytes User Reset On Standby
  */
#define OB_STANDBY_RST          0x00000000U             /*!< Reset generated when entering the standby mode */
#define OB_STANDBY_NORST        FLASH_OPTR_nRST_STDBY   /*!< No reset generated when entering the standby mode */

/**
  * @brief  FLASH_OB_USER_nRST_SHUTDOWN FLASH Option Bytes User Reset On Shutdown
  */
#define OB_SHUTDOWN_RST         0x00000000U             /*!< Reset generated when entering the shutdown mode */
#define OB_SHUTDOWN_NORST       FLASH_OPTR_nRST_SHDW    /*!< No reset generated when entering the shutdown mode */

/**
  * @brief  FLASH_OB_USER_IWDG_SW FLASH Option Bytes User IWDG Type
  */
#define OB_IWDG_HW              0x00000000U             /*!< Hardware independent watchdog */
#define OB_IWDG_SW              FLASH_OPTR_IWDG_SW      /*!< Software independent watchdog */

/**
  * @brief  FLASH_OB_USER_IWDG_STOP FLASH Option Bytes User IWDG Mode On Stop
  */
#define OB_IWDG_STOP_FREEZE     0x00000000U             /*!< Independent watchdog counter is frozen in Stop mode */
#define OB_IWDG_STOP_RUN        FLASH_OPTR_IWDG_STOP    /*!< Independent watchdog counter is running in Stop mode */

/**
  * @brief  FLASH_OB_USER_IWDG_STANDBY FLASH Option Bytes User IWDG Mode On Standby
  */
#define OB_IWDG_STDBY_FREEZE    0x00000000U             /*!< Independent watchdog counter is frozen in Standby mode */
#define OB_IWDG_STDBY_RUN         FLASH_OPTR_IWDG_STDBY /*!< Independent watchdog counter is running in Standby mode */

/**
  * @brief  FLASH_OB_USER_WWDG_SW FLASH Option Bytes User WWDG Type
  */
#define OB_WWDG_HW              0x00000000U             /*!< Hardware window watchdog */
#define OB_WWDG_SW              FLASH_OPTR_WWDG_SW      /*!< Software window watchdog */

#if defined (FLASH_OPTR_DBANK)
/**
  * @brief  FLASH_OB_USER_BFB2 FLASH Option Bytes User BFB2 Mode
  */
#define OB_BFB2_DISABLE         0x00000000U             /*!< Dual-bank boot disable */
#define OB_BFB2_ENABLE          FLASH_OPTR_BFB2         /*!< Dual-bank boot enable */

/**
  * @brief  FLASH_OB_USER_DBANK FLASH Option Bytes User DBANK Type
  */
#define OB_DBANK_128_BITS       0x00000000U             /*!< Single-bank with 128-bits data */
#define OB_DBANK_64_BITS        FLASH_OPTR_DBANK        /*!< Dual-bank with 64-bits data */
#endif

#if defined (FLASH_OPTR_PB4_PUPEN)
/**
  * @brief  FLASH_OB_USER_PB4_PUPEN FLASH Option Bytes User PB4 PUPEN bit
  */
#define OB_PB4_PUPEN_DISABLE    0x00000000U             /*!< USB power delivery dead-battery enabled/ TDI pull-up deactivated */
#define OB_PB4_PUPEN_ENABLE     FLASH_OPTR_PB4_PUPEN    /*!< USB power delivery dead-battery disabled/ TDI pull-up activated */
#endif

/**
  * @brief  FLASH_OB_USER_nBOOT1 FLASH Option Bytes User BOOT1 Type
  */
#define OB_BOOT1_SRAM           0x00000000U             /*!< Embedded SRAM1 is selected as boot space (if BOOT0=1) */
#define OB_BOOT1_SYSTEM         FLASH_OPTR_nBOOT1       /*!< System memory is selected as boot space (if BOOT0=1) */

/**
  * @brief  FLASH_OB_USER_SRAM_PE FLASH Option Bytes User SRAM Parity Check Type
  */
#define OB_SRAM_PARITY_ENABLE   0x00000000U              /*!< SRAM parity check enable (first 32kB of SRAM1 + CCM SRAM) */
#define OB_SRAM_PARITY_DISABLE  FLASH_OPTR_SRAM_PE       /*!< SRAM parity check disable (first 32kB of SRAM1 + CCM SRAM) */

/**
  * @brief  FLASH_OB_USER_CCMSRAM_RST FLASH Option Bytes User CCMSRAM Erase On Reset Type
  */
#define OB_CCMSRAM_RST_ERASE        0x00000000U             /*!< CCMSRAM erased when a system reset occurs */
#define OB_CCMSRAM_RST_NOT_ERASE    FLASH_OPTR_CCMSRAM_RST  /*!< CCMSRAM is not erased when a system reset occurs */

/**
  * @brief  FLASH_OB_USER_nSWBOOT0 FLASH Option Bytes User Software BOOT0
  */
#define OB_BOOT0_FROM_OB        0x00000000U             /*!< BOOT0 taken from the option bit nBOOT0 */
#define OB_BOOT0_FROM_PIN       FLASH_OPTR_nSWBOOT0     /*!< BOOT0 taken from PB8/BOOT0 pin */

/**
  * @brief  FLASH_OB_USER_nBOOT0 FLASH Option Bytes User nBOOT0 option bit
  */
#define OB_nBOOT0_RESET         0x00000000U             /*!< nBOOT0 = 0 */
#define OB_nBOOT0_SET           FLASH_OPTR_nBOOT0       /*!< nBOOT0 = 1 */

/**
  * @brief  FLASH_OB_USER_NRST_MODE FLASH Option Bytes User NRST mode bit
  */
#define OB_NRST_MODE_INPUT_ONLY     FLASH_OPTR_NRST_MODE_0  /*!< Reset pin is in Reset input mode only */
#define OB_NRST_MODE_GPIO           FLASH_OPTR_NRST_MODE_1  /*!< Reset pin is in GPIO mode only */
#define OB_NRST_MODE_INPUT_OUTPUT   FLASH_OPTR_NRST_MODE    /*!< Reset pin is in reset input and output mode */

/**
  * @brief  FLASH_OB_USER_INTERNAL_RESET_HOLDER FLASH Option Bytes User internal reset holder bit
  */
#define OB_IRH_DISABLE          0x00000000U             /*!< Internal Reset holder disable */
#define OB_IRH_ENABLE           FLASH_OPTR_IRHEN        /*!< Internal Reset holder enable */

/**
  * @brief  FLASH_OB_PCROP_RDP FLASH Option Bytes PCROP On RDP Level Type
  */
#define OB_PCROP_RDP_NOT_ERASE  0x00000000U                 /*!< PCROP area is not erased when the RDP level 
                                                                is decreased from Level 1 to Level 0 */
#define OB_PCROP_RDP_ERASE      FLASH_PCROP1ER_PCROP_RDP    /*!< PCROP area is erased when the RDP level is 
                                                                decreased from Level 1 to Level 0 (full mass erase) */
/**
  * @brief  FLASH_Latency FLASH Latency
  */
#define FLASH_LATENCY_0         FLASH_ACR_LATENCY_0WS   /*!< FLASH Zero wait state */
#define FLASH_LATENCY_1         FLASH_ACR_LATENCY_1WS   /*!< FLASH One wait state */
#define FLASH_LATENCY_2         FLASH_ACR_LATENCY_2WS   /*!< FLASH Two wait states */
#define FLASH_LATENCY_3         FLASH_ACR_LATENCY_3WS   /*!< FLASH Three wait states */
#define FLASH_LATENCY_4         FLASH_ACR_LATENCY_4WS   /*!< FLASH Four wait states */
#define FLASH_LATENCY_5         FLASH_ACR_LATENCY_5WS   /*!< FLASH Five wait state */
#define FLASH_LATENCY_6         FLASH_ACR_LATENCY_6WS   /*!< FLASH Six wait state */
#define FLASH_LATENCY_7         FLASH_ACR_LATENCY_7WS   /*!< FLASH Seven wait states */
#define FLASH_LATENCY_8         FLASH_ACR_LATENCY_8WS   /*!< FLASH Eight wait states */
#define FLASH_LATENCY_9         FLASH_ACR_LATENCY_9WS   /*!< FLASH Nine wait states */
#define FLASH_LATENCY_10        FLASH_ACR_LATENCY_10WS  /*!< FLASH Ten wait state */
#define FLASH_LATENCY_11        FLASH_ACR_LATENCY_11WS  /*!< FLASH Eleven wait state */
#define FLASH_LATENCY_12        FLASH_ACR_LATENCY_12WS  /*!< FLASH Twelve wait states */
#define FLASH_LATENCY_13        FLASH_ACR_LATENCY_13WS  /*!< FLASH Thirteen wait states */
#define FLASH_LATENCY_14        FLASH_ACR_LATENCY_14WS  /*!< FLASH Fourteen wait states */
#define FLASH_LATENCY_15        FLASH_ACR_LATENCY_15WS  /*!< FLASH Fifteen wait states */

/**
  * @brief  FLASH_Keys FLASH Keys
  */
#define FLASH_KEY1              0x45670123U     /*!< Flash key1 */
#define FLASH_KEY2              0xCDEF89ABU     /*!< Flash key2: used with FLASH_KEY1 
                                                    to unlock the FLASH registers access */

#define FLASH_PDKEY1            0x04152637U     /*!< Flash power down key1 */
#define FLASH_PDKEY2            0xFAFBFCFDU     /*!< Flash power down key2: used with FLASH_PDKEY1 
                                                    to unlock the RUN_PD bit in FLASH_ACR */

#define FLASH_OPTKEY1           0x08192A3BU     /*!< Flash option byte key1 */
#define FLASH_OPTKEY2           0x4C5D6E7FU     /*!< Flash option byte key2: used with FLASH_OPTKEY1 
                                                    to allow option bytes operations */
/**
  * @brief  FLASH_Flags FLASH Flags Definition
  */
#define FLASH_FLAG_EOP          FLASH_SR_EOP        /*!< FLASH End of operation flag */
#define FLASH_FLAG_OPERR        FLASH_SR_OPERR      /*!< FLASH Operation error flag */
#define FLASH_FLAG_PROGERR      FLASH_SR_PROGERR    /*!< FLASH Programming error flag */
#define FLASH_FLAG_WRPERR       FLASH_SR_WRPERR     /*!< FLASH Write protection error flag */
#define FLASH_FLAG_PGAERR       FLASH_SR_PGAERR     /*!< FLASH Programming alignment error flag */
#define FLASH_FLAG_SIZERR       FLASH_SR_SIZERR     /*!< FLASH Size error flag  */
#define FLASH_FLAG_PGSERR       FLASH_SR_PGSERR     /*!< FLASH Programming sequence error flag */
#define FLASH_FLAG_MISERR       FLASH_SR_MISERR     /*!< FLASH Fast programming data miss error flag */
#define FLASH_FLAG_FASTERR      FLASH_SR_FASTERR    /*!< FLASH Fast programming error flag */
#define FLASH_FLAG_RDERR        FLASH_SR_RDERR      /*!< FLASH PCROP read error flag */
#define FLASH_FLAG_OPTVERR      FLASH_SR_OPTVERR    /*!< FLASH Option validity error flag  */
#define FLASH_FLAG_BSY          FLASH_SR_BSY        /*!< FLASH Busy flag */
#define FLASH_FLAG_ECCC         FLASH_ECCR_ECCC     /*!< FLASH ECC correction in 64 LSB bits */
#define FLASH_FLAG_ECCD         FLASH_ECCR_ECCD     /*!< FLASH ECC detection in 64 LSB bits */
#if defined (FLASH_OPTR_DBANK)
#define FLASH_FLAG_ECCC2        FLASH_ECCR_ECCC2    /*!< FLASH ECC correction in 64 MSB bits (mode 128 bits only) */
#define FLASH_FLAG_ECCD2        FLASH_ECCR_ECCD2    /*!< FLASH ECC detection in 64 MSB bits (mode 128 bits only) */
#endif

#define FLASH_FLAG_SR_ERRORS    (FLASH_FLAG_OPERR | FLASH_FLAG_PROGERR | FLASH_FLAG_WRPERR | \
                                    FLASH_FLAG_PGAERR | FLASH_FLAG_SIZERR | FLASH_FLAG_PGSERR | \
                                    FLASH_FLAG_MISERR | FLASH_FLAG_FASTERR | FLASH_FLAG_RDERR | \
                                    FLASH_FLAG_OPTVERR)
#if defined (FLASH_OPTR_DBANK)
#define FLASH_FLAG_ECCR_ERRORS  (FLASH_FLAG_ECCC | FLASH_FLAG_ECCD | FLASH_FLAG_ECCC2 | FLASH_FLAG_ECCD2)
#else
#define FLASH_FLAG_ECCR_ERRORS  (FLASH_FLAG_ECCC | FLASH_FLAG_ECCD)
#endif
#define FLASH_FLAG_ALL_ERRORS   (FLASH_FLAG_SR_ERRORS | FLASH_FLAG_ECCR_ERRORS)

/**
  * @brief  FLASH_Interrupt_definition FLASH Interrupts Definition
  */
#define FLASH_IT_EOP            FLASH_CR_EOPIE              /*!< End of FLASH Operation Interrupt source */
#define FLASH_IT_OPERR          FLASH_CR_ERRIE              /*!< Error Interrupt source */
#define FLASH_IT_RDERR          FLASH_CR_RDERRIE            /*!< PCROP Read Error Interrupt source*/
#define FLASH_IT_ECCC           (FLASH_ECCR_ECCIE >> 24U)   /*!< ECC Correction Interrupt source */

/* Exported types ------------------------------------------------------------*/
/**
  * @brief  FLASH Status structures definition
  */
typedef enum
{
    FLASH_OK       = 0x00U,
    FLASH_ERROR    = 0x01U,
    FLASH_BUSY     = 0x02U,
    FLASH_TIMEOUT  = 0x03U
} FLASH_StatusTypeDef;

/**
  * @brief  FLASH Lock structures definition
  */
typedef enum
{
    UNLOCKED = 0x00U,
    LOCKED   = 0x01U
} FLASH_LockTypeDef;

/**
  * @brief  FLASH Erase structure definition
  */
typedef struct
{
    uint32_t TypeErase;   /*!< Mass erase or page erase.
                             This parameter can be a value of @ref FLASH_Type_Erase */
    uint32_t Banks;       /*!< Select bank to erase.
                             This parameter must be a value of @ref FLASH_Banks
                             (FLASH_BANK_BOTH should be used only for mass erase) */
    uint32_t Page;        /*!< Initial Flash page to erase when page erase is disabled.
                             This parameter must be a value between 0 and (max number of pages in the bank - 1)
                             (eg : 127 for 512KB dual bank) */
    uint32_t NbPages;     /*!< Number of pages to be erased.
                             This parameter must be a value between 1 and (max number of pages in the bank - value of initial page)*/
} FLASH_EraseInitTypeDef;

/**
  * @brief  FLASH Option Bytes Program structure definition
  */
typedef struct
{
    uint32_t OptionType;     /*!< Option byte to be configured.
                                This parameter can be a combination of the values of @ref FLASH_OB_Type */
    uint32_t WRPArea;        /*!< Write protection area to be programmed (used for OPTIONBYTE_WRP).
                                Only one WRP area could be programmed at the same time.
                                This parameter can be value of @ref FLASH_OB_WRP_Area */
    uint32_t WRPStartOffset; /*!< Write protection start offset (used for OPTIONBYTE_WRP).
                                This parameter must be a value between 0 and (max number of pages in the bank - 1) */
    uint32_t WRPEndOffset;   /*!< Write protection end offset (used for OPTIONBYTE_WRP).
                                This parameter must be a value between WRPStartOffset and (max number of pages in the bank - 1) */
    uint32_t RDPLevel;       /*!< Set the read protection level.. (used for OPTIONBYTE_RDP).
                                This parameter can be a value of @ref FLASH_OB_Read_Protection */
    uint32_t USERType;       /*!< User option byte(s) to be configured (used for OPTIONBYTE_USER).
                                This parameter can be a combination of @ref FLASH_OB_USER_Type */
    uint32_t USERConfig;     /*!< Value of the user option byte (used for OPTIONBYTE_USER).
                                This parameter can be a combination of @ref FLASH_OB_USER_BOR_LEVEL,
                                @ref FLASH_OB_USER_nRST_STOP, @ref FLASH_OB_USER_nRST_STANDBY,
                                @ref FLASH_OB_USER_nRST_SHUTDOWN, @ref FLASH_OB_USER_IWDG_SW,
                                @ref FLASH_OB_USER_IWDG_STOP, @ref FLASH_OB_USER_IWDG_STANDBY,
                                @ref FLASH_OB_USER_WWDG_SW, @ref FLASH_OB_USER_BFB2 (*),
                                @ref FLASH_OB_USER_nBOOT1, @ref FLASH_OB_USER_SRAM_PE,
                                @ref FLASH_OB_USER_CCMSRAM_RST
                                @note (*) availability depends on devices */
    uint32_t PCROPConfig;    /*!< Configuration of the PCROP (used for OPTIONBYTE_PCROP).
                                This parameter must be a combination of @ref FLASH_Banks (except FLASH_BANK_BOTH)
                                and @ref FLASH_OB_PCROP_RDP */
    uint32_t PCROPStartAddr; /*!< PCROP Start address (used for OPTIONBYTE_PCROP).
                                This parameter must be a value between begin and end of bank
                                => Be careful of the bank swapping for the address */
    uint32_t PCROPEndAddr;   /*!< PCROP End address (used for OPTIONBYTE_PCROP).
                                This parameter must be a value between PCROP Start address and end of bank */
    uint32_t BootEntryPoint; /*!< Set the Boot Lock (used for OPTIONBYTE_BOOT_LOCK).
                                This parameter can be a value of @ref FLASH_OB_Boot_Lock */
    uint32_t SecBank;        /*!< Bank of securable memory area to be programmed (used for OPTIONBYTE_SEC).
                                Only one securable memory area could be programmed at the same time.
                                This parameter can be one of the following values:
                                FLASH_BANK_1: Securable memory area to be programmed in bank 1
                                FLASH_BANK_2: Securable memory area to be programmed in bank 2 (*)
                                @note (*) availability depends on devices */
    uint32_t SecSize;        /*!< Size of securable memory area to be programmed (used for OPTIONBYTE_SEC),
                                in number of pages. Securable memory area is starting from first page of the bank.
                                Only one securable memory could be programmed at the same time.
                                This parameter must be a value between 0 and (max number of pages in the bank - 1) */
} FLASH_OBProgramInitTypeDef;

/**
  * @brief  FLASH Procedure structure definition
  */
typedef enum
{
    FLASH_PROC_NONE = 0,
    FLASH_PROC_PAGE_ERASE,
    FLASH_PROC_MASS_ERASE,
    FLASH_PROC_PROGRAM,
    FLASH_PROC_PROGRAM_LAST
} FLASH_ProcedureTypeDef;

/**
  * @brief  FLASH Cache structure definition
  */
typedef enum
{
    FLASH_CACHE_DISABLED = 0,
    FLASH_CACHE_ICACHE_ENABLED,
    FLASH_CACHE_DCACHE_ENABLED,
    FLASH_CACHE_ICACHE_DCACHE_ENABLED
} FLASH_CacheTypeDef;

/**
  * @brief  FLASH handle Structure definition
  */
typedef struct
{
    FLASH_LockTypeDef Lock;                       /* FLASH locking object */
    __IO uint32_t ErrorCode;                      /* FLASH error code */
    __IO FLASH_ProcedureTypeDef ProcedureOnGoing; /* Internal variable to indicate which procedure is ongoing or not in IT context */
    __IO uint32_t Address;                        /* Internal variable to save address selected for program in IT context */
    __IO uint32_t Bank;                           /* Internal variable to save current bank selected during erase in IT context */
    __IO uint32_t Page;                           /* Internal variable to define the current page which is erasing in IT context */
    __IO uint32_t NbPagesToErase;                 /* Internal variable to save the remaining pages to erase in IT context */
    __IO FLASH_CacheTypeDef CacheToReactivate;    /* Internal variable to indicate which caches should be reactivated */
} FLASH_ProcessTypeDef;


/* Exported macro ------------------------------------------------------------*/
/**
  * @brief  Set the FLASH Latency.
  * @param  __LATENCY__ FLASH Latency.
  *         This parameter can be one of the following values :
  *           @arg FLASH_LATENCY_0:  FLASH Zero wait state
  *           @arg FLASH_LATENCY_1:  FLASH One wait state
  *           @arg FLASH_LATENCY_2:  FLASH Two wait states
  *           @arg FLASH_LATENCY_3:  FLASH Three wait states
  *           @arg FLASH_LATENCY_4:  FLASH Four wait states
  *           @arg FLASH_LATENCY_5:  FLASH Five wait states
  *           @arg FLASH_LATENCY_6:  FLASH Six wait states
  *           @arg FLASH_LATENCY_7:  FLASH Seven wait states
  *           @arg FLASH_LATENCY_8:  FLASH Eight wait states
  *           @arg FLASH_LATENCY_9:  FLASH Nine wait states
  *           @arg FLASH_LATENCY_10: FLASH Ten wait state
  *           @arg FLASH_LATENCY_11: FLASH Eleven wait state
  *           @arg FLASH_LATENCY_12: FLASH Twelve wait states
  *           @arg FLASH_LATENCY_13: FLASH Thirteen wait states
  *           @arg FLASH_LATENCY_14: FLASH Fourteen wait states
  *           @arg FLASH_LATENCY_15: FLASH Fifteen wait states
  * @retval None
  */
#define FLASH_SET_LATENCY(__LATENCY__)      MODIFY_REG(FLASH->ACR, FLASH_ACR_LATENCY, (__LATENCY__))

/**
  * @brief  Get the FLASH Latency.
  * @retval FLASH_Latency.
  *         This parameter can be one of the following values :
  *           @arg FLASH_LATENCY_0:  FLASH Zero wait state
  *           @arg FLASH_LATENCY_1:  FLASH One wait state
  *           @arg FLASH_LATENCY_2:  FLASH Two wait states
  *           @arg FLASH_LATENCY_3:  FLASH Three wait states
  *           @arg FLASH_LATENCY_4:  FLASH Four wait states
  *           @arg FLASH_LATENCY_5:  FLASH Five wait states
  *           @arg FLASH_LATENCY_6:  FLASH Six wait states
  *           @arg FLASH_LATENCY_7:  FLASH Seven wait states
  *           @arg FLASH_LATENCY_8:  FLASH Eight wait states
  *           @arg FLASH_LATENCY_9:  FLASH Nine wait states
  *           @arg FLASH_LATENCY_10: FLASH Ten wait state
  *           @arg FLASH_LATENCY_11: FLASH Eleven wait state
  *           @arg FLASH_LATENCY_12: FLASH Twelve wait states
  *           @arg FLASH_LATENCY_13: FLASH Thirteen wait states
  *           @arg FLASH_LATENCY_14: FLASH Fourteen wait states
  *           @arg FLASH_LATENCY_15: FLASH Fifteen wait states
  */
#define FLASH_GET_LATENCY()                 READ_BIT(FLASH->ACR, FLASH_ACR_LATENCY)

/**
  * @brief  Enable the FLASH prefetch buffer.
  * @retval None
  */
#define FLASH_PREFETCH_BUFFER_ENABLE()      SET_BIT(FLASH->ACR, FLASH_ACR_PRFTEN)

/**
  * @brief  Disable the FLASH prefetch buffer.
  * @retval None
  */
#define FLASH_PREFETCH_BUFFER_DISABLE()     CLEAR_BIT(FLASH->ACR, FLASH_ACR_PRFTEN)

/**
  * @brief  Enable the FLASH instruction cache.
  * @retval none
  */
#define FLASH_INSTRUCTION_CACHE_ENABLE()    SET_BIT(FLASH->ACR, FLASH_ACR_ICEN)

/**
  * @brief  Disable the FLASH instruction cache.
  * @retval none
  */
#define FLASH_INSTRUCTION_CACHE_DISABLE()   CLEAR_BIT(FLASH->ACR, FLASH_ACR_ICEN)

/**
  * @brief  Enable the FLASH data cache.
  * @retval none
  */
#define FLASH_DATA_CACHE_ENABLE()           SET_BIT(FLASH->ACR, FLASH_ACR_DCEN)

/**
  * @brief  Disable the FLASH data cache.
  * @retval none
  */
#define FLASH_DATA_CACHE_DISABLE()          CLEAR_BIT(FLASH->ACR, FLASH_ACR_DCEN)

/**
  * @brief  Reset the FLASH instruction Cache.
  * @note   This function must be used only when the Instruction Cache is disabled.
  * @retval None
  */
#define FLASH_INSTRUCTION_CACHE_RESET()     do { \
                                                SET_BIT(FLASH->ACR, FLASH_ACR_ICRST);   \
                                                CLEAR_BIT(FLASH->ACR, FLASH_ACR_ICRST); \
                                            } while (0)

/**
  * @brief  Reset the FLASH data Cache.
  * @note   This function must be used only when the data Cache is disabled.
  * @retval None
  */
#define FLASH_DATA_CACHE_RESET()            do { \
                                                SET_BIT(FLASH->ACR, FLASH_ACR_DCRST);   \
                                                CLEAR_BIT(FLASH->ACR, FLASH_ACR_DCRST); \
                                            } while (0)

/**
  * @brief  Enable the FLASH power down during Low-power run mode.
  * @note   Writing this bit to 1, automatically the keys are
  *         lost and a new unlock sequence is necessary to re-write it to 0.
  */
#define FLASH_POWER_DOWN_ENABLE()           do { \
                                                WRITE_REG(FLASH->PDKEYR, FLASH_PDKEY1); \
                                                WRITE_REG(FLASH->PDKEYR, FLASH_PDKEY2); \
                                                SET_BIT(FLASH->ACR, FLASH_ACR_RUN_PD);   \
                                            } while (0)

/**
  * @brief  Disable the FLASH power down during Low-power run mode.
  * @note   Writing this bit to 0, automatically the keys are
  *         lost and a new unlock sequence is necessary to re-write it to 1.
  */
#define FLASH_POWER_DOWN_DISABLE()          do { \
                                                WRITE_REG(FLASH->PDKEYR, FLASH_PDKEY1); \
                                                WRITE_REG(FLASH->PDKEYR, FLASH_PDKEY2); \
                                                CLEAR_BIT(FLASH->ACR, FLASH_ACR_RUN_PD); \
                                            } while (0)

/**
  * @brief  Enable the FLASH power down during Low-Power sleep mode
  * @retval none
  */
#define FLASH_SLEEP_POWERDOWN_ENABLE()      SET_BIT(FLASH->ACR, FLASH_ACR_SLEEP_PD)

/**
  * @brief  Disable the FLASH power down during Low-Power sleep mode
  * @retval none
  */
#define FLASH_SLEEP_POWERDOWN_DISABLE()     CLEAR_BIT(FLASH->ACR, FLASH_ACR_SLEEP_PD)

/**
  * @brief  Enable the specified FLASH interrupt.
  * @param  __INTERRUPT__ FLASH interrupt
  *         This parameter can be any combination of the following values:
  *     @arg FLASH_IT_EOP: End of FLASH Operation Interrupt
  *     @arg FLASH_IT_OPERR: Error Interrupt
  *     @arg FLASH_IT_RDERR: PCROP Read Error Interrupt
  *     @arg FLASH_IT_ECCC: ECC Correction Interrupt
  * @retval none
  */
#define FLASH_ENABLE_IT(__INTERRUPT__)      do { \
                                                if (((__INTERRUPT__) & FLASH_IT_ECCC) != 0U) \
                                                { \
                                                    SET_BIT(FLASH->ECCR, FLASH_ECCR_ECCIE); \
                                                } \
                                                if (((__INTERRUPT__) & (~FLASH_IT_ECCC)) != 0U) \
                                                { \
                                                    SET_BIT(FLASH->CR, ((__INTERRUPT__) & (~FLASH_IT_ECCC))); \
                                                } \
                                            } while (0)

/**
  * @brief  Disable the specified FLASH interrupt.
  * @param  __INTERRUPT__ FLASH interrupt
  *         This parameter can be any combination of the following values:
  *     @arg FLASH_IT_EOP: End of FLASH Operation Interrupt
  *     @arg FLASH_IT_OPERR: Error Interrupt
  *     @arg FLASH_IT_RDERR: PCROP Read Error Interrupt
  *     @arg FLASH_IT_ECCC: ECC Correction Interrupt
  * @retval none
  */
#define FLASH_DISABLE_IT(__INTERRUPT__)     do { \
                                                if (((__INTERRUPT__) & FLASH_IT_ECCC) != 0U) \
                                                { \
                                                    CLEAR_BIT(FLASH->ECCR, FLASH_ECCR_ECCIE); \
                                                } \
                                                if (((__INTERRUPT__) & (~FLASH_IT_ECCC)) != 0U) \
                                                { \
                                                CLEAR_BIT(FLASH->CR, ((__INTERRUPT__) & (~FLASH_IT_ECCC))); \
                                                } \
                                            } while (0)

/**
  * @brief  Check whether the specified FLASH flag is set or not.
  * @param  __FLAG__ specifies the FLASH flag to check.
  *   This parameter can be one of the following values:
  *     @arg FLASH_FLAG_EOP: FLASH End of Operation flag
  *     @arg FLASH_FLAG_OPERR: FLASH Operation error flag
  *     @arg FLASH_FLAG_PROGERR: FLASH Programming error flag
  *     @arg FLASH_FLAG_WRPERR: FLASH Write protection error flag
  *     @arg FLASH_FLAG_PGAERR: FLASH Programming alignment error flag
  *     @arg FLASH_FLAG_SIZERR: FLASH Size error flag
  *     @arg FLASH_FLAG_PGSERR: FLASH Programming sequence error flag
  *     @arg FLASH_FLAG_MISERR: FLASH Fast programming data miss error flag
  *     @arg FLASH_FLAG_FASTERR: FLASH Fast programming error flag
  *     @arg FLASH_FLAG_RDERR: FLASH PCROP read  error flag
  *     @arg FLASH_FLAG_OPTVERR: FLASH Option validity error flag
  *     @arg FLASH_FLAG_BSY: FLASH write/erase operations in progress flag
  *     @arg FLASH_FLAG_ECCC: FLASH one ECC error has been detected and corrected in 64 LSB bits
  *     @arg FLASH_FLAG_ECCD: FLASH two ECC errors have been detected in 64 LSB bits
  *     @arg FLASH_FLAG_ECCC2(*): FLASH one ECC error has been detected and corrected in 64 MSB bits (mode 128 bits only)
  *     @arg FLASH_FLAG_ECCD2(*): FLASH two ECC errors have been detected in 64 MSB bits (mode 128 bits only)
  * @note  (*) availability depends on devices
  * @retval The new state of FLASH_FLAG (SET or RESET).
  */
#define FLASH_GET_FLAG(__FLAG__)            ((((__FLAG__) & FLASH_FLAG_ECCR_ERRORS) != 0U) ? \
                                                (READ_BIT(FLASH->ECCR, (__FLAG__)) == (__FLAG__)) : \
                                                (READ_BIT(FLASH->SR, (__FLAG__)) == (__FLAG__)))

/**
  * @brief  Clear the FLASH's pending flags.
  * @param  __FLAG__ specifies the FLASH flags to clear.
  *   This parameter can be any combination of the following values:
  *     @arg FLASH_FLAG_EOP: FLASH End of Operation flag
  *     @arg FLASH_FLAG_OPERR: FLASH Operation error flag
  *     @arg FLASH_FLAG_PROGERR: FLASH Programming error flag
  *     @arg FLASH_FLAG_WRPERR: FLASH Write protection error flag
  *     @arg FLASH_FLAG_PGAERR: FLASH Programming alignment error flag
  *     @arg FLASH_FLAG_SIZERR: FLASH Size error flag
  *     @arg FLASH_FLAG_PGSERR: FLASH Programming sequence error flag
  *     @arg FLASH_FLAG_MISERR: FLASH Fast programming data miss error flag
  *     @arg FLASH_FLAG_FASTERR: FLASH Fast programming error flag
  *     @arg FLASH_FLAG_RDERR: FLASH PCROP read  error flag
  *     @arg FLASH_FLAG_OPTVERR: FLASH Option validity error flag
  *     @arg FLASH_FLAG_ECCC: FLASH one ECC error has been detected and corrected in 64 LSB bits
  *     @arg FLASH_FLAG_ECCD: FLASH two ECC errors have been detected in 64 LSB bits
  *     @arg FLASH_FLAG_ECCC2(*): FLASH one ECC error has been detected and corrected in 64 MSB bits (mode 128 bits only)
  *     @arg FLASH_FLAG_ECCD2(*): FLASH two ECC errors have been detected in 64 MSB bits (mode 128 bits only)
  *     @arg FLASH_FLAG_SR_ERRORS: FLASH All SR errors flags
  *     @arg FLASH_FLAG_ECCR_ERRORS: FLASH All ECCR errors flags
  * @note  (*) availability depends on devices
  * @retval None
  */
#define FLASH_CLEAR_FLAG(__FLAG__)          do { \
                                                if (((__FLAG__) & FLASH_FLAG_ECCR_ERRORS) != 0U) \
                                                { \
                                                    SET_BIT(FLASH->ECCR, ((__FLAG__) & FLASH_FLAG_ECCR_ERRORS)); \
                                                } \
                                                if (((__FLAG__) & ~(FLASH_FLAG_ECCR_ERRORS)) != 0U) \
                                                { \
                                                    WRITE_REG(FLASH->SR, ((__FLAG__) & ~(FLASH_FLAG_ECCR_ERRORS))); \
                                                } \
                                            } while (0)

#define FLASH_LOCK(__HANDLE__)              do { \
                                                if((__HANDLE__)->Lock == LOCKED) \
                                                { \
                                                    return FLASH_BUSY; \
                                                } \
                                                else \
                                                { \
                                                    (__HANDLE__)->Lock = LOCKED; \
                                                } \
                                            } while (0)

#define FLASH_UNLOCK(__HANDLE__)            do{ \
                                                (__HANDLE__)->Lock = UNLOCKED; \
                                            } while (0)

/**
  * @brief  __RAM_FUNC definition
  */
#if defined (__CC_ARM) || (defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))
/* ARM Compiler V4/V5 and V6
   --------------------------
   RAM functions are defined using the toolchain options.
   Functions that are executed in RAM should reside in a separate source module.
   Using the 'Options for File' dialog you can simply change the 'Code / Const'
   area of a module to a memory space in physical RAM.
   Available memory areas are declared in the 'Target' tab of the 'Options for Target'
   dialog.
*/
#define __RAM_FUNC

#elif defined ( __ICCARM__ )
/* ICCARM Compiler
   ---------------
   RAM functions are defined using a specific toolchain keyword "__ramfunc".
*/
#define __RAM_FUNC __ramfunc

#elif defined   (  __GNUC__  )
/* GNU Compiler
   ------------
  RAM functions are defined using a specific toolchain attribute
   "__attribute__((section(".RamFunc")))".
*/
#define __RAM_FUNC __attribute__((section(".RamFunc")))

#endif /* __CC_ARM */

/* Exported variables --------------------------------------------------------*/
extern FLASH_ProcessTypeDef MCU_FLASH;

/* Exported functions prototypes ---------------------------------------------*/
FLASH_StatusTypeDef FLASH_Unlock(void);
FLASH_StatusTypeDef FLASH_Lock(void);

FLASH_StatusTypeDef FLASH_OB_Unlock(void);
FLASH_StatusTypeDef FLASH_OB_Lock(void);
FLASH_StatusTypeDef FLASH_OB_Launch(void);

FLASH_StatusTypeDef FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);
FLASH_StatusTypeDef FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint64_t Data);
void FLASH_IRQHandler(void);
void FLASH_EndOfOperationCallback(uint32_t ReturnValue);
void FLASH_OperationErrorCallback(uint32_t ReturnValue);
FLASH_StatusTypeDef FLASH_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError);

FLASH_StatusTypeDef FLASH_OBProgram(FLASH_OBProgramInitTypeDef *pOBInit);
void FLASH_OBGetConfig(FLASH_OBProgramInitTypeDef *pOBInit);

__RAM_FUNC FLASH_StatusTypeDef FLASH_EnableRunPowerDown(void);
__RAM_FUNC FLASH_StatusTypeDef FLASH_DisableRunPowerDown(void);
#if defined (FLASH_OPTR_DBANK)
__RAM_FUNC FLASH_StatusTypeDef FLASH_OB_DBankConfig(uint32_t DBankConfig);
#endif

FLASH_StatusTypeDef FLASH_EnableSecMemProtection(uint32_t Bank);
void FLASH_EnableDebugger(void);
void FLASH_DisableDebugger(void);

uint32_t FLASH_GetError(void);

#ifdef __cplusplus
}
#endif

#endif /* FLASH_H__ */

/**
  ******************************************************************************
  * @file       flash.c
  * @author     Amos
  * @brief      MCU flash program body.
  ******************************************************************************
  * @attention
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "flash.h"
#ifdef  USE_FULL_ASSERT
#include "stm32_assert.h"
#else
#define assert_param(expr) ((void)0U)
#endif /* USE_FULL_ASSERT */

/* External variables declaration --------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define FLASH_SIZE_DATA_REGISTER        FLASHSIZE_BASE

#if defined (FLASH_OPTR_DBANK)
#define FLASH_SIZE                      ((((*((uint16_t *)FLASH_SIZE_DATA_REGISTER)) == 0xFFFFU)) ? (0x200UL << 10U) : \
                                        (((*((uint32_t *)FLASH_SIZE_DATA_REGISTER)) & 0xFFFFUL) << 10U))
#define FLASH_BANK_SIZE                 (FLASH_SIZE >> 1)
#define FLASH_PAGE_NB                   128U
#define FLASH_PAGE_SIZE_128_BITS        0x1000U /* 4 KB */
#else
#define FLASH_SIZE                      ((((*((uint16_t *)FLASH_SIZE_DATA_REGISTER)) == 0xFFFFU)) ? (0x80UL << 10U) : \
                                        (((*((uint32_t *)FLASH_SIZE_DATA_REGISTER)) & 0xFFFFUL) << 10U))
#define FLASH_BANK_SIZE                 (FLASH_SIZE)
#define FLASH_PAGE_NB                   ((FLASH_SIZE == 0x00080000U) ? 256U : \
                                        ((FLASH_SIZE == 0x00040000U) ? 128U : 64U))
#endif

#define FLASH_PAGE_SIZE                 0x800U  /* 2 KB */

#define FLASH_TIMEOUT_VALUE             1000U   /* 1 s  */

#define FLASH_NB_DOUBLE_WORDS_IN_ROW    32U

/* Private typedef -----------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
FLASH_StatusTypeDef  FLASH_WaitForLastOperation(uint32_t Timeout);

static FLASH_StatusTypeDef FLASH_WaitForLastOperation(uint32_t Timeout);
static void FLASH_Program_DoubleWord(uint32_t Address, uint64_t Data);
static void FLASH_Program_Fast(uint32_t Address, uint32_t DataAddress);

static void FLASH_MassErase(uint32_t Banks);
static void FLASH_PageErase(uint32_t Page, uint32_t Banks);
static void FLASH_FlushCaches(void);
static FLASH_StatusTypeDef FLASH_OB_WRPConfig(uint32_t WRPArea, uint32_t WRPStartOffset, uint32_t WRDPEndOffset);
static FLASH_StatusTypeDef FLASH_OB_RDPConfig(uint32_t RDPLevel);
static FLASH_StatusTypeDef FLASH_OB_UserConfig(uint32_t UserType, uint32_t UserConfig);
static FLASH_StatusTypeDef FLASH_OB_PCROPConfig(uint32_t PCROPConfig, uint32_t PCROPStartAddr, uint32_t PCROPEndAddr);
static void FLASH_OB_GetWRP(uint32_t WRPArea, uint32_t *WRPStartOffset, uint32_t *WRDPEndOffset);
static uint32_t FLASH_OB_GetRDP(void);
static uint32_t FLASH_OB_GetUser(void);
static void FLASH_OB_GetPCROP(uint32_t *PCROPConfig, uint32_t *PCROPStartAddr, uint32_t *PCROPEndAddr);
static FLASH_StatusTypeDef FLASH_OB_SecMemConfig(uint32_t SecMemBank, uint32_t SecMemSize);
static void FLASH_OB_GetSecMem(uint32_t SecMemBank, uint32_t *SecMemSize);
static FLASH_StatusTypeDef FLASH_OB_BootLockConfig(uint32_t BootLockConfig);
static uint32_t FLASH_OB_GetBootLock(void);

/* Private macro -------------------------------------------------------------*/
#define IS_FLASH_TYPEERASE(VALUE)           (((VALUE) == FLASH_TYPEERASE_PAGES) || \
                                            ((VALUE) == FLASH_TYPEERASE_MASSERASE))

#if defined (FLASH_OPTR_DBANK)
#define IS_FLASH_BANK(BANK)                 (((BANK) == FLASH_BANK_1) || \
                                                ((BANK) == FLASH_BANK_2) || \
                                                ((BANK) == FLASH_BANK_BOTH))

#define IS_FLASH_BANK_EXCLUSIVE(BANK)       (((BANK) == FLASH_BANK_1)  || ((BANK) == FLASH_BANK_2))
#else
#define IS_FLASH_BANK(BANK)                 ((BANK) == FLASH_BANK_1)

#define IS_FLASH_BANK_EXCLUSIVE(BANK)       ((BANK) == FLASH_BANK_1)
#endif

#define IS_FLASH_TYPEPROGRAM(VALUE)         (((VALUE) == FLASH_TYPEPROGRAM_DOUBLEWORD) || \
                                                ((VALUE) == FLASH_TYPEPROGRAM_FAST) || \
                                                ((VALUE) == FLASH_TYPEPROGRAM_FAST_AND_LAST))

#define IS_FLASH_MAIN_MEM_ADDRESS(ADDRESS)  (((ADDRESS) >= FLASH_BASE) && ((ADDRESS) < (FLASH_BASE+FLASH_SIZE)))

#define IS_FLASH_OTP_ADDRESS(ADDRESS)       (((ADDRESS) >= 0x1FFF7000U) && ((ADDRESS) <= 0x1FFF73FFU))

#define IS_FLASH_PROGRAM_ADDRESS(ADDRESS)   (IS_FLASH_MAIN_MEM_ADDRESS(ADDRESS) || IS_FLASH_OTP_ADDRESS(ADDRESS))

#define IS_FLASH_PAGE(PAGE)                 ((PAGE) < FLASH_PAGE_NB)

#define IS_OPTIONBYTE(VALUE)                (((VALUE) <= (OPTIONBYTE_WRP | OPTIONBYTE_RDP | OPTIONBYTE_USER | \
                                                OPTIONBYTE_PCROP | OPTIONBYTE_BOOT_LOCK | OPTIONBYTE_SEC)))

#if defined (FLASH_OPTR_DBANK)
#define IS_OB_WRPAREA(VALUE)                (((VALUE) == OB_WRPAREA_BANK1_AREAA) || ((VALUE) == OB_WRPAREA_BANK1_AREAB) || \
                                                ((VALUE) == OB_WRPAREA_BANK2_AREAA) || ((VALUE) == OB_WRPAREA_BANK2_AREAB))
#else
#define IS_OB_WRPAREA(VALUE)                (((VALUE) == OB_WRPAREA_BANK1_AREAA) || ((VALUE) == OB_WRPAREA_BANK1_AREAB))
#endif

#define IS_OB_BOOT_LOCK(VALUE)              (((VALUE) == OB_BOOT_LOCK_ENABLE) || ((VALUE) == OB_BOOT_LOCK_DISABLE))

#define IS_OB_RDP_LEVEL(LEVEL)              (((LEVEL) == OB_RDP_LEVEL_0) || ((LEVEL) == OB_RDP_LEVEL_1) || \
                                                ((LEVEL) == OB_RDP_LEVEL_2))

#define IS_OB_USER_TYPE(TYPE)               (((TYPE) <= 0x1FFFFU) && ((TYPE) != 0U))

#define IS_OB_USER_BOR_LEVEL(LEVEL)         (((LEVEL) == OB_BOR_LEVEL_0) || ((LEVEL) == OB_BOR_LEVEL_1) || \
                                                ((LEVEL) == OB_BOR_LEVEL_2) || ((LEVEL) == OB_BOR_LEVEL_3) || \
                                                ((LEVEL) == OB_BOR_LEVEL_4))

#define IS_OB_USER_STOP(VALUE)              (((VALUE) == OB_STOP_RST) || ((VALUE) == OB_STOP_NORST))

#define IS_OB_USER_STANDBY(VALUE)           (((VALUE) == OB_STANDBY_RST) || ((VALUE) == OB_STANDBY_NORST))

#define IS_OB_USER_SHUTDOWN(VALUE)          (((VALUE) == OB_SHUTDOWN_RST) || ((VALUE) == OB_SHUTDOWN_NORST))

#define IS_OB_USER_IWDG(VALUE)              (((VALUE) == OB_IWDG_HW) || ((VALUE) == OB_IWDG_SW))

#define IS_OB_USER_IWDG_STOP(VALUE)         (((VALUE) == OB_IWDG_STOP_FREEZE) || ((VALUE) == OB_IWDG_STOP_RUN))

#define IS_OB_USER_IWDG_STDBY(VALUE)        (((VALUE) == OB_IWDG_STDBY_FREEZE) || ((VALUE) == OB_IWDG_STDBY_RUN))

#define IS_OB_USER_WWDG(VALUE)              (((VALUE) == OB_WWDG_HW) || ((VALUE) == OB_WWDG_SW))

#if defined (FLASH_OPTR_DBANK)
#define IS_OB_USER_BFB2(VALUE)              (((VALUE) == OB_BFB2_DISABLE) || ((VALUE) == OB_BFB2_ENABLE))

#define IS_OB_USER_DBANK(VALUE)             (((VALUE) == OB_DBANK_128_BITS) || ((VALUE) == OB_DBANK_64_BITS))
#endif

#if defined (FLASH_OPTR_PB4_PUPEN)
#define IS_OB_USER_PB4_PUPEN(VALUE)         (((VALUE) == OB_PB4_PUPEN_DISABLE) || ((VALUE) == OB_PB4_PUPEN_ENABLE))
#endif

#define IS_OB_USER_BOOT1(VALUE)             (((VALUE) == OB_BOOT1_SRAM) || ((VALUE) == OB_BOOT1_SYSTEM))

#define IS_OB_USER_SRAM_PARITY(VALUE)       (((VALUE) == OB_SRAM_PARITY_ENABLE) || ((VALUE) == OB_SRAM_PARITY_DISABLE))

#define IS_OB_USER_CCMSRAM_RST(VALUE)       (((VALUE) == OB_CCMSRAM_RST_ERASE) || ((VALUE) == OB_CCMSRAM_RST_NOT_ERASE))

#define IS_OB_USER_SWBOOT0(VALUE)           (((VALUE) == OB_BOOT0_FROM_OB) || ((VALUE) == OB_BOOT0_FROM_PIN))

#define IS_OB_USER_BOOT0(VALUE)             (((VALUE) == OB_nBOOT0_RESET) || ((VALUE) == OB_nBOOT0_SET))

#define IS_OB_USER_NRST_MODE(VALUE)         (((VALUE) == OB_NRST_MODE_GPIO) || ((VALUE) == OB_NRST_MODE_INPUT_ONLY) || \
                                                ((VALUE) == OB_NRST_MODE_INPUT_OUTPUT))

#define IS_OB_USER_IRHEN(VALUE)             (((VALUE) == OB_IRH_ENABLE) || ((VALUE) == OB_IRH_DISABLE))

#define IS_OB_PCROP_RDP(VALUE)              (((VALUE) == OB_PCROP_RDP_NOT_ERASE) || ((VALUE) == OB_PCROP_RDP_ERASE))

#define IS_OB_SECMEM_SIZE(VALUE)            ((VALUE) <= FLASH_PAGE_NB)

#define IS_FLASH_LATENCY(LATENCY)           (((LATENCY) == FLASH_LATENCY_0) || ((LATENCY) == FLASH_LATENCY_1) || \
                                            ((LATENCY) == FLASH_LATENCY_2) || ((LATENCY) == FLASH_LATENCY_3) || \
                                            ((LATENCY) == FLASH_LATENCY_4) || ((LATENCY) == FLASH_LATENCY_5) || \
                                            ((LATENCY) == FLASH_LATENCY_6) || ((LATENCY) == FLASH_LATENCY_7) || \
                                            ((LATENCY) == FLASH_LATENCY_8) || ((LATENCY) == FLASH_LATENCY_9) || \
                                            ((LATENCY) == FLASH_LATENCY_10) || ((LATENCY) == FLASH_LATENCY_11) || \
                                            ((LATENCY) == FLASH_LATENCY_12) || ((LATENCY) == FLASH_LATENCY_13) || \
                                            ((LATENCY) == FLASH_LATENCY_14) || ((LATENCY) == FLASH_LATENCY_15))
/* Exported variables --------------------------------------------------------*/
/**
  * @brief  Variable used for Program/Erase sectors under interruption
  */
FLASH_ProcessTypeDef MCU_FLASH =
{
    .Lock = UNLOCKED,
    .ErrorCode = FLASH_ERROR_NONE,
    .ProcedureOnGoing = FLASH_PROC_NONE,
    .Address = 0U,
    .Bank = FLASH_BANK_1,
    .Page = 0U,
    .NbPagesToErase = 0U,
    .CacheToReactivate = FLASH_CACHE_DISABLED
};

/* Exported functions --------------------------------------------------------*/
/**
  * @brief  Unlock the FLASH control register access.
  * @retval FLASH_Status
  */
FLASH_StatusTypeDef FLASH_Unlock(void)
{
    FLASH_StatusTypeDef status = FLASH_OK;

    if (READ_BIT(FLASH->CR, FLASH_CR_LOCK) != 0U)
    {
        /* Authorize the FLASH Registers access */
        WRITE_REG(FLASH->KEYR, FLASH_KEY1);
        WRITE_REG(FLASH->KEYR, FLASH_KEY2);

        /* verify Flash is unlocked */
        if (READ_BIT(FLASH->CR, FLASH_CR_LOCK) != 0U)
        {
            status = FLASH_ERROR;
        }
    }

    return status;
}

/**
  * @brief  Lock the FLASH control register access.
  * @retval FLASH_Status
  */
FLASH_StatusTypeDef FLASH_Lock(void)
{
    FLASH_StatusTypeDef status = FLASH_ERROR;

    /* Set the LOCK Bit to lock the FLASH Registers access */
    SET_BIT(FLASH->CR, FLASH_CR_LOCK);

    /* verify Flash is locked */
    if (READ_BIT(FLASH->CR, FLASH_CR_LOCK) != 0U)
    {
        status = FLASH_OK;
    }

    return status;
}

/**
  * @brief  Unlock the FLASH Option Bytes Registers access.
  * @retval FLASH_Status
  */
FLASH_StatusTypeDef FLASH_OB_Unlock(void)
{
    FLASH_StatusTypeDef status = FLASH_OK;

    if (READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) != 0U)
    {
        /* Authorizes the Option Byte register programming */
        WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY1);
        WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY2);

        /* verify option bytes are unlocked */
        if (READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) != 0U)
        {
            status = FLASH_ERROR;
        }
    }

    return status;
}

/**
  * @brief  Lock the FLASH Option Bytes Registers access.
  * @retval FLASH_Status
  */
FLASH_StatusTypeDef FLASH_OB_Lock(void)
{
    FLASH_StatusTypeDef status = FLASH_ERROR;

    /* Set the OPTLOCK Bit to lock the FLASH Option Byte Registers access */
    SET_BIT(FLASH->CR, FLASH_CR_OPTLOCK);

    /* Verify option bytes are locked */
    if (READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) != 0U)
    {
        status = FLASH_OK;
    }

    return status;
}

/**
  * @brief  Launch the option byte loading.
  * @retval FLASH_Status
  */
FLASH_StatusTypeDef FLASH_OB_Launch(void)
{
    /* Set the bit to force the option byte reloading */
    SET_BIT(FLASH->CR, FLASH_CR_OBL_LAUNCH);

    /* Wait for last operation to be completed */
    return (FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE));
}

/**
  * @brief  Program double word or fast program of a row 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.
  *         This parameter is the data for the double word program and the address where
  *         are stored the data for the row fast program.
  *
  * @retval FLASH_Status
  */
FLASH_StatusTypeDef FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
{
    FLASH_StatusTypeDef status;
    uint32_t prog_bit = 0;

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

    /* Process Locked */
    FLASH_LOCK(&MCU_FLASH);

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

    if (status == FLASH_OK)
    {
        MCU_FLASH.ErrorCode = FLASH_ERROR_NONE;

        if (TypeProgram == FLASH_TYPEPROGRAM_DOUBLEWORD)
        {
            /* Program double-word (64-bit) at a specified address */
            FLASH_Program_DoubleWord(Address, Data);
            prog_bit = FLASH_CR_PG;
        }
        else if ((TypeProgram == FLASH_TYPEPROGRAM_FAST) || (TypeProgram == FLASH_TYPEPROGRAM_FAST_AND_LAST))
        {
            /* Fast program a 32 row double-word (64-bit) at a specified address */
            FLASH_Program_Fast(Address, (uint32_t)Data);

            /* If it is the last row, the bit will be cleared at the end of the operation */
            if (TypeProgram == FLASH_TYPEPROGRAM_FAST_AND_LAST)
            {
                prog_bit = FLASH_CR_FSTPG;
            }
        }
        else
        {
            /* Nothing to do */
        }

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

        /* If the program operation is completed, disable the PG or FSTPG Bit */
        if (prog_bit != 0U)
        {
            CLEAR_BIT(FLASH->CR, prog_bit);
        }
    }

    /* Process Unlocked */
    FLASH_UNLOCK(&MCU_FLASH);

    /* return status */
    return status;
}

/**
  * @brief  Program double word or fast program of a row 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.
  *         This parameter is the data for the double word program and the address where
  *         are stored the data for the row fast program.
  *
  * @retval FLASH_Status
  */
FLASH_StatusTypeDef FLASH_Program_IT(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
{
    FLASH_StatusTypeDef status;

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

    /* Process Locked */
    FLASH_LOCK(&MCU_FLASH);

    /* Reset error code */
    MCU_FLASH.ErrorCode = FLASH_ERROR_NONE;

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

    if (status != FLASH_OK)
    {
        /* Process Unlocked */
        FLASH_UNLOCK(&MCU_FLASH);
    }
    else
    {
        /* Set internal variables used by the IRQ handler */
        if (TypeProgram == FLASH_TYPEPROGRAM_FAST_AND_LAST)
        {
            MCU_FLASH.ProcedureOnGoing = FLASH_PROC_PROGRAM_LAST;
        }
        else
        {
            MCU_FLASH.ProcedureOnGoing = FLASH_PROC_PROGRAM;
        }
        MCU_FLASH.Address = Address;

        /* Enable End of Operation and Error interrupts */
        FLASH_ENABLE_IT(FLASH_IT_EOP | FLASH_IT_OPERR);

        if (TypeProgram == FLASH_TYPEPROGRAM_DOUBLEWORD)
        {
            /* Program double-word (64-bit) at a specified address */
            FLASH_Program_DoubleWord(Address, Data);
        }
        else if ((TypeProgram == FLASH_TYPEPROGRAM_FAST) || (TypeProgram == FLASH_TYPEPROGRAM_FAST_AND_LAST))
        {
            /* Fast program a 32 row double-word (64-bit) at a specified address */
            FLASH_Program_Fast(Address, (uint32_t)Data);
        }
        else
        {
            /* Nothing to do */
        }
    }

    return status;
}

/**
  * @brief  Handle FLASH interrupt request.
  * @retval None
  */
void FLASH_IRQHandler(void)
{
    uint32_t tmp_page;
    uint32_t error;
    FLASH_ProcedureTypeDef procedure;

    /* If the operation is completed, disable the PG, PNB, MER1, MER2 and PER Bit */
    CLEAR_BIT(FLASH->CR, (FLASH_CR_PG | FLASH_CR_MER1 | FLASH_CR_PER | FLASH_CR_PNB));
#if defined (FLASH_OPTR_DBANK)
    CLEAR_BIT(FLASH->CR, FLASH_CR_MER2);
#endif

    /* Disable the FSTPG Bit only if it is the last row programmed */
    if (MCU_FLASH.ProcedureOnGoing == FLASH_PROC_PROGRAM_LAST)
    {
        CLEAR_BIT(FLASH->CR, FLASH_CR_FSTPG);
    }

    /* Check FLASH operation error flags */
    error = (FLASH->SR & FLASH_FLAG_SR_ERRORS);

    if (error != 0U)
    {
        /* Save the error code */
        MCU_FLASH.ErrorCode |= error;

        /* Clear error programming flags */
        FLASH_CLEAR_FLAG(error);

        /* Flush the caches to be sure of the data consistency */
        FLASH_FlushCaches() ;

        /* FLASH error interrupt user callback */
        procedure = MCU_FLASH.ProcedureOnGoing;
        if (procedure == FLASH_PROC_PAGE_ERASE)
        {
            FLASH_OperationErrorCallback(MCU_FLASH.Page);
        }
        else if (procedure == FLASH_PROC_MASS_ERASE)
        {
            FLASH_OperationErrorCallback(MCU_FLASH.Bank);
        }
        else if ((procedure == FLASH_PROC_PROGRAM) ||
                 (procedure == FLASH_PROC_PROGRAM_LAST))
        {
            FLASH_OperationErrorCallback(MCU_FLASH.Address);
        }
        else
        {
            /* Nothing to do */
        }

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

    /* Check FLASH End of Operation flag  */
    if (FLASH_GET_FLAG(FLASH_FLAG_EOP))
    {
        /* Clear FLASH End of Operation pending bit */
        FLASH_CLEAR_FLAG(FLASH_FLAG_EOP);

        if (MCU_FLASH.ProcedureOnGoing == FLASH_PROC_PAGE_ERASE)
        {
            /* Nb of pages to erased can be decreased */
            MCU_FLASH.NbPagesToErase--;

            /* Check if there are still pages to erase*/
            if (MCU_FLASH.NbPagesToErase != 0U)
            {
                /* Indicate user which page has been erased*/
                FLASH_EndOfOperationCallback(MCU_FLASH.Page);

                /* Increment page number */
                MCU_FLASH.Page++;
                tmp_page = MCU_FLASH.Page;
                FLASH_PageErase(tmp_page, MCU_FLASH.Bank);
            }
            else
            {
                /* No more pages to Erase */
                /* Reset Address and stop Erase pages procedure */
                MCU_FLASH.Page = 0xFFFFFFFFU;
                MCU_FLASH.ProcedureOnGoing = FLASH_PROC_NONE;

                /* Flush the caches to be sure of the data consistency */
                FLASH_FlushCaches() ;

                /* FLASH EOP interrupt user callback */
                FLASH_EndOfOperationCallback(MCU_FLASH.Page);
            }
        }
        else
        {
            /* Flush the caches to be sure of the data consistency */
            FLASH_FlushCaches() ;

            procedure = MCU_FLASH.ProcedureOnGoing;
            if (procedure == FLASH_PROC_MASS_ERASE)
            {
                /* MassErase ended. Return the selected bank */
                /* FLASH EOP interrupt user callback */
                FLASH_EndOfOperationCallback(MCU_FLASH.Bank);
            }
            else if ((procedure == FLASH_PROC_PROGRAM) ||
                     (procedure == FLASH_PROC_PROGRAM_LAST))
            {
                /* Program ended. Return the selected address */
                /* FLASH EOP interrupt user callback */
                FLASH_EndOfOperationCallback(MCU_FLASH.Address);
            }
            else
            {
                /* Nothing to do */
            }

            /*Clear the procedure ongoing*/
            MCU_FLASH.ProcedureOnGoing = FLASH_PROC_NONE;
        }
    }

    if (MCU_FLASH.ProcedureOnGoing == FLASH_PROC_NONE)
    {
        /* Disable End of Operation and Error interrupts */
        FLASH_DISABLE_IT(FLASH_IT_EOP | FLASH_IT_OPERR);

        /* Process Unlocked */
        FLASH_UNLOCK(&MCU_FLASH);
    }
}

/**
  * @brief  FLASH end of operation interrupt callback.
  * @param  ReturnValue The value saved in this parameter depends on the ongoing procedure:
  *           @arg Mass Erase: Bank number which has been requested to erase
  *           @arg Page Erase: Page which has been erased
  *                            (if 0xFFFFFFFF, it means that all the selected pages have been erased)
  *           @arg Program: Address which was selected for data program
  * @retval None
  */
__weak void FLASH_EndOfOperationCallback(uint32_t ReturnValue)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(ReturnValue);

    /* NOTE : This function should not be modified, when the callback is needed,
              the FLASH_EndOfOperationCallback could be implemented in the user file
     */
}

/**
  * @brief  FLASH operation error interrupt callback.
  * @param  ReturnValue The value saved in this parameter depends on the ongoing procedure:
  *           @arg Mass Erase: Bank number which has been requested to erase
  *           @arg Page Erase: Page number which returned an error
  *           @arg Program: Address which was selected for data program
  * @retval None
  */
__weak void FLASH_OperationErrorCallback(uint32_t ReturnValue)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(ReturnValue);

    /* NOTE : This function should not be modified, when the callback is needed,
              the FLASH_OperationErrorCallback could be implemented in the user file
     */
}

/**
  * @brief  Perform a mass erase or erase the specified FLASH memory pages.
  * @param[in]  pEraseInit pointer to an FLASH_EraseInitTypeDef structure that
  *         contains the configuration information for the erasing.
  * @param[out]  PageError pointer to variable that contains the configuration
  *         information on faulty page in case of error (0xFFFFFFFF means that all
  *         the pages have been correctly erased).
  * @retval FLASH_Status
  */
FLASH_StatusTypeDef FLASH_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError)
{
    FLASH_StatusTypeDef status;
    uint32_t page_index;

    /* Check the parameters */
    assert_param(IS_FLASH_TYPEERASE(pEraseInit->TypeErase));

    /* Process Locked */
    FLASH_LOCK(&MCU_FLASH);

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

    if (status == FLASH_OK)
    {
        MCU_FLASH.ErrorCode = FLASH_ERROR_NONE;

        /* Deactivate the cache if they are activated to avoid data misbehavior */
        if (READ_BIT(FLASH->ACR, FLASH_ACR_ICEN) != 0U)
        {
            if (READ_BIT(FLASH->ACR, FLASH_ACR_DCEN) != 0U)
            {
                /* Disable data cache  */
                FLASH_DATA_CACHE_DISABLE();
                MCU_FLASH.CacheToReactivate = FLASH_CACHE_ICACHE_DCACHE_ENABLED;
            }
            else
            {
                MCU_FLASH.CacheToReactivate = FLASH_CACHE_ICACHE_ENABLED;
            }
        }
        else if (READ_BIT(FLASH->ACR, FLASH_ACR_DCEN) != 0U)
        {
            /* Disable data cache  */
            FLASH_DATA_CACHE_DISABLE();
            MCU_FLASH.CacheToReactivate = FLASH_CACHE_DCACHE_ENABLED;
        }
        else
        {
            MCU_FLASH.CacheToReactivate = FLASH_CACHE_DISABLED;
        }

        if (pEraseInit->TypeErase == FLASH_TYPEERASE_MASSERASE)
        {
            /* Mass erase to be done */
            FLASH_MassErase(pEraseInit->Banks);

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

#if defined (FLASH_OPTR_DBANK)
            /* If the erase operation is completed, disable the MER1 and MER2 Bits */
            CLEAR_BIT(FLASH->CR, (FLASH_CR_MER1 | FLASH_CR_MER2));
#else
            /* If the erase operation is completed, disable the MER1 Bit */
            CLEAR_BIT(FLASH->CR, (FLASH_CR_MER1));
#endif
        }
        else
        {
            /*Initialization of PageError variable*/
            *PageError = 0xFFFFFFFFU;

            for (page_index = pEraseInit->Page; page_index < (pEraseInit->Page + pEraseInit->NbPages); page_index++)
            {
                FLASH_PageErase(page_index, pEraseInit->Banks);

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

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

                if (status != FLASH_OK)
                {
                    /* In case of error, stop erase procedure and return the faulty page */
                    *PageError = page_index;
                    break;
                }
            }
        }

        /* Flush the caches to be sure of the data consistency */
        FLASH_FlushCaches();
    }

    /* Process Unlocked */
    FLASH_UNLOCK(&MCU_FLASH);

    return status;
}

/**
  * @brief  Program Option bytes.
  * @param  pOBInit pointer to an FLASH_OBInitStruct structure that
  *         contains the configuration information for the programming.
  * @note   To configure any option bytes, the option lock bit OPTLOCK must be
  *         cleared with the call of FLASH_OB_Unlock() function.
  * @note   New option bytes configuration will be taken into account in two cases:
  *         - after an option bytes launch through the call of FLASH_OB_Launch()
  *         - after a power reset (BOR reset or exit from Standby/Shutdown modes)
  * @retval FLASH_Status
  */
FLASH_StatusTypeDef FLASH_OBProgram(FLASH_OBProgramInitTypeDef *pOBInit)
{
    FLASH_StatusTypeDef status = FLASH_OK;

    /* Check the parameters */
    assert_param(IS_OPTIONBYTE(pOBInit->OptionType));

    /* Process Locked */
    FLASH_LOCK(&MCU_FLASH);

    MCU_FLASH.ErrorCode = FLASH_ERROR_NONE;

    /* Write protection configuration */
    if ((pOBInit->OptionType & OPTIONBYTE_WRP) != 0U)
    {
        /* Configure of Write protection on the selected area */
        if (FLASH_OB_WRPConfig(pOBInit->WRPArea, pOBInit->WRPStartOffset, pOBInit->WRPEndOffset) != FLASH_OK)
        {
            status = FLASH_ERROR;
        }
    }

    /* Read protection configuration */
    if ((pOBInit->OptionType & OPTIONBYTE_RDP) != 0U)
    {
        /* Configure the Read protection level */
        if (FLASH_OB_RDPConfig(pOBInit->RDPLevel) != FLASH_OK)
        {
            status = FLASH_ERROR;
        }
    }

    /* User Configuration */
    if ((pOBInit->OptionType & OPTIONBYTE_USER) != 0U)
    {
        /* Configure the user option bytes */
        if (FLASH_OB_UserConfig(pOBInit->USERType, pOBInit->USERConfig) != FLASH_OK)
        {
            status = FLASH_ERROR;
        }
    }

    /* PCROP Configuration */
    if ((pOBInit->OptionType & OPTIONBYTE_PCROP) != 0U)
    {
        if (pOBInit->PCROPStartAddr != pOBInit->PCROPEndAddr)
        {
            /* Configure the Proprietary code readout protection */
            if (FLASH_OB_PCROPConfig(pOBInit->PCROPConfig, pOBInit->PCROPStartAddr, pOBInit->PCROPEndAddr) != FLASH_OK)
            {
                status = FLASH_ERROR;
            }
        }
    }

    /* Securable memory Configuration */
    if ((pOBInit->OptionType & OPTIONBYTE_SEC) != 0U)
    {
        /* Configure the securable memory area */
        if (FLASH_OB_SecMemConfig(pOBInit->SecBank, pOBInit->SecSize) != FLASH_OK)
        {
            status = FLASH_ERROR;
        }
    }

    /* Boot Entry Point Configuration */
    if ((pOBInit->OptionType & OPTIONBYTE_BOOT_LOCK) != 0U)
    {
        /* Configure the boot unique entry point option */
        if (FLASH_OB_BootLockConfig(pOBInit->BootEntryPoint) != FLASH_OK)
        {
            status = FLASH_ERROR;
        }
    }

    /* Process Unlocked */
    FLASH_UNLOCK(&MCU_FLASH);

    return status;
}

/**
  * @brief  Get the Option bytes configuration.
  * @param  pOBInit pointer to an FLASH_OBInitStruct structure that contains the
  *         configuration information.
  * @note   The fields pOBInit->WRPArea and pOBInit->PCROPConfig should indicate
  *         which area is requested for the WRP and PCROP, else no information will be returned.
  * @retval None
  */
void FLASH_OBGetConfig(FLASH_OBProgramInitTypeDef *pOBInit)
{
    pOBInit->OptionType = (OPTIONBYTE_RDP | OPTIONBYTE_USER);

#if defined (FLASH_OPTR_DBANK)
    if ((pOBInit->WRPArea == OB_WRPAREA_BANK1_AREAA) || (pOBInit->WRPArea == OB_WRPAREA_BANK1_AREAB) ||
            (pOBInit->WRPArea == OB_WRPAREA_BANK2_AREAA) || (pOBInit->WRPArea == OB_WRPAREA_BANK2_AREAB))
#else
    if ((pOBInit->WRPArea == OB_WRPAREA_BANK1_AREAA) || (pOBInit->WRPArea == OB_WRPAREA_BANK1_AREAB))
#endif
    {
        pOBInit->OptionType |= OPTIONBYTE_WRP;
        /* Get write protection on the selected area */
        FLASH_OB_GetWRP(pOBInit->WRPArea, &(pOBInit->WRPStartOffset), &(pOBInit->WRPEndOffset));
    }

    /* Get Read protection level */
    pOBInit->RDPLevel = FLASH_OB_GetRDP();

    /* Get the user option bytes */
    pOBInit->USERConfig = FLASH_OB_GetUser();

#if defined (FLASH_OPTR_DBANK)
    if ((pOBInit->PCROPConfig == FLASH_BANK_1) || (pOBInit->PCROPConfig == FLASH_BANK_2))
#else
    if (pOBInit->PCROPConfig == FLASH_BANK_1)
#endif
    {
        pOBInit->OptionType |= OPTIONBYTE_PCROP;
        /* Get the Proprietary code readout protection */
        FLASH_OB_GetPCROP(&(pOBInit->PCROPConfig), &(pOBInit->PCROPStartAddr), &(pOBInit->PCROPEndAddr));
    }

    pOBInit->OptionType |= OPTIONBYTE_BOOT_LOCK;

    /* Get the boot entry point */
    pOBInit->BootEntryPoint = FLASH_OB_GetBootLock();

    /* Get the securable memory area configuration */
#if defined (FLASH_OPTR_DBANK)
    if ((pOBInit->SecBank == FLASH_BANK_1) || (pOBInit->SecBank == FLASH_BANK_2))
#else
    if (pOBInit->SecBank == FLASH_BANK_1)
#endif
    {
        pOBInit->OptionType |= OPTIONBYTE_SEC;
        FLASH_OB_GetSecMem(pOBInit->SecBank, &(pOBInit->SecSize));
    }
}

/**
  * @brief  Enable the Power down in Run Mode
  * @note   This function should be called and executed from SRAM memory.
  * @retval None
  */
__RAM_FUNC FLASH_StatusTypeDef FLASH_EnableRunPowerDown(void)
{
    /* Enable the Power Down in Run mode*/
    FLASH_POWER_DOWN_ENABLE();

    return FLASH_OK;
}

/**
  * @brief  Disable the Power down in Run Mode
  * @note   This function should be called and executed from SRAM memory.
  * @retval None
  */
__RAM_FUNC FLASH_StatusTypeDef FLASH_DisableRunPowerDown(void)
{
    /* Disable the Power Down in Run mode*/
    FLASH_POWER_DOWN_DISABLE();

    return FLASH_OK;
}

#if defined (FLASH_OPTR_DBANK)
/**
  * @brief  Program the FLASH DBANK User Option Byte.
  *
  * @note   To configure the user option bytes, the option lock bit OPTLOCK must
  *         be cleared with the call of the FLASH_OB_Unlock() function.
  * @note   To modify the DBANK option byte, no PCROP region should be defined.
  *         To deactivate PCROP, user should perform RDP changing.
  *
  * @param  DBankConfig The FLASH DBANK User Option Byte value.
  *         This parameter  can be one of the following values:
  *            @arg OB_DBANK_128_BITS: Single-bank with 128-bits data
  *            @arg OB_DBANK_64_BITS: Dual-bank with 64-bits data
  *
  * @retval FLASH_Status
  */
__RAM_FUNC FLASH_StatusTypeDef FLASH_OB_DBankConfig(uint32_t DBankConfig)
{
    uint32_t count, reg;
    FLASH_StatusTypeDef status = FLASH_ERROR;

    /* Process Locked */
    FLASH_LOCK(&MCU_FLASH);

    /* Check if the PCROP is disabled */
    reg = FLASH->PCROP1SR;
    if (reg > FLASH->PCROP1ER)
    {
        reg = FLASH->PCROP2SR;
        if (reg > FLASH->PCROP2ER)
        {
            /* Disable Flash prefetch */
            FLASH_PREFETCH_BUFFER_DISABLE();

            if (READ_BIT(FLASH->ACR, FLASH_ACR_ICEN) != 0U)
            {
                /* Disable Flash instruction cache */
                FLASH_INSTRUCTION_CACHE_DISABLE();

                /* Flush Flash instruction cache */
                FLASH_INSTRUCTION_CACHE_RESET();
            }

            if (READ_BIT(FLASH->ACR, FLASH_ACR_DCEN) != 0U)
            {
                /* Disable Flash data cache */
                FLASH_DATA_CACHE_DISABLE();

                /* Flush Flash data cache */
                FLASH_DATA_CACHE_RESET();
            }

            /* Disable WRP zone A of 1st bank if needed */
            reg = FLASH->WRP1AR;
            if (((reg & FLASH_WRP1AR_WRP1A_STRT) >> FLASH_WRP1AR_WRP1A_STRT_Pos) <=
                    ((reg & FLASH_WRP1AR_WRP1A_END) >> FLASH_WRP1AR_WRP1A_END_Pos))
            {
                MODIFY_REG(FLASH->WRP1AR, (FLASH_WRP1AR_WRP1A_STRT | FLASH_WRP1AR_WRP1A_END), FLASH_WRP1AR_WRP1A_STRT);
            }

            /* Disable WRP zone B of 1st bank if needed */
            reg = FLASH->WRP1BR;
            if (((reg & FLASH_WRP1BR_WRP1B_STRT) >> FLASH_WRP1BR_WRP1B_STRT_Pos) <=
                    ((reg & FLASH_WRP1BR_WRP1B_END) >> FLASH_WRP1BR_WRP1B_END_Pos))
            {
                MODIFY_REG(FLASH->WRP1BR, (FLASH_WRP1BR_WRP1B_STRT | FLASH_WRP1BR_WRP1B_END), FLASH_WRP1BR_WRP1B_STRT);
            }

            /* Disable WRP zone A of 2nd bank if needed */
            reg = FLASH->WRP2AR;
            if (((reg & FLASH_WRP2AR_WRP2A_STRT) >> FLASH_WRP2AR_WRP2A_STRT_Pos) <=
                    ((reg & FLASH_WRP2AR_WRP2A_END) >> FLASH_WRP2AR_WRP2A_END_Pos))
            {
                MODIFY_REG(FLASH->WRP2AR, (FLASH_WRP2AR_WRP2A_STRT | FLASH_WRP2AR_WRP2A_END), FLASH_WRP2AR_WRP2A_STRT);
            }

            /* Disable WRP zone B of 2nd bank if needed */
            reg = FLASH->WRP2BR;
            if (((reg & FLASH_WRP2BR_WRP2B_STRT) >> FLASH_WRP2BR_WRP2B_STRT_Pos) <=
                    ((reg & FLASH_WRP2BR_WRP2B_END) >> FLASH_WRP2BR_WRP2B_END_Pos))
            {
                MODIFY_REG(FLASH->WRP2BR, (FLASH_WRP2BR_WRP2B_STRT | FLASH_WRP2BR_WRP2B_END), FLASH_WRP2BR_WRP2B_STRT);
            }

            /* Modify the DBANK user option byte */
            MODIFY_REG(FLASH->OPTR, FLASH_OPTR_DBANK, DBankConfig);

            /* Set OPTSTRT Bit */
            SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);

            /* Wait for last operation to be completed */
            /* 8 is the number of required instruction cycles for the below loop statement (timeout expressed in ms) */
            count = FLASH_TIMEOUT_VALUE * (SystemCoreClock / 8U / 1000U);
            do
            {
                if (count == 0U)
                {
                    break;
                }
                count--;
            }
            while (FLASH_GET_FLAG(FLASH_FLAG_BSY) != RESET);

            /* If the option byte program operation is completed, disable the OPTSTRT Bit */
            CLEAR_BIT(FLASH->CR, FLASH_CR_OPTSTRT);

            /* Set the bit to force the option byte reloading */
            SET_BIT(FLASH->CR, FLASH_CR_OBL_LAUNCH);
        }
    }

    /* Process Unlocked */
    FLASH_UNLOCK(&MCU_FLASH);

    return status;
}
#endif

/**
  * @brief  Enable the FLASH Securable Memory protection.
  * @param  Bank: Bank to be protected
  *          This parameter can be one of the following values:
  *            @arg FLASH_BANK_1: Bank1 to be protected
  *            @arg FLASH_BANK_2: Bank2 to be protected (*)
  *            @arg FLASH_BANK_BOTH: Bank1 and Bank2 to be protected (*)
  * @note   (*) availability depends on devices
  * @retval FLASH Status
  */
FLASH_StatusTypeDef FLASH_EnableSecMemProtection(uint32_t Bank)
{
#if defined (FLASH_OPTR_DBANK)
    if (READ_BIT(FLASH->OPTR, FLASH_OPTR_DBANK) != 0U)
    {
        /* Check the parameters */
        assert_param(IS_FLASH_BANK(Bank));

        /* Enable the Securable Memory Protection Bit for the bank 1 if requested */
        if ((Bank & FLASH_BANK_1) != 0U)
        {
            SET_BIT(FLASH->CR, FLASH_CR_SEC_PROT1);
        }

        /* Enable the Securable Memory Protection Bit for the bank 2 if requested */
        if ((Bank & FLASH_BANK_2) != 0U)
        {
            SET_BIT(FLASH->CR, FLASH_CR_SEC_PROT2);
        }
    }
    else
#endif
    {
        SET_BIT(FLASH->CR, FLASH_CR_SEC_PROT1);
    }

    return FLASH_OK;
}

/**
  * @brief  Enable Debugger.
  * @note   After calling this API, flash interface allow debugger intrusion.
  * @retval None
  */
void FLASH_EnableDebugger(void)
{
    FLASH->ACR |= FLASH_ACR_DBG_SWEN;
}


/**
  * @brief  Disable Debugger.
  * @note   After calling this API, Debugger is disabled: it's no more possible to
  *         break, see CPU register, etc...
  * @retval None
  */
void FLASH_DisableDebugger(void)
{
    FLASH->ACR &= ~FLASH_ACR_DBG_SWEN;
}

/**
  * @brief  Get the specific FLASH error flag.
  * @retval FLASH_ErrorCode. The returned value can be:
  *            @arg FLASH_ERROR_RD: FLASH Read Protection error flag (PCROP)
  *            @arg FLASH_ERROR_PGS: FLASH Programming Sequence error flag
  *            @arg FLASH_ERROR_PGP: FLASH Programming Parallelism error flag
  *            @arg FLASH_ERROR_PGA: FLASH Programming Alignment error flag
  *            @arg FLASH_ERROR_WRP: FLASH Write protected error flag
  *            @arg FLASH_ERROR_OPERATION: FLASH operation Error flag
  *            @arg FLASH_ERROR_NONE: No error set
  *            @arg FLASH_ERROR_OP: FLASH Operation error
  *            @arg FLASH_ERROR_PROG: FLASH Programming error
  *            @arg FLASH_ERROR_WRP: FLASH Write protection error
  *            @arg FLASH_ERROR_PGA: FLASH Programming alignment error
  *            @arg FLASH_ERROR_SIZ: FLASH Size error
  *            @arg FLASH_ERROR_PGS: FLASH Programming sequence error
  *            @arg FLASH_ERROR_MIS: FLASH Fast programming data miss error
  *            @arg FLASH_ERROR_FAST: FLASH Fast programming error
  *            @arg FLASH_ERROR_RD: FLASH PCROP read error
  *            @arg FLASH_ERROR_OPTV: FLASH Option validity error
  */
uint32_t FLASH_GetError(void)
{
    return MCU_FLASH.ErrorCode;
}

/* Private functions ---------------------------------------------------------*/
/**
  * @brief  Wait for a FLASH operation to complete.
  * @param  Timeout maximum flash operation timeout.
  * @retval FLASH_Status
  */
static FLASH_StatusTypeDef FLASH_WaitForLastOperation(uint32_t Timeout)
{
    /* Wait for the FLASH operation to complete by polling on BUSY flag to be reset.
       Even if the FLASH operation fails, the BUSY flag will be reset and an error
       flag will be set */

    uint32_t MsCnt = 0;
    uint32_t ErrFlag;

    while (FLASH_GET_FLAG(FLASH_FLAG_BSY))
    {
        if (MsCnt++ > Timeout)
        {
            return FLASH_TIMEOUT;
        }
        LL_mDelay(1);
    }

    /* Check FLASH operation error flags */
    ErrFlag = (FLASH->SR & FLASH_FLAG_SR_ERRORS);
    if (ErrFlag != 0u)
    {
        /* Save the error code */
        MCU_FLASH.ErrorCode |= ErrFlag;

        /* Clear error programming flags */
        FLASH_CLEAR_FLAG(ErrFlag);

        return FLASH_ERROR;
    }

    /* Check FLASH End of Operation flag  */
    if (FLASH_GET_FLAG(FLASH_FLAG_EOP))
    {
        /* Clear FLASH End of Operation pending bit */
        FLASH_CLEAR_FLAG(FLASH_FLAG_EOP);
    }

    /* If there is an error flag set */
    return FLASH_OK;
}

/**
  * @brief  Program double-word (64-bit) at a specified address.
  * @param  Address specifies the address to be programmed.
  * @param  Data specifies the data to be programmed.
  * @retval None
  */
static void FLASH_Program_DoubleWord(uint32_t Address, uint64_t Data)
{
    /* Check the parameters */
    assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));

    /* Set PG bit */
    SET_BIT(FLASH->CR, FLASH_CR_PG);

    /* Program first word */
    *(uint32_t *)Address = (uint32_t)Data;

    /* Barrier to ensure programming is performed in 2 steps, in right order
      (independently of compiler optimization behavior) */
    __ISB();

    /* Program second word */
    *(uint32_t *)(Address + 4U) = (uint32_t)(Data >> 32U);
}

/**
  * @brief  Fast program a row double-word (64-bit) at a specified address.
  * @param  Address specifies the address to be programmed.
  * @param  DataAddress specifies the address where the data are stored.
  * @retval None
  */
static void FLASH_Program_Fast(uint32_t Address, uint32_t DataAddress)
{
    uint8_t row_index = (2 * FLASH_NB_DOUBLE_WORDS_IN_ROW);
    uint32_t *dest_addr = (uint32_t *)Address;
    uint32_t *src_addr = (uint32_t *)DataAddress;
    uint32_t primask_bit;

    /* Check the parameters */
    assert_param(IS_FLASH_MAIN_MEM_ADDRESS(Address));

    /* Set FSTPG bit */
    SET_BIT(FLASH->CR, FLASH_CR_FSTPG);

    /* Enter critical section: Disable interrupts to avoid any interruption during the loop */
    primask_bit = __get_PRIMASK();
    __disable_irq();

    /* Program the double words of the row */
    do
    {
        *dest_addr = *src_addr;
        dest_addr++;
        src_addr++;
        row_index--;
    }
    while (row_index != 0U);

    /* Exit critical section: restore previous priority mask */
    __set_PRIMASK(primask_bit);
}

/**
  * @brief  Mass erase of FLASH memory.
  * @param  Banks Banks to be erased.
  *         This parameter can be one of the following values:
  *            @arg FLASH_BANK_1: Bank1 to be erased
  *            @arg FLASH_BANK_2: Bank2 to be erased (*)
  *            @arg FLASH_BANK_BOTH: Bank1 and Bank2 to be erased (*)
  * @note   (*) availability depends on devices
  * @retval None
  */
static void FLASH_MassErase(uint32_t Banks)
{
#if defined (FLASH_OPTR_DBANK)
    if (READ_BIT(FLASH->OPTR, FLASH_OPTR_DBANK) != 0U)
#endif
    {
        /* Check the parameters */
        assert_param(IS_FLASH_BANK(Banks));

        /* Set the Mass Erase Bit for the bank 1 if requested */
        if ((Banks & FLASH_BANK_1) != 0U)
        {
            SET_BIT(FLASH->CR, FLASH_CR_MER1);
        }

#if defined (FLASH_OPTR_DBANK)
        /* Set the Mass Erase Bit for the bank 2 if requested */
        if ((Banks & FLASH_BANK_2) != 0U)
        {
            SET_BIT(FLASH->CR, FLASH_CR_MER2);
        }
#endif
    }
#if defined (FLASH_OPTR_DBANK)
    else
    {
        SET_BIT(FLASH->CR, (FLASH_CR_MER1 | FLASH_CR_MER2));
    }
#endif

    /* Proceed to erase all sectors */
    SET_BIT(FLASH->CR, FLASH_CR_STRT);
}

/**
  * @brief  Erase the specified FLASH memory page.
  * @param  Page FLASH page to erase.
  *         This parameter must be a value between 0 and (max number of pages in the bank - 1).
  * @param  Banks Bank where the page will be erased.
  *         This parameter can be one of the following values:
  *            @arg FLASH_BANK_1: Page in bank 1 to be erased
  *            @arg FLASH_BANK_2: Page in bank 2 to be erased (*)
  * @note   (*) availability depends on devices
  * @retval None
  */
static void FLASH_PageErase(uint32_t Page, uint32_t Banks)
{
    /* Check the parameters */
    assert_param(IS_FLASH_PAGE(Page));

#if defined (FLASH_OPTR_DBANK)
    if (READ_BIT(FLASH->OPTR, FLASH_OPTR_DBANK) == 0U)
    {
        CLEAR_BIT(FLASH->CR, FLASH_CR_BKER);
    }
    else
    {
        assert_param(IS_FLASH_BANK_EXCLUSIVE(Banks));

        if ((Banks & FLASH_BANK_1) != 0U)
        {
            CLEAR_BIT(FLASH->CR, FLASH_CR_BKER);
        }
        else
        {
            SET_BIT(FLASH->CR, FLASH_CR_BKER);
        }
    }
#endif

    /* Proceed to erase the page */
    MODIFY_REG(FLASH->CR, FLASH_CR_PNB, ((Page & 0xFFU) << FLASH_CR_PNB_Pos));
    SET_BIT(FLASH->CR, FLASH_CR_PER);
    SET_BIT(FLASH->CR, FLASH_CR_STRT);
}

/**
  * @brief  Flush the instruction and data caches.
  * @retval None
  */
static void FLASH_FlushCaches(void)
{
    FLASH_CacheTypeDef cache = MCU_FLASH.CacheToReactivate;

    /* Flush instruction cache  */
    if ((cache == FLASH_CACHE_ICACHE_ENABLED) ||
            (cache == FLASH_CACHE_ICACHE_DCACHE_ENABLED))
    {
        /* Disable instruction cache */
        FLASH_INSTRUCTION_CACHE_DISABLE();
        /* Reset instruction cache */
        FLASH_INSTRUCTION_CACHE_RESET();
        /* Enable instruction cache */
        FLASH_INSTRUCTION_CACHE_ENABLE();
    }

    /* Flush data cache */
    if ((cache == FLASH_CACHE_DCACHE_ENABLED) ||
            (cache == FLASH_CACHE_ICACHE_DCACHE_ENABLED))
    {
        /* Reset data cache */
        FLASH_DATA_CACHE_RESET();
        /* Enable data cache */
        FLASH_DATA_CACHE_ENABLE();
    }

    /* Reset internal variable */
    MCU_FLASH.CacheToReactivate = FLASH_CACHE_DISABLED;
}

/**
  * @brief  Configure the write protection area into Option Bytes.
  * @note   When the memory read protection level is selected (RDP level = 1),
  *         it is not possible to program or erase Flash memory if the CPU debug
  *         features are connected (JTAG or single wire) or boot code is being
  *         executed from RAM or System flash, even if WRP is not activated.
  * @note   To configure any option bytes, the option lock bit OPTLOCK must be
  *         cleared with the call of FLASH_OB_Unlock() function.
  * @note   New option bytes configuration will be taken into account in two cases:
  *         - after an option bytes launch through the call of FLASH_OB_Launch()
  *         - after a power reset (BOR reset or exit from Standby/Shutdown modes)
  * @param  WRPArea specifies the area to be configured.
  *         This parameter can be one of the following values:
  *            @arg OB_WRPAREA_BANK1_AREAA: Flash Bank 1 Area A
  *            @arg OB_WRPAREA_BANK1_AREAB: Flash Bank 1 Area B
  *            @arg OB_WRPAREA_BANK2_AREAA: Flash Bank 2 Area A (*)
  *            @arg OB_WRPAREA_BANK2_AREAB: Flash Bank 2 Area B (*)
  * @note   (*) availability depends on devices
  * @param  WRPStartOffset specifies the start page of the write protected area.
  *         This parameter can be page number between 0 and (max number of pages in the bank - 1).
  * @param  WRDPEndOffset specifies the end page of the write protected area.
  *         This parameter can be page number between WRPStartOffset and (max number of pages in the bank - 1).
  * @retval FLASH_Status
  */
static FLASH_StatusTypeDef FLASH_OB_WRPConfig(uint32_t WRPArea, uint32_t WRPStartOffset, uint32_t WRDPEndOffset)
{
    FLASH_StatusTypeDef status;

    /* Check the parameters */
    assert_param(IS_OB_WRPAREA(WRPArea));
    assert_param(IS_FLASH_PAGE(WRPStartOffset));
    assert_param(IS_FLASH_PAGE(WRDPEndOffset));

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

    if (status == FLASH_OK)
    {
        /* Configure the write protected area */
        if (WRPArea == OB_WRPAREA_BANK1_AREAA)
        {
            FLASH->WRP1AR = ((WRDPEndOffset << FLASH_WRP1AR_WRP1A_END_Pos) | WRPStartOffset);
        }
        else if (WRPArea == OB_WRPAREA_BANK1_AREAB)
        {
            FLASH->WRP1BR = ((WRDPEndOffset << FLASH_WRP1BR_WRP1B_END_Pos) | WRPStartOffset);
        }
#if defined (FLASH_OPTR_DBANK)
        else if (WRPArea == OB_WRPAREA_BANK2_AREAA)
        {
            FLASH->WRP2AR = ((WRDPEndOffset << FLASH_WRP2AR_WRP2A_END_Pos) | WRPStartOffset);
        }
        else if (WRPArea == OB_WRPAREA_BANK2_AREAB)
        {
            FLASH->WRP2BR = ((WRDPEndOffset << FLASH_WRP2BR_WRP2B_END_Pos) | WRPStartOffset);
        }
#endif
        else
        {
            /* Nothing to do */
        }

        /* Set OPTSTRT Bit */
        SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);

        /* Wait for last operation to be completed */
        status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
    }

    return status;
}

/**
  * @brief  Set the read protection level into Option Bytes.
  * @note   To configure any option bytes, the option lock bit OPTLOCK must be
  *         cleared with the call of FLASH_OB_Unlock() function.
  * @note   New option bytes configuration will be taken into account in two cases:
  *         - after an option bytes launch through the call of FLASH_OB_Launch()
  *         - after a power reset (BOR reset or exit from Standby/Shutdown modes)
  * @note   !!! Warning : When enabling OB_RDP level 2 it's no more possible
  *         to go back to level 1 or 0 !!!
  * @param  RDPLevel specifies the read protection level.
  *         This parameter can be one of the following values:
  *            @arg OB_RDP_LEVEL_0: No protection
  *            @arg OB_RDP_LEVEL_1: Memory Read protection
  *            @arg OB_RDP_LEVEL_2: Full chip protection
  *
  * @retval FLASH_Status
  */
static FLASH_StatusTypeDef FLASH_OB_RDPConfig(uint32_t RDPLevel)
{
    FLASH_StatusTypeDef status;

    /* Check the parameters */
    assert_param(IS_OB_RDP_LEVEL(RDPLevel));

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

    if (status == FLASH_OK)
    {
        /* Configure the RDP level in the option bytes register */
        MODIFY_REG(FLASH->OPTR, FLASH_OPTR_RDP, RDPLevel);

        /* Set OPTSTRT Bit */
        SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);

        /* Wait for last operation to be completed */
        status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
    }

    return status;
}

/**
  * @brief  Program the FLASH User Option Bytes.
  * @note   To configure any option bytes, the option lock bit OPTLOCK must be
  *         cleared with the call of FLASH_OB_Unlock() function.
  * @note   New option bytes configuration will be taken into account in two cases:
  *         - after an option bytes launch through the call of FLASH_OB_Launch()
  *         - after a power reset (BOR reset or exit from Standby/Shutdown modes)
  * @param  UserType The FLASH User Option Bytes to be modified.
  *         This parameter can be a combination of @ref FLASH_OB_USER_Type.
  * @param  UserConfig The selected User Option Bytes values:
  *         This parameter can be a combination of @ref FLASH_OB_USER_BOR_LEVEL,
  *         @ref FLASH_OB_USER_nRST_STOP, @ref FLASH_OB_USER_nRST_STANDBY ,
  *         @ref FLASH_OB_USER_nRST_SHUTDOWN, @ref FLASH_OB_USER_IWDG_SW,
  *         @ref FLASH_OB_USER_IWDG_STOP, @ref FLASH_OB_USER_IWDG_STANDBY,
  *         @ref FLASH_OB_USER_WWDG_SW, @ref FLASH_OB_USER_WWDG_SW,
  *         @ref FLASH_OB_USER_BFB2 (*), @ref FLASH_OB_USER_nBOOT1,
  *         @ref FLASH_OB_USER_SRAM_PE, @ref FLASH_OB_USER_CCMSRAM_RST,
  *         @ref FLASH_OB_USER_nSWBOOT0, @ref FLASH_OB_USER_nBOOT0,
  *         @ref FLASH_OB_USER_NRST_MODE, @ref FLASH_OB_USER_INTERNAL_RESET_HOLDER
  * @note   (*) availability depends on devices
  * @retval FLASH_Status
  */
static FLASH_StatusTypeDef FLASH_OB_UserConfig(uint32_t UserType, uint32_t UserConfig)
{
    uint32_t optr_reg_val = 0;
    uint32_t optr_reg_mask = 0;
    FLASH_StatusTypeDef status;

    /* Check the parameters */
    assert_param(IS_OB_USER_TYPE(UserType));

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

    if (status == FLASH_OK)
    {
        if ((UserType & OB_USER_BOR_LEV) != 0U)
        {
            /* BOR level option byte should be modified */
            assert_param(IS_OB_USER_BOR_LEVEL(UserConfig & FLASH_OPTR_BOR_LEV));

            /* Set value and mask for BOR level option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_BOR_LEV);
            optr_reg_mask |= FLASH_OPTR_BOR_LEV;
        }

        if ((UserType & OB_USER_nRST_STOP) != 0U)
        {
            /* nRST_STOP option byte should be modified */
            assert_param(IS_OB_USER_STOP(UserConfig & FLASH_OPTR_nRST_STOP));

            /* Set value and mask for nRST_STOP option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_nRST_STOP);
            optr_reg_mask |= FLASH_OPTR_nRST_STOP;
        }

        if ((UserType & OB_USER_nRST_STDBY) != 0U)
        {
            /* nRST_STDBY option byte should be modified */
            assert_param(IS_OB_USER_STANDBY(UserConfig & FLASH_OPTR_nRST_STDBY));

            /* Set value and mask for nRST_STDBY option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_nRST_STDBY);
            optr_reg_mask |= FLASH_OPTR_nRST_STDBY;
        }

        if ((UserType & OB_USER_nRST_SHDW) != 0U)
        {
            /* nRST_SHDW option byte should be modified */
            assert_param(IS_OB_USER_SHUTDOWN(UserConfig & FLASH_OPTR_nRST_SHDW));

            /* Set value and mask for nRST_SHDW option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_nRST_SHDW);
            optr_reg_mask |= FLASH_OPTR_nRST_SHDW;
        }

        if ((UserType & OB_USER_IWDG_SW) != 0U)
        {
            /* IWDG_SW option byte should be modified */
            assert_param(IS_OB_USER_IWDG(UserConfig & FLASH_OPTR_IWDG_SW));

            /* Set value and mask for IWDG_SW option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_IWDG_SW);
            optr_reg_mask |= FLASH_OPTR_IWDG_SW;
        }

        if ((UserType & OB_USER_IWDG_STOP) != 0U)
        {
            /* IWDG_STOP option byte should be modified */
            assert_param(IS_OB_USER_IWDG_STOP(UserConfig & FLASH_OPTR_IWDG_STOP));

            /* Set value and mask for IWDG_STOP option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_IWDG_STOP);
            optr_reg_mask |= FLASH_OPTR_IWDG_STOP;
        }

        if ((UserType & OB_USER_IWDG_STDBY) != 0U)
        {
            /* IWDG_STDBY option byte should be modified */
            assert_param(IS_OB_USER_IWDG_STDBY(UserConfig & FLASH_OPTR_IWDG_STDBY));

            /* Set value and mask for IWDG_STDBY option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_IWDG_STDBY);
            optr_reg_mask |= FLASH_OPTR_IWDG_STDBY;
        }

        if ((UserType & OB_USER_WWDG_SW) != 0U)
        {
            /* WWDG_SW option byte should be modified */
            assert_param(IS_OB_USER_WWDG(UserConfig & FLASH_OPTR_WWDG_SW));

            /* Set value and mask for WWDG_SW option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_WWDG_SW);
            optr_reg_mask |= FLASH_OPTR_WWDG_SW;
        }

#if defined (FLASH_OPTR_BFB2)
        if ((UserType & OB_USER_BFB2) != 0U)
        {
            /* BFB2 option byte should be modified */
            assert_param(IS_OB_USER_BFB2(UserConfig & FLASH_OPTR_BFB2));

            /* Set value and mask for BFB2 option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_BFB2);
            optr_reg_mask |= FLASH_OPTR_BFB2;
        }
#endif

        if ((UserType & OB_USER_nBOOT1) != 0U)
        {
            /* nBOOT1 option byte should be modified */
            assert_param(IS_OB_USER_BOOT1(UserConfig & FLASH_OPTR_nBOOT1));

            /* Set value and mask for nBOOT1 option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_nBOOT1);
            optr_reg_mask |= FLASH_OPTR_nBOOT1;
        }

        if ((UserType & OB_USER_SRAM_PE) != 0U)
        {
            /* SRAM_PE option byte should be modified */
            assert_param(IS_OB_USER_SRAM_PARITY(UserConfig & FLASH_OPTR_SRAM_PE));

            /* Set value and mask for SRAM_PE option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_SRAM_PE);
            optr_reg_mask |= FLASH_OPTR_SRAM_PE;
        }

        if ((UserType & OB_USER_CCMSRAM_RST) != 0U)
        {
            /* CCMSRAM_RST option byte should be modified */
            assert_param(IS_OB_USER_CCMSRAM_RST(UserConfig & FLASH_OPTR_CCMSRAM_RST));

            /* Set value and mask for CCMSRAM_RST option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_CCMSRAM_RST);
            optr_reg_mask |= FLASH_OPTR_CCMSRAM_RST;
        }

        if ((UserType & OB_USER_nSWBOOT0) != 0U)
        {
            /* nSWBOOT0 option byte should be modified */
            assert_param(IS_OB_USER_SWBOOT0(UserConfig & FLASH_OPTR_nSWBOOT0));

            /* Set value and mask for nSWBOOT0 option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_nSWBOOT0);
            optr_reg_mask |= FLASH_OPTR_nSWBOOT0;
        }

        if ((UserType & OB_USER_nBOOT0) != 0U)
        {
            /* nBOOT0 option byte should be modified */
            assert_param(IS_OB_USER_BOOT0(UserConfig & FLASH_OPTR_nBOOT0));

            /* Set value and mask for nBOOT0 option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_nBOOT0);
            optr_reg_mask |= FLASH_OPTR_nBOOT0;
        }

        if ((UserType & OB_USER_NRST_MODE) != 0U)
        {
            /* Reset Configuration option byte should be modified */
            assert_param(IS_OB_USER_NRST_MODE(UserConfig & FLASH_OPTR_NRST_MODE));

            /* Set value and mask for Reset Configuration option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_NRST_MODE);
            optr_reg_mask |= FLASH_OPTR_NRST_MODE;
        }

        if ((UserType & OB_USER_IRHEN) != 0U)
        {
            /* IRH option byte should be modified */
            assert_param(IS_OB_USER_IRHEN(UserConfig & FLASH_OPTR_IRHEN));

            /* Set value and mask for IRH option byte */
            optr_reg_val |= (UserConfig & FLASH_OPTR_IRHEN);
            optr_reg_mask |= FLASH_OPTR_IRHEN;
        }

        /* Configure the option bytes register */
        MODIFY_REG(FLASH->OPTR, optr_reg_mask, optr_reg_val);

        /* Set OPTSTRT Bit */
        SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);

        /* Wait for last operation to be completed */
        status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
    }

    return status;
}

/**
  * @brief  Configure the Proprietary code readout protection area into Option Bytes.
  * @note   To configure any option bytes, the option lock bit OPTLOCK must be
  *         cleared with the call of FLASH_OB_Unlock() function.
  * @note   New option bytes configuration will be taken into account in two cases:
  *         - after an option bytes launch through the call of FLASH_OB_Launch()
  *         - after a power reset (BOR reset or exit from Standby/Shutdown modes)
  * @param  PCROPConfig specifies the configuration (Bank to be configured and PCROP_RDP option).
  *         This parameter must be a combination of FLASH_BANK_1 or FLASH_BANK_2 (*)
  *         with OB_PCROP_RDP_NOT_ERASE or OB_PCROP_RDP_ERASE.
  * @note   (*) availability depends on devices
  * @param  PCROPStartAddr specifies the start address of the Proprietary code readout protection.
  *         This parameter can be an address between begin and end of the bank.
  * @param  PCROPEndAddr specifies the end address of the Proprietary code readout protection.
  *         This parameter can be an address between PCROPStartAddr and end of the bank.
  * @retval FLASH_Status
  */
static FLASH_StatusTypeDef FLASH_OB_PCROPConfig(uint32_t PCROPConfig, uint32_t PCROPStartAddr, uint32_t PCROPEndAddr)
{
    FLASH_StatusTypeDef status;
    uint32_t reg_value;
    uint32_t bank1_addr;
#if defined (FLASH_OPTR_DBANK)
    uint32_t bank2_addr;
#endif

    /* Check the parameters */
    assert_param(IS_FLASH_BANK_EXCLUSIVE(PCROPConfig & FLASH_BANK_BOTH));
    assert_param(IS_OB_PCROP_RDP(PCROPConfig & FLASH_PCROP1ER_PCROP_RDP));
    assert_param(IS_FLASH_MAIN_MEM_ADDRESS(PCROPStartAddr));
    assert_param(IS_FLASH_MAIN_MEM_ADDRESS(PCROPEndAddr));

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

    if (status == FLASH_OK)
    {
#if defined (FLASH_OPTR_DBANK)
        /* Get the information about the bank swapping */
        if (READ_BIT(SYSCFG->MEMRMP, SYSCFG_MEMRMP_FB_MODE) == 0U)
        {
            bank1_addr = FLASH_BASE;
            bank2_addr = FLASH_BASE + FLASH_BANK_SIZE;
        }
        else
        {
            bank1_addr = FLASH_BASE + FLASH_BANK_SIZE;
            bank2_addr = FLASH_BASE;
        }
#else
        bank1_addr = FLASH_BASE;
#endif

#if defined (FLASH_OPTR_DBANK)
        if (READ_BIT(FLASH->OPTR, FLASH_OPTR_DBANK) == 0U)
        {
            /* Configure the Proprietary code readout protection */
            if ((PCROPConfig & FLASH_BANK_BOTH) == FLASH_BANK_1)
            {
                reg_value = ((PCROPStartAddr - FLASH_BASE) >> 4);
                MODIFY_REG(FLASH->PCROP1SR, FLASH_PCROP1SR_PCROP1_STRT, reg_value);

                reg_value = ((PCROPEndAddr - FLASH_BASE) >> 4);
                MODIFY_REG(FLASH->PCROP1ER, FLASH_PCROP1ER_PCROP1_END, reg_value);
            }
            else if ((PCROPConfig & FLASH_BANK_BOTH) == FLASH_BANK_2)
            {
                reg_value = ((PCROPStartAddr - FLASH_BASE) >> 4);
                MODIFY_REG(FLASH->PCROP2SR, FLASH_PCROP2SR_PCROP2_STRT, reg_value);

                reg_value = ((PCROPEndAddr - FLASH_BASE) >> 4);
                MODIFY_REG(FLASH->PCROP2ER, FLASH_PCROP2ER_PCROP2_END, reg_value);
            }
            else
            {
                /* Nothing to do */
            }
        }
        else
#endif
        {
            /* Configure the Proprietary code readout protection */
            if ((PCROPConfig & FLASH_BANK_BOTH) == FLASH_BANK_1)
            {
                reg_value = ((PCROPStartAddr - bank1_addr) >> 3);
                MODIFY_REG(FLASH->PCROP1SR, FLASH_PCROP1SR_PCROP1_STRT, reg_value);

                reg_value = ((PCROPEndAddr - bank1_addr) >> 3);
                MODIFY_REG(FLASH->PCROP1ER, FLASH_PCROP1ER_PCROP1_END, reg_value);
            }
#if defined (FLASH_OPTR_DBANK)
            else if ((PCROPConfig & FLASH_BANK_BOTH) == FLASH_BANK_2)
            {
                reg_value = ((PCROPStartAddr - bank2_addr) >> 3);
                MODIFY_REG(FLASH->PCROP2SR, FLASH_PCROP2SR_PCROP2_STRT, reg_value);

                reg_value = ((PCROPEndAddr - bank2_addr) >> 3);
                MODIFY_REG(FLASH->PCROP2ER, FLASH_PCROP2ER_PCROP2_END, reg_value);
            }
#endif
            else
            {
                /* Nothing to do */
            }
        }

        MODIFY_REG(FLASH->PCROP1ER, FLASH_PCROP1ER_PCROP_RDP, (PCROPConfig & FLASH_PCROP1ER_PCROP_RDP));

        /* Set OPTSTRT Bit */
        SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);

        /* Wait for last operation to be completed */
        status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
    }

    return status;
}

/**
  * @brief  Configure the Securable memory area into Option Bytes.
  * @note   To configure any option bytes, the option lock bit OPTLOCK must be
  *         cleared with the call of FLASH_OB_Unlock() function.
  * @note   New option bytes configuration will be taken into account in two cases:
  *         - after an option bytes launch through the call of FLASH_OB_Launch()
  *         - after a power reset (BOR reset or exit from Standby/Shutdown modes)
  * @param  SecBank specifies bank of securable memory area to be configured.
  *          This parameter can be one of the following values:
  *            @arg FLASH_BANK_1: Securable memory in Bank1 to be configured
  *            @arg FLASH_BANK_2: Securable memory in Bank2 to be configured (*)
  * @note   (*) availability depends on devices
  * @param  SecSize specifies the number of pages of the Securable memory area,
  *         starting from first page of the bank.
  *         This parameter can be page number between 0 and (max number of pages in the bank - 1)
  * @retval FLASH Status
  */
static FLASH_StatusTypeDef FLASH_OB_SecMemConfig(uint32_t SecBank, uint32_t SecSize)
{
    FLASH_StatusTypeDef status;

    /* Check the parameters */
    assert_param(IS_FLASH_BANK_EXCLUSIVE(SecBank));
    assert_param(IS_OB_SECMEM_SIZE(SecSize));

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

    if (status == FLASH_OK)
    {
        /* Configure the write protected area */
        if (SecBank == FLASH_BANK_1)
        {
            MODIFY_REG(FLASH->SEC1R, FLASH_SEC1R_SEC_SIZE1, SecSize);
        }
#if defined (FLASH_OPTR_DBANK)
        else if (SecBank == FLASH_BANK_2)
        {
            MODIFY_REG(FLASH->SEC2R, FLASH_SEC2R_SEC_SIZE2, SecSize);
        }
        else
        {
            /* Nothing to do */
        }
#endif

        /* Set OPTSTRT Bit */
        SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);

        /* Wait for last operation to be completed */
        status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
    }

    return status;
}

/**
  * @brief  Configure the Boot Lock into Option Bytes.
  * @note   To configure any option bytes, the option lock bit OPTLOCK must be
  *         cleared with the call of FLASH_OB_Unlock() function.
  * @note   New option bytes configuration will be taken into account in two cases:
  *         - after an option bytes launch through the call of FLASH_OB_Launch()
  *         - after a power reset (BOR reset or exit from Standby/Shutdown modes)
  * @param  BootLockConfig specifies the boot lock configuration.
  *          This parameter can be one of the following values:
  *            @arg OB_BOOT_LOCK_ENABLE: Enable Boot Lock
  *            @arg OB_BOOT_LOCK_DISABLE: Disable Boot Lock
  *
  * @retval FLASH_Status
  */
static FLASH_StatusTypeDef FLASH_OB_BootLockConfig(uint32_t BootLockConfig)
{
    FLASH_StatusTypeDef status;

    /* Check the parameters */
    assert_param(IS_OB_BOOT_LOCK(BootLockConfig));

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

    if (status == FLASH_OK)
    {
        MODIFY_REG(FLASH->SEC1R, FLASH_SEC1R_BOOT_LOCK, BootLockConfig);

        /* Set OPTSTRT Bit */
        SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);

        /* Wait for last operation to be completed */
        status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
    }

    return status;
}

/**
  * @brief  Return the Securable memory area configuration into Option Bytes.
  * @param[in]  SecBank specifies the bank where securable memory area is located.
  *          This parameter can be one of the following values:
  *            @arg FLASH_BANK_1: Securable memory in Bank1
  *            @arg FLASH_BANK_2: Securable memory in Bank2 (*)
  * @note   (*) availability depends on devices
  * @param[out]  SecSize specifies the number of pages used in the securable
                 memory area of the bank.
  * @retval None
  */
static void FLASH_OB_GetSecMem(uint32_t SecBank, uint32_t *SecSize)
{
    /* Get the configuration of the securable memory area */
    if (SecBank == FLASH_BANK_1)
    {
        *SecSize = READ_BIT(FLASH->SEC1R, FLASH_SEC1R_SEC_SIZE1);
    }
#if defined (FLASH_OPTR_DBANK)
    else if (SecBank == FLASH_BANK_2)
    {
        *SecSize = READ_BIT(FLASH->SEC2R, FLASH_SEC2R_SEC_SIZE2);
    }
    else
    {
        /* Nothing to do */
    }
#endif
}

/**
  * @brief  Return the Boot Lock configuration into Option Byte.
  * @retval BootLockConfig.
  *         This return value can be one of the following values:
  *            @arg OB_BOOT_LOCK_ENABLE: Boot lock enabled
  *            @arg OB_BOOT_LOCK_DISABLE: Boot lock disabled
  */
static uint32_t FLASH_OB_GetBootLock(void)
{
    return (READ_REG(FLASH->SEC1R) & FLASH_SEC1R_BOOT_LOCK);
}

/**
  * @brief  Return the Write Protection configuration into Option Bytes.
  * @param[in]  WRPArea specifies the area to be returned.
  *          This parameter can be one of the following values:
  *            @arg OB_WRPAREA_BANK1_AREAA: Flash Bank 1 Area A
  *            @arg OB_WRPAREA_BANK1_AREAB: Flash Bank 1 Area B
  *            @arg OB_WRPAREA_BANK2_AREAA: Flash Bank 2 Area A (don't apply to STM32G43x/STM32G44x devices)
  *            @arg OB_WRPAREA_BANK2_AREAB: Flash Bank 2 Area B (don't apply to STM32G43x/STM32G44x devices)
  * @param[out]  WRPStartOffset specifies the address where to copied the start page
  *              of the write protected area.
  * @param[out]  WRDPEndOffset specifies the address where to copied the end page of
  *              the write protected area.
  * @retval None
  */
static void FLASH_OB_GetWRP(uint32_t WRPArea, uint32_t *WRPStartOffset, uint32_t *WRDPEndOffset)
{
    /* Get the configuration of the write protected area */
    if (WRPArea == OB_WRPAREA_BANK1_AREAA)
    {
        *WRPStartOffset = READ_BIT(FLASH->WRP1AR, FLASH_WRP1AR_WRP1A_STRT);
        *WRDPEndOffset = (READ_BIT(FLASH->WRP1AR, FLASH_WRP1AR_WRP1A_END) >> FLASH_WRP1AR_WRP1A_END_Pos);
    }
    else if (WRPArea == OB_WRPAREA_BANK1_AREAB)
    {
        *WRPStartOffset = READ_BIT(FLASH->WRP1BR, FLASH_WRP1BR_WRP1B_STRT);
        *WRDPEndOffset = (READ_BIT(FLASH->WRP1BR, FLASH_WRP1BR_WRP1B_END) >> FLASH_WRP1BR_WRP1B_END_Pos);
    }
#if defined (FLASH_OPTR_DBANK)
    else if (WRPArea == OB_WRPAREA_BANK2_AREAA)
    {
        *WRPStartOffset = READ_BIT(FLASH->WRP2AR, FLASH_WRP2AR_WRP2A_STRT);
        *WRDPEndOffset = (READ_BIT(FLASH->WRP2AR, FLASH_WRP2AR_WRP2A_END) >> FLASH_WRP2AR_WRP2A_END_Pos);
    }
    else if (WRPArea == OB_WRPAREA_BANK2_AREAB)
    {
        *WRPStartOffset = READ_BIT(FLASH->WRP2BR, FLASH_WRP2BR_WRP2B_STRT);
        *WRDPEndOffset = (READ_BIT(FLASH->WRP2BR, FLASH_WRP2BR_WRP2B_END) >> FLASH_WRP2BR_WRP2B_END_Pos);
    }
#endif
    else
    {
        /* Nothing to do */
    }
}

/**
  * @brief  Return the FLASH Read Protection level into Option Bytes.
  * @retval RDP_Level
  *         This return value can be one of the following values:
  *            @arg OB_RDP_LEVEL_0: No protection
  *            @arg OB_RDP_LEVEL_1: Read protection of the memory
  *            @arg OB_RDP_LEVEL_2: Full chip protection
  */
static uint32_t FLASH_OB_GetRDP(void)
{
    uint32_t rdp_level = READ_BIT(FLASH->OPTR, FLASH_OPTR_RDP);

    if ((rdp_level != OB_RDP_LEVEL_0) && (rdp_level != OB_RDP_LEVEL_2))
    {
        return (OB_RDP_LEVEL_1);
    }
    else
    {
        return rdp_level;
    }
}

/**
  * @brief  Return the FLASH User Option Byte value.
  * @retval OB_user_config
  *         This return value is a combination of @ref FLASH_OB_USER_BOR_LEVEL,
  *         @ref FLASH_OB_USER_nRST_STOP, @ref FLASH_OB_USER_nRST_STANDBY,
  *         @ref FLASH_OB_USER_nRST_SHUTDOWN, @ref FLASH_OB_USER_IWDG_SW,
  *         @ref FLASH_OB_USER_IWDG_STOP, @ref FLASH_OB_USER_IWDG_STANDBY,
  *         @ref FLASH_OB_USER_WWDG_SW, @ref FLASH_OB_USER_WWDG_SW,
  *         @ref FLASH_OB_USER_BFB2 (*), @ref FLASH_OB_USER_DBANK (*),
  *         @ref FLASH_OB_USER_nBOOT1, @ref FLASH_OB_USER_SRAM_PE,
  *         @ref FLASH_OB_USER_CCMSRAM_RST, @ref OB_USER_nSWBOOT0,@ref FLASH_OB_USER_nBOOT0,
  *         @ref FLASH_OB_USER_NRST_MODE, @ref FLASH_OB_USER_INTERNAL_RESET_HOLDER
  * @note  (*) availability depends on devices
  */
static uint32_t FLASH_OB_GetUser(void)
{
    uint32_t user_config = READ_REG(FLASH->OPTR);
    CLEAR_BIT(user_config, FLASH_OPTR_RDP);

    return user_config;
}

/**
  * @brief  Return the FLASH PCROP configuration into Option Bytes.
  * @param[in,out] PCROPConfig specifies the configuration (Bank to be configured and PCROP_RDP option).
  *        This parameter must be a combination of FLASH_BANK_1 or FLASH_BANK_2
  *        with OB_PCROP_RDP_NOT_ERASE or OB_PCROP_RDP_ERASE.
  * @param[out] PCROPStartAddr specifies the address where to copied the start address
  *        of the Proprietary code readout protection.
  * @param[out] PCROPEndAddr specifies the address where to copied the end address of
  *        the Proprietary code readout protection.
  * @retval None
  */
static void FLASH_OB_GetPCROP(uint32_t *PCROPConfig, uint32_t *PCROPStartAddr, uint32_t *PCROPEndAddr)
{
    uint32_t reg_value;
    uint32_t bank1_addr;
#if defined (FLASH_OPTR_DBANK)
    uint32_t bank2_addr;

    /* Get the information about the bank swapping */
    if (READ_BIT(SYSCFG->MEMRMP, SYSCFG_MEMRMP_FB_MODE) == 0U)
    {
        bank1_addr = FLASH_BASE;
        bank2_addr = FLASH_BASE + FLASH_BANK_SIZE;
    }
    else
    {
        bank1_addr = FLASH_BASE + FLASH_BANK_SIZE;
        bank2_addr = FLASH_BASE;
    }
#else
    bank1_addr = FLASH_BASE;
#endif

#if defined (FLASH_OPTR_DBANK)
    if (READ_BIT(FLASH->OPTR, FLASH_OPTR_DBANK) == 0U)
    {
        if (((*PCROPConfig) & FLASH_BANK_BOTH) == FLASH_BANK_1)
        {
            reg_value       = (READ_REG(FLASH->PCROP1SR) & FLASH_PCROP1SR_PCROP1_STRT);
            *PCROPStartAddr = (reg_value << 4) + FLASH_BASE;

            reg_value     = (READ_REG(FLASH->PCROP1ER) & FLASH_PCROP1ER_PCROP1_END);
            *PCROPEndAddr = (reg_value << 4) + FLASH_BASE;
        }
        else if (((*PCROPConfig) & FLASH_BANK_BOTH) == FLASH_BANK_2)
        {
            reg_value       = (READ_REG(FLASH->PCROP2SR) & FLASH_PCROP2SR_PCROP2_STRT);
            *PCROPStartAddr = (reg_value << 4) + FLASH_BASE;

            reg_value     = (READ_REG(FLASH->PCROP2ER) & FLASH_PCROP2ER_PCROP2_END);
            *PCROPEndAddr = (reg_value << 4) + FLASH_BASE;
        }
        else
        {
            /* Nothing to do */
        }
    }
    else
#endif
    {
        if (((*PCROPConfig) & FLASH_BANK_BOTH) == FLASH_BANK_1)
        {
            reg_value       = (READ_REG(FLASH->PCROP1SR) & FLASH_PCROP1SR_PCROP1_STRT);
            *PCROPStartAddr = (reg_value << 3) + bank1_addr;

            reg_value     = (READ_REG(FLASH->PCROP1ER) & FLASH_PCROP1ER_PCROP1_END);
            *PCROPEndAddr = (reg_value << 3) + bank1_addr;
        }
#if defined (FLASH_OPTR_DBANK)
        else if (((*PCROPConfig) & FLASH_BANK_BOTH) == FLASH_BANK_2)
        {
            reg_value       = (READ_REG(FLASH->PCROP2SR) & FLASH_PCROP2SR_PCROP2_STRT);
            *PCROPStartAddr = (reg_value << 3) + bank2_addr;

            reg_value     = (READ_REG(FLASH->PCROP2ER) & FLASH_PCROP2ER_PCROP2_END);
            *PCROPEndAddr = (reg_value << 3) + bank2_addr;
        }
#endif
        else
        {
            /* Nothing to do */
        }
    }

    *PCROPConfig |= (READ_REG(FLASH->PCROP1ER) & FLASH_PCROP1ER_PCROP_RDP);
}

文章来源:https://blog.csdn.net/L0ve777/article/details/135129043
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。