使用LL库下的STM32G4x FLASH读写配置结构体详解

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

笔者用的芯片为STM32G473CBT6 128KB Flash,开环环境为CUBEMX+MDK5.32,因为G4已经没有标准库了,笔者还是习惯使用标准库的开发方式,所以选择了LL库开发应用,但是LL库没有对Flash进行支持,所以笔者想通过修改HAL库的Flash驱动来使用。

介绍下Datasheet内容,STM32G473系列有支持 ECC 的 最大512 KB 闪存、两个边写边读的存储体、专有代码读出保护 (PCROP)、安全存储区域、1 KB OTP区域。

STM32G473xB/xC/xE 器件具有高达 512 KB 的嵌入式闪存,可用于存储程序和数据。

闪存接口特性:

        – 单存储体或双存储体操作模式

        – 双存储体模式下的边写边读 (RWW)

此功能允许在对另一存储体执行擦除或编程操作的同时从一个存储体执行读取操作。还支持双组启动。(双分区升级貌似很方便)

通过选项字节可以配置灵活的保护:

• 读出保护(RDP),用于保护整个存储器。提供三种保护级别:

        – 0 级:无读出保护

        – 1 级:存储器读出保护;如果连接了调试功能或选择了 RAM 启动或启动加载程序,则无法读取或写入闪存

        – 第 2 级:芯片读出保护;调试功能(Cortex-M4 JTAG 和串行线)、RAM 中的引导和引导加载程序选择被禁用(JTAG 熔丝)。这种选择是不可逆转的。(产品定型之后)

• 写保护(WRP):保护区域不被擦除和编程。

• 专有代码读出保护(PCROP):可以保护闪存的一部分,防止第三方读写。该保护区是只执行的,只能由STM32 CPU作为指令代码访问,而严格禁止所有其他访问(DMA、调试和CPU数据读取、写入和擦除)。当 RDP 保护从级别 1 更改为级别 0 时,附加选项位 (PCROP_RDP) 允许选择是否擦除 PCROP 区域。

• 安全存储区域:闪存的一部分可以通过选项字节配置为安全的。复位后,该安全存储区域不受保护,其行为类似于主闪存的其余部分(执行、读取、写入访问)。当安全时,对该安全存储区域的任何访问都会产生相应的读/写错误。

安全内存区域的目的是保护敏感代码和数据(安全密钥存储),这些代码和数据只能在启动时执行一次,除非发生新的重置,否则永远不会再次执行。

闪存嵌入纠错码 (ECC) 功能,支持:

• 单错误检测和纠正(应该指字节)

• 双错误检测

• ECC 失败的地址可在 ECC 寄存器中读取

• 1 KB(128 个双字)OTP(单字节)时间可编程)用于用户数据。 OTP 区域仅在 Bank 1 中可用。 OTP 数据无法擦除且只能写入一次。(常用来写密钥)

Flash的相关特性

查看参考手册发现STM32G4系列芯片FLASH分成好几类

4类器件

• 高达512 KB 的闪存(单块)

• 64 位数据宽度的闪存读取操作

• 页擦除和批量擦除

2类设备

• 高达128 KB 的闪存(单块)

• 64 位数据宽度的闪存读取操作

• 页擦除和批量擦除

 3类器件

• 高达 512 KB 的闪存,采用双存储体架构,支持边写边读功能 (RWW)

• 支持两种数据宽度模式的闪存读取操作:

        – 单存储体模式 DBANK=0:128 位读访问

        – 双存储体模式 DBANK=1:64 位读访问

• 页擦除、存储体擦除和批量擦除(两个存储体)

根据描述,STM32G473应该是第3类器件

接着来看,闪存结构

该闪存具有以下主要特性:

• 容量高达 512 KB,单存储体模式(读取宽度为 128 位)或双存储体模式(读取宽度为 64 位)

• 通过 BFB2 选项支持双启动模式位(仅在双存储体模式下)

• 当 DBANK 位置 1 时,为双存储体模式:

        – 512 KB 组织为 2 个存储体作为主存储器

        – 页大小为 2 KB

        – 72 位宽数据读取(64 位加 8 ECC 位)

        – Bank和批量擦除

• DBANK 复位时,为单存储体模式:

        – 512 KB 组织在一个存储体中作为主存储器

        – 页大小为 4 KB

        – 144 位宽数据读取(128 位加 2×8 ECC 位)

        – 批量擦除

结构划分如下:

• 根据双存储体配置位划分的主存储器块结构:

        – 当使能双存储体(DBANK 位置位)时,闪存分为 2 个 256 KB 的存储体,每个存储体的组织方式如下:

                主存储器块包含 128 个 2 KB 页

                每页由 8 行 256 字节组成

        – 当禁用双存储体(DBANK 位复位)时,主存储器块被组织为一个 512 KB 的单存储体,如下所示:

                主存储器包含 128 个 4 KB 页的块

                每个页由 8 行 512 字节组成

• 信息块包含:

        – 设备在系统内存引导模式下从其引导的系统内存。该区域保留供 STMicroElectronics 使用,包含引导加载程序,用于通过以下接口之一对闪存重新编程:USART、SPI、I2C、FDCAN、USB。它在设备制造时由意法半导体进行编程,并防止虚假写入/擦除操作。如需了解更多详细信息,请参阅 www.st.com 上提供的 AN2606。(就是ST的bootloader,如ISP下载等)

        – 1 KB(128 个双字)OTP(一次性可编程)字节用于用户数据。 OTP 区域仅在 Bank 1 中可用。 OTP 数据无法擦除且只能写入一次。如果只有一位为 0,则即使值为 0x0000 0000 0000 0000,也无法再写入整个双字。

        – 用户配置的选项字节。

存储器结构基于主区域和信息块,如表所示。

查看G473的配置文件,可以看大看到DBANK初始化置位,为双区模式,128KB划分两个64KB分区,每一个从页0到页31,32x2KBx2=128KB。后面为功能介绍,因为移植官方驱动,我决定现在去测试一下代码。

直接给代码。

/**
  ******************************************************************************
  * @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 --------------------------------------------------------*/
#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  */

/**
  * @brief  Base address of the Flash pages under dual bank mode for category 3 devices Bank 1
  *         Dual bank mode(2KB each page)
  *         For 256KB devices: from page 0 to page 63
  *         For 128KB devices: from page 0 to page 31
  *         Single bank mode(4KB each page)
  *         For 256KB devices: from page 0 to page 63
  *         For 128KB devices: from page 0 to page 31
  */
#define FLASH_ADDR_PAGE_0       ((uint32_t)0x08000000)  /* Base @ of Page 0, 2 Kbytes */
#define FLASH_ADDR_PAGE_1       ((uint32_t)0x08000800)  /* Base @ of Page 1, 2 Kbytes */
#define FLASH_ADDR_PAGE_2       ((uint32_t)0x08001000)  /* Base @ of Page 2, 2 Kbytes */
#define FLASH_ADDR_PAGE_3       ((uint32_t)0x08001800)  /* Base @ of Page 3, 2 Kbytes */
#define FLASH_ADDR_PAGE_4       ((uint32_t)0x08002000)  /* Base @ of Page 4, 2 Kbytes */
#define FLASH_ADDR_PAGE_5       ((uint32_t)0x08002800)  /* Base @ of Page 5, 2 Kbytes */
#define FLASH_ADDR_PAGE_6       ((uint32_t)0x08003000)  /* Base @ of Page 6, 2 Kbytes */
#define FLASH_ADDR_PAGE_7       ((uint32_t)0x08003800)  /* Base @ of Page 7, 2 Kbytes */
#define FLASH_ADDR_PAGE_8       ((uint32_t)0x08004000)  /* Base @ of Page 8, 2 Kbytes */
#define FLASH_ADDR_PAGE_9       ((uint32_t)0x08004800)  /* Base @ of Page 9, 2 Kbytes */
#define FLASH_ADDR_PAGE_10      ((uint32_t)0x08005000)  /* Base @ of Page 10, 2 Kbytes */
#define FLASH_ADDR_PAGE_11      ((uint32_t)0x08005800)  /* Base @ of Page 11, 2 Kbytes */
#define FLASH_ADDR_PAGE_12      ((uint32_t)0x08006000)  /* Base @ of Page 12, 2 Kbytes */
#define FLASH_ADDR_PAGE_13      ((uint32_t)0x08006800)  /* Base @ of Page 13, 2 Kbytes */
#define FLASH_ADDR_PAGE_14      ((uint32_t)0x08007000)  /* Base @ of Page 14, 2 Kbytes */
#define FLASH_ADDR_PAGE_15      ((uint32_t)0x08007800)  /* Base @ of Page 15, 2 Kbytes */
#define FLASH_ADDR_PAGE_16      ((uint32_t)0x08008000)  /* Base @ of Page 16, 2 Kbytes */
#define FLASH_ADDR_PAGE_17      ((uint32_t)0x08008800)  /* Base @ of Page 17, 2 Kbytes */
#define FLASH_ADDR_PAGE_18      ((uint32_t)0x08009000)  /* Base @ of Page 18, 2 Kbytes */
#define FLASH_ADDR_PAGE_19      ((uint32_t)0x08009800)  /* Base @ of Page 19, 2 Kbytes */
#define FLASH_ADDR_PAGE_20      ((uint32_t)0x0800A000)  /* Base @ of Page 20, 2 Kbytes */
#define FLASH_ADDR_PAGE_21      ((uint32_t)0x0800A800)  /* Base @ of Page 21, 2 Kbytes */
#define FLASH_ADDR_PAGE_22      ((uint32_t)0x0800B000)  /* Base @ of Page 22, 2 Kbytes */
#define FLASH_ADDR_PAGE_23      ((uint32_t)0x0800B800)  /* Base @ of Page 23, 2 Kbytes */
#define FLASH_ADDR_PAGE_24      ((uint32_t)0x0800C000)  /* Base @ of Page 24, 2 Kbytes */
#define FLASH_ADDR_PAGE_25      ((uint32_t)0x0800C800)  /* Base @ of Page 25, 2 Kbytes */
#define FLASH_ADDR_PAGE_26      ((uint32_t)0x0800D000)  /* Base @ of Page 26, 2 Kbytes */
#define FLASH_ADDR_PAGE_27      ((uint32_t)0x0800D800)  /* Base @ of Page 27, 2 Kbytes */
#define FLASH_ADDR_PAGE_28      ((uint32_t)0x0800E000)  /* Base @ of Page 28, 2 Kbytes */
#define FLASH_ADDR_PAGE_29      ((uint32_t)0x0800E800)  /* Base @ of Page 29, 2 Kbytes */
#define FLASH_ADDR_PAGE_30      ((uint32_t)0x0800F000)  /* Base @ of Page 30, 2 Kbytes */
#define FLASH_ADDR_PAGE_31      ((uint32_t)0x0800F800)  /* Base @ of Page 31, 2 Kbytes */
#define FLASH_ADDR_PAGE_32      ((uint32_t)0x08010000)  /* Base @ of Page 32, 2 Kbytes */
#define FLASH_ADDR_PAGE_33      ((uint32_t)0x08010800)  /* Base @ of Page 33, 2 Kbytes */
#define FLASH_ADDR_PAGE_34      ((uint32_t)0x08011000)  /* Base @ of Page 34, 2 Kbytes */
#define FLASH_ADDR_PAGE_35      ((uint32_t)0x08011800)  /* Base @ of Page 35, 2 Kbytes */
#define FLASH_ADDR_PAGE_36      ((uint32_t)0x08012000)  /* Base @ of Page 36, 2 Kbytes */
#define FLASH_ADDR_PAGE_37      ((uint32_t)0x08012800)  /* Base @ of Page 37, 2 Kbytes */
#define FLASH_ADDR_PAGE_38      ((uint32_t)0x08013000)  /* Base @ of Page 38, 2 Kbytes */
#define FLASH_ADDR_PAGE_39      ((uint32_t)0x08013800)  /* Base @ of Page 39, 2 Kbytes */
#define FLASH_ADDR_PAGE_40      ((uint32_t)0x08014000)  /* Base @ of Page 40, 2 Kbytes */
#define FLASH_ADDR_PAGE_41      ((uint32_t)0x08014800)  /* Base @ of Page 41, 2 Kbytes */
#define FLASH_ADDR_PAGE_42      ((uint32_t)0x08015000)  /* Base @ of Page 42, 2 Kbytes */
#define FLASH_ADDR_PAGE_43      ((uint32_t)0x08015800)  /* Base @ of Page 43, 2 Kbytes */
#define FLASH_ADDR_PAGE_44      ((uint32_t)0x08016000)  /* Base @ of Page 44, 2 Kbytes */
#define FLASH_ADDR_PAGE_45      ((uint32_t)0x08016800)  /* Base @ of Page 45, 2 Kbytes */
#define FLASH_ADDR_PAGE_46      ((uint32_t)0x08017000)  /* Base @ of Page 46, 2 Kbytes */
#define FLASH_ADDR_PAGE_47      ((uint32_t)0x08017800)  /* Base @ of Page 47, 2 Kbytes */
#define FLASH_ADDR_PAGE_48      ((uint32_t)0x08018000)  /* Base @ of Page 48, 2 Kbytes */
#define FLASH_ADDR_PAGE_49      ((uint32_t)0x08018800)  /* Base @ of Page 49, 2 Kbytes */
#define FLASH_ADDR_PAGE_50      ((uint32_t)0x08019000)  /* Base @ of Page 50, 2 Kbytes */
#define FLASH_ADDR_PAGE_51      ((uint32_t)0x08019800)  /* Base @ of Page 51, 2 Kbytes */
#define FLASH_ADDR_PAGE_52      ((uint32_t)0x0801A000)  /* Base @ of Page 52, 2 Kbytes */
#define FLASH_ADDR_PAGE_53      ((uint32_t)0x0801A800)  /* Base @ of Page 53, 2 Kbytes */
#define FLASH_ADDR_PAGE_54      ((uint32_t)0x0801B000)  /* Base @ of Page 54, 2 Kbytes */
#define FLASH_ADDR_PAGE_55      ((uint32_t)0x0801B800)  /* Base @ of Page 55, 2 Kbytes */
#define FLASH_ADDR_PAGE_56      ((uint32_t)0x0801C000)  /* Base @ of Page 56, 2 Kbytes */
#define FLASH_ADDR_PAGE_57      ((uint32_t)0x0801C800)  /* Base @ of Page 57, 2 Kbytes */
#define FLASH_ADDR_PAGE_58      ((uint32_t)0x0801D000)  /* Base @ of Page 58, 2 Kbytes */
#define FLASH_ADDR_PAGE_59      ((uint32_t)0x0801D800)  /* Base @ of Page 59, 2 Kbytes */
#define FLASH_ADDR_PAGE_60      ((uint32_t)0x0801E000)  /* Base @ of Page 60, 2 Kbytes */
#define FLASH_ADDR_PAGE_61      ((uint32_t)0x0801E800)  /* Base @ of Page 61, 2 Kbytes */
#define FLASH_ADDR_PAGE_62      ((uint32_t)0x0801F000)  /* Base @ of Page 62, 2 Kbytes */
#define FLASH_ADDR_PAGE_63      ((uint32_t)0x0801F800)  /* Base @ of Page 63, 2 Kbytes */
#define FLASH_ADDR_PAGE_64      ((uint32_t)0x08020000)  /* Base @ of Page 64, 2 Kbytes */
#define FLASH_ADDR_PAGE_65      ((uint32_t)0x08020800)  /* Base @ of Page 65, 2 Kbytes */
#define FLASH_ADDR_PAGE_66      ((uint32_t)0x08021000)  /* Base @ of Page 66, 2 Kbytes */
#define FLASH_ADDR_PAGE_67      ((uint32_t)0x08021800)  /* Base @ of Page 67, 2 Kbytes */
#define FLASH_ADDR_PAGE_68      ((uint32_t)0x08022000)  /* Base @ of Page 68, 2 Kbytes */
#define FLASH_ADDR_PAGE_69      ((uint32_t)0x08022800)  /* Base @ of Page 69, 2 Kbytes */
#define FLASH_ADDR_PAGE_70      ((uint32_t)0x08023000)  /* Base @ of Page 70, 2 Kbytes */
#define FLASH_ADDR_PAGE_71      ((uint32_t)0x08023800)  /* Base @ of Page 71, 2 Kbytes */
#define FLASH_ADDR_PAGE_72      ((uint32_t)0x08024000)  /* Base @ of Page 72, 2 Kbytes */
#define FLASH_ADDR_PAGE_73      ((uint32_t)0x08024800)  /* Base @ of Page 73, 2 Kbytes */
#define FLASH_ADDR_PAGE_74      ((uint32_t)0x08025000)  /* Base @ of Page 74, 2 Kbytes */
#define FLASH_ADDR_PAGE_75      ((uint32_t)0x08025800)  /* Base @ of Page 75, 2 Kbytes */
#define FLASH_ADDR_PAGE_76      ((uint32_t)0x08026000)  /* Base @ of Page 76, 2 Kbytes */
#define FLASH_ADDR_PAGE_77      ((uint32_t)0x08026800)  /* Base @ of Page 77, 2 Kbytes */
#define FLASH_ADDR_PAGE_78      ((uint32_t)0x08027000)  /* Base @ of Page 78, 2 Kbytes */
#define FLASH_ADDR_PAGE_79      ((uint32_t)0x08027800)  /* Base @ of Page 79, 2 Kbytes */
#define FLASH_ADDR_PAGE_80      ((uint32_t)0x08028000)  /* Base @ of Page 80, 2 Kbytes */
#define FLASH_ADDR_PAGE_81      ((uint32_t)0x08028800)  /* Base @ of Page 81, 2 Kbytes */
#define FLASH_ADDR_PAGE_82      ((uint32_t)0x08029000)  /* Base @ of Page 82, 2 Kbytes */
#define FLASH_ADDR_PAGE_83      ((uint32_t)0x08029800)  /* Base @ of Page 83, 2 Kbytes */
#define FLASH_ADDR_PAGE_84      ((uint32_t)0x0802A000)  /* Base @ of Page 84, 2 Kbytes */
#define FLASH_ADDR_PAGE_85      ((uint32_t)0x0802A800)  /* Base @ of Page 85, 2 Kbytes */
#define FLASH_ADDR_PAGE_86      ((uint32_t)0x0802B000)  /* Base @ of Page 86, 2 Kbytes */
#define FLASH_ADDR_PAGE_87      ((uint32_t)0x0802B800)  /* Base @ of Page 87, 2 Kbytes */
#define FLASH_ADDR_PAGE_88      ((uint32_t)0x0802C000)  /* Base @ of Page 88, 2 Kbytes */
#define FLASH_ADDR_PAGE_89      ((uint32_t)0x0802C800)  /* Base @ of Page 89, 2 Kbytes */
#define FLASH_ADDR_PAGE_90      ((uint32_t)0x0802D000)  /* Base @ of Page 90, 2 Kbytes */
#define FLASH_ADDR_PAGE_91      ((uint32_t)0x0802D800)  /* Base @ of Page 91, 2 Kbytes */
#define FLASH_ADDR_PAGE_92      ((uint32_t)0x0802E000)  /* Base @ of Page 92, 2 Kbytes */
#define FLASH_ADDR_PAGE_93      ((uint32_t)0x0802E800)  /* Base @ of Page 93, 2 Kbytes */
#define FLASH_ADDR_PAGE_94      ((uint32_t)0x0802F000)  /* Base @ of Page 94, 2 Kbytes */
#define FLASH_ADDR_PAGE_95      ((uint32_t)0x0802F800)  /* Base @ of Page 95, 2 Kbytes */
#define FLASH_ADDR_PAGE_96      ((uint32_t)0x08030000)  /* Base @ of Page 96, 2 Kbytes */
#define FLASH_ADDR_PAGE_97      ((uint32_t)0x08030800)  /* Base @ of Page 97, 2 Kbytes */
#define FLASH_ADDR_PAGE_98      ((uint32_t)0x08031000)  /* Base @ of Page 98, 2 Kbytes */
#define FLASH_ADDR_PAGE_99      ((uint32_t)0x08031800)  /* Base @ of Page 99, 2 Kbytes */
#define FLASH_ADDR_PAGE_100     ((uint32_t)0x08032000)  /* Base @ of Page 100, 2 Kbytes */
#define FLASH_ADDR_PAGE_101     ((uint32_t)0x08032800)  /* Base @ of Page 101, 2 Kbytes */
#define FLASH_ADDR_PAGE_102     ((uint32_t)0x08033000)  /* Base @ of Page 102, 2 Kbytes */
#define FLASH_ADDR_PAGE_103     ((uint32_t)0x08033800)  /* Base @ of Page 103, 2 Kbytes */
#define FLASH_ADDR_PAGE_104     ((uint32_t)0x08034000)  /* Base @ of Page 104, 2 Kbytes */
#define FLASH_ADDR_PAGE_105     ((uint32_t)0x08034800)  /* Base @ of Page 105, 2 Kbytes */
#define FLASH_ADDR_PAGE_106     ((uint32_t)0x08035000)  /* Base @ of Page 106, 2 Kbytes */
#define FLASH_ADDR_PAGE_107     ((uint32_t)0x08035800)  /* Base @ of Page 107, 2 Kbytes */
#define FLASH_ADDR_PAGE_108     ((uint32_t)0x08036000)  /* Base @ of Page 108, 2 Kbytes */
#define FLASH_ADDR_PAGE_109     ((uint32_t)0x08036800)  /* Base @ of Page 109, 2 Kbytes */
#define FLASH_ADDR_PAGE_110     ((uint32_t)0x08037000)  /* Base @ of Page 110, 2 Kbytes */
#define FLASH_ADDR_PAGE_111     ((uint32_t)0x08037800)  /* Base @ of Page 111, 2 Kbytes */
#define FLASH_ADDR_PAGE_112     ((uint32_t)0x08038000)  /* Base @ of Page 112, 2 Kbytes */
#define FLASH_ADDR_PAGE_113     ((uint32_t)0x08038800)  /* Base @ of Page 113, 2 Kbytes */
#define FLASH_ADDR_PAGE_114     ((uint32_t)0x08039000)  /* Base @ of Page 114, 2 Kbytes */
#define FLASH_ADDR_PAGE_115     ((uint32_t)0x08039800)  /* Base @ of Page 115, 2 Kbytes */
#define FLASH_ADDR_PAGE_116     ((uint32_t)0x0803A000)  /* Base @ of Page 116, 2 Kbytes */
#define FLASH_ADDR_PAGE_117     ((uint32_t)0x0803A800)  /* Base @ of Page 117, 2 Kbytes */
#define FLASH_ADDR_PAGE_118     ((uint32_t)0x0803B000)  /* Base @ of Page 118, 2 Kbytes */
#define FLASH_ADDR_PAGE_119     ((uint32_t)0x0803B800)  /* Base @ of Page 119, 2 Kbytes */
#define FLASH_ADDR_PAGE_120     ((uint32_t)0x0803C000)  /* Base @ of Page 120, 2 Kbytes */
#define FLASH_ADDR_PAGE_121     ((uint32_t)0x0803C800)  /* Base @ of Page 121, 2 Kbytes */
#define FLASH_ADDR_PAGE_122     ((uint32_t)0x0803D000)  /* Base @ of Page 122, 2 Kbytes */
#define FLASH_ADDR_PAGE_123     ((uint32_t)0x0803D800)  /* Base @ of Page 123, 2 Kbytes */
#define FLASH_ADDR_PAGE_124     ((uint32_t)0x0803E000)  /* Base @ of Page 124, 2 Kbytes */
#define FLASH_ADDR_PAGE_125     ((uint32_t)0x0803E800)  /* Base @ of Page 125, 2 Kbytes */
#define FLASH_ADDR_PAGE_126     ((uint32_t)0x0803F000)  /* Base @ of Page 126, 2 Kbytes */
#define FLASH_ADDR_PAGE_127     ((uint32_t)0x0803F800)  /* Base @ of Page 127, 2 Kbytes */

/**
  * @brief  Base address of the Flash pages under dual bank mode for category 3 devices Bank 2
  *         Dual bank mode(2KB each page)
  *         For 256KB devices: from page 0 to page 63
  *         For 128KB devices: from page 0 to page 31
  *         Single bank mode(4KB each page)
  *         For 256KB devices: from page 0 to page 63
  *         For 128KB devices: from page 0 to page 31
  */
#define FLASH_ADDR_PAGE_128     ((uint32_t)0x08040000)  /* Base @ of Page 128, 2 Kbytes */
#define FLASH_ADDR_PAGE_129     ((uint32_t)0x08040800)  /* Base @ of Page 129, 2 Kbytes */
#define FLASH_ADDR_PAGE_130     ((uint32_t)0x08041000)  /* Base @ of Page 130, 2 Kbytes */
#define FLASH_ADDR_PAGE_131     ((uint32_t)0x08041800)  /* Base @ of Page 131, 2 Kbytes */
#define FLASH_ADDR_PAGE_132     ((uint32_t)0x08042000)  /* Base @ of Page 132, 2 Kbytes */
#define FLASH_ADDR_PAGE_133     ((uint32_t)0x08042800)  /* Base @ of Page 133, 2 Kbytes */
#define FLASH_ADDR_PAGE_134     ((uint32_t)0x08043000)  /* Base @ of Page 134, 2 Kbytes */
#define FLASH_ADDR_PAGE_135     ((uint32_t)0x08043800)  /* Base @ of Page 135, 2 Kbytes */
#define FLASH_ADDR_PAGE_136     ((uint32_t)0x08044000)  /* Base @ of Page 136, 2 Kbytes */
#define FLASH_ADDR_PAGE_137     ((uint32_t)0x08044800)  /* Base @ of Page 137, 2 Kbytes */
#define FLASH_ADDR_PAGE_138     ((uint32_t)0x08045000)  /* Base @ of Page 138, 2 Kbytes */
#define FLASH_ADDR_PAGE_139     ((uint32_t)0x08045800)  /* Base @ of Page 139, 2 Kbytes */
#define FLASH_ADDR_PAGE_140     ((uint32_t)0x08046000)  /* Base @ of Page 140, 2 Kbytes */
#define FLASH_ADDR_PAGE_141     ((uint32_t)0x08046800)  /* Base @ of Page 141, 2 Kbytes */
#define FLASH_ADDR_PAGE_142     ((uint32_t)0x08047000)  /* Base @ of Page 142, 2 Kbytes */
#define FLASH_ADDR_PAGE_143     ((uint32_t)0x08047800)  /* Base @ of Page 143, 2 Kbytes */
#define FLASH_ADDR_PAGE_144     ((uint32_t)0x08048000)  /* Base @ of Page 144, 2 Kbytes */
#define FLASH_ADDR_PAGE_145     ((uint32_t)0x08048800)  /* Base @ of Page 145, 2 Kbytes */
#define FLASH_ADDR_PAGE_146     ((uint32_t)0x08049000)  /* Base @ of Page 146, 2 Kbytes */
#define FLASH_ADDR_PAGE_147     ((uint32_t)0x08049800)  /* Base @ of Page 147, 2 Kbytes */
#define FLASH_ADDR_PAGE_148     ((uint32_t)0x0804a000)  /* Base @ of Page 148, 2 Kbytes */
#define FLASH_ADDR_PAGE_149     ((uint32_t)0x0804a800)  /* Base @ of Page 149, 2 Kbytes */
#define FLASH_ADDR_PAGE_150     ((uint32_t)0x0804b000)  /* Base @ of Page 150, 2 Kbytes */
#define FLASH_ADDR_PAGE_151     ((uint32_t)0x0804b800)  /* Base @ of Page 151, 2 Kbytes */
#define FLASH_ADDR_PAGE_152     ((uint32_t)0x0804c000)  /* Base @ of Page 152, 2 Kbytes */
#define FLASH_ADDR_PAGE_153     ((uint32_t)0x0804c800)  /* Base @ of Page 153, 2 Kbytes */
#define FLASH_ADDR_PAGE_154     ((uint32_t)0x0804d000)  /* Base @ of Page 154, 2 Kbytes */
#define FLASH_ADDR_PAGE_155     ((uint32_t)0x0804d800)  /* Base @ of Page 155, 2 Kbytes */
#define FLASH_ADDR_PAGE_156     ((uint32_t)0x0804e000)  /* Base @ of Page 156, 2 Kbytes */
#define FLASH_ADDR_PAGE_157     ((uint32_t)0x0804e800)  /* Base @ of Page 157, 2 Kbytes */
#define FLASH_ADDR_PAGE_158     ((uint32_t)0x0804f000)  /* Base @ of Page 158, 2 Kbytes */
#define FLASH_ADDR_PAGE_159     ((uint32_t)0x0804f800)  /* Base @ of Page 159, 2 Kbytes */
#define FLASH_ADDR_PAGE_160     ((uint32_t)0x08050000)  /* Base @ of Page 160, 2 Kbytes */
#define FLASH_ADDR_PAGE_161     ((uint32_t)0x08050800)  /* Base @ of Page 161, 2 Kbytes */
#define FLASH_ADDR_PAGE_162     ((uint32_t)0x08051000)  /* Base @ of Page 162, 2 Kbytes */
#define FLASH_ADDR_PAGE_163     ((uint32_t)0x08051800)  /* Base @ of Page 163, 2 Kbytes */
#define FLASH_ADDR_PAGE_164     ((uint32_t)0x08052000)  /* Base @ of Page 164, 2 Kbytes */
#define FLASH_ADDR_PAGE_165     ((uint32_t)0x08052800)  /* Base @ of Page 165, 2 Kbytes */
#define FLASH_ADDR_PAGE_166     ((uint32_t)0x08053000)  /* Base @ of Page 166, 2 Kbytes */
#define FLASH_ADDR_PAGE_167     ((uint32_t)0x08053800)  /* Base @ of Page 167, 2 Kbytes */
#define FLASH_ADDR_PAGE_168     ((uint32_t)0x08054000)  /* Base @ of Page 168, 2 Kbytes */
#define FLASH_ADDR_PAGE_169     ((uint32_t)0x08054800)  /* Base @ of Page 169, 2 Kbytes */
#define FLASH_ADDR_PAGE_170     ((uint32_t)0x08055000)  /* Base @ of Page 170, 2 Kbytes */
#define FLASH_ADDR_PAGE_171     ((uint32_t)0x08055800)  /* Base @ of Page 171, 2 Kbytes */
#define FLASH_ADDR_PAGE_172     ((uint32_t)0x08056000)  /* Base @ of Page 172, 2 Kbytes */
#define FLASH_ADDR_PAGE_173     ((uint32_t)0x08056800)  /* Base @ of Page 173, 2 Kbytes */
#define FLASH_ADDR_PAGE_174     ((uint32_t)0x08057000)  /* Base @ of Page 174, 2 Kbytes */
#define FLASH_ADDR_PAGE_175     ((uint32_t)0x08057800)  /* Base @ of Page 175, 2 Kbytes */
#define FLASH_ADDR_PAGE_176     ((uint32_t)0x08058000)  /* Base @ of Page 176, 2 Kbytes */
#define FLASH_ADDR_PAGE_177     ((uint32_t)0x08058800)  /* Base @ of Page 177, 2 Kbytes */
#define FLASH_ADDR_PAGE_178     ((uint32_t)0x08059000)  /* Base @ of Page 178, 2 Kbytes */
#define FLASH_ADDR_PAGE_179     ((uint32_t)0x08059800)  /* Base @ of Page 179, 2 Kbytes */
#define FLASH_ADDR_PAGE_180     ((uint32_t)0x0805a000)  /* Base @ of Page 180, 2 Kbytes */
#define FLASH_ADDR_PAGE_181     ((uint32_t)0x0805a800)  /* Base @ of Page 181, 2 Kbytes */
#define FLASH_ADDR_PAGE_182     ((uint32_t)0x0805b000)  /* Base @ of Page 182, 2 Kbytes */
#define FLASH_ADDR_PAGE_183     ((uint32_t)0x0805b800)  /* Base @ of Page 183, 2 Kbytes */
#define FLASH_ADDR_PAGE_184     ((uint32_t)0x0805c000)  /* Base @ of Page 184, 2 Kbytes */
#define FLASH_ADDR_PAGE_185     ((uint32_t)0x0805c800)  /* Base @ of Page 185, 2 Kbytes */
#define FLASH_ADDR_PAGE_186     ((uint32_t)0x0805d000)  /* Base @ of Page 186, 2 Kbytes */
#define FLASH_ADDR_PAGE_187     ((uint32_t)0x0805d800)  /* Base @ of Page 187, 2 Kbytes */
#define FLASH_ADDR_PAGE_188     ((uint32_t)0x0805e000)  /* Base @ of Page 188, 2 Kbytes */
#define FLASH_ADDR_PAGE_189     ((uint32_t)0x0805e800)  /* Base @ of Page 189, 2 Kbytes */
#define FLASH_ADDR_PAGE_190     ((uint32_t)0x0805f000)  /* Base @ of Page 190, 2 Kbytes */
#define FLASH_ADDR_PAGE_191     ((uint32_t)0x0805f800)  /* Base @ of Page 191, 2 Kbytes */
#define FLASH_ADDR_PAGE_192     ((uint32_t)0x08060000)  /* Base @ of Page 192, 2 Kbytes */
#define FLASH_ADDR_PAGE_193     ((uint32_t)0x08060800)  /* Base @ of Page 193, 2 Kbytes */
#define FLASH_ADDR_PAGE_194     ((uint32_t)0x08061000)  /* Base @ of Page 194, 2 Kbytes */
#define FLASH_ADDR_PAGE_195     ((uint32_t)0x08061800)  /* Base @ of Page 195, 2 Kbytes */
#define FLASH_ADDR_PAGE_196     ((uint32_t)0x08062000)  /* Base @ of Page 196, 2 Kbytes */
#define FLASH_ADDR_PAGE_197     ((uint32_t)0x08062800)  /* Base @ of Page 197, 2 Kbytes */
#define FLASH_ADDR_PAGE_198     ((uint32_t)0x08063000)  /* Base @ of Page 198, 2 Kbytes */
#define FLASH_ADDR_PAGE_199     ((uint32_t)0x08063800)  /* Base @ of Page 199, 2 Kbytes */
#define FLASH_ADDR_PAGE_200     ((uint32_t)0x08064000)  /* Base @ of Page 200, 2 Kbytes */
#define FLASH_ADDR_PAGE_201     ((uint32_t)0x08064800)  /* Base @ of Page 201, 2 Kbytes */
#define FLASH_ADDR_PAGE_202     ((uint32_t)0x08065000)  /* Base @ of Page 202, 2 Kbytes */
#define FLASH_ADDR_PAGE_203     ((uint32_t)0x08065800)  /* Base @ of Page 203, 2 Kbytes */
#define FLASH_ADDR_PAGE_204     ((uint32_t)0x08066000)  /* Base @ of Page 204, 2 Kbytes */
#define FLASH_ADDR_PAGE_205     ((uint32_t)0x08066800)  /* Base @ of Page 205, 2 Kbytes */
#define FLASH_ADDR_PAGE_206     ((uint32_t)0x08067000)  /* Base @ of Page 206, 2 Kbytes */
#define FLASH_ADDR_PAGE_207     ((uint32_t)0x08067800)  /* Base @ of Page 207, 2 Kbytes */
#define FLASH_ADDR_PAGE_208     ((uint32_t)0x08068000)  /* Base @ of Page 208, 2 Kbytes */
#define FLASH_ADDR_PAGE_209     ((uint32_t)0x08068800)  /* Base @ of Page 209, 2 Kbytes */
#define FLASH_ADDR_PAGE_210     ((uint32_t)0x08069000)  /* Base @ of Page 210, 2 Kbytes */
#define FLASH_ADDR_PAGE_211     ((uint32_t)0x08069800)  /* Base @ of Page 211, 2 Kbytes */
#define FLASH_ADDR_PAGE_212     ((uint32_t)0x0806a000)  /* Base @ of Page 212, 2 Kbytes */
#define FLASH_ADDR_PAGE_213     ((uint32_t)0x0806a800)  /* Base @ of Page 213, 2 Kbytes */
#define FLASH_ADDR_PAGE_214     ((uint32_t)0x0806b000)  /* Base @ of Page 214, 2 Kbytes */
#define FLASH_ADDR_PAGE_215     ((uint32_t)0x0806b800)  /* Base @ of Page 215, 2 Kbytes */
#define FLASH_ADDR_PAGE_216     ((uint32_t)0x0806c000)  /* Base @ of Page 216, 2 Kbytes */
#define FLASH_ADDR_PAGE_217     ((uint32_t)0x0806c800)  /* Base @ of Page 217, 2 Kbytes */
#define FLASH_ADDR_PAGE_218     ((uint32_t)0x0806d000)  /* Base @ of Page 218, 2 Kbytes */
#define FLASH_ADDR_PAGE_219     ((uint32_t)0x0806d800)  /* Base @ of Page 219, 2 Kbytes */
#define FLASH_ADDR_PAGE_220     ((uint32_t)0x0806e000)  /* Base @ of Page 220, 2 Kbytes */
#define FLASH_ADDR_PAGE_221     ((uint32_t)0x0806e800)  /* Base @ of Page 221, 2 Kbytes */
#define FLASH_ADDR_PAGE_222     ((uint32_t)0x0806f000)  /* Base @ of Page 222, 2 Kbytes */
#define FLASH_ADDR_PAGE_223     ((uint32_t)0x0806f800)  /* Base @ of Page 223, 2 Kbytes */
#define FLASH_ADDR_PAGE_224     ((uint32_t)0x08070000)  /* Base @ of Page 224, 2 Kbytes */
#define FLASH_ADDR_PAGE_225     ((uint32_t)0x08070800)  /* Base @ of Page 225, 2 Kbytes */
#define FLASH_ADDR_PAGE_226     ((uint32_t)0x08071000)  /* Base @ of Page 226, 2 Kbytes */
#define FLASH_ADDR_PAGE_227     ((uint32_t)0x08071800)  /* Base @ of Page 227, 2 Kbytes */
#define FLASH_ADDR_PAGE_228     ((uint32_t)0x08072000)  /* Base @ of Page 228, 2 Kbytes */
#define FLASH_ADDR_PAGE_229     ((uint32_t)0x08072800)  /* Base @ of Page 229, 2 Kbytes */
#define FLASH_ADDR_PAGE_230     ((uint32_t)0x08073000)  /* Base @ of Page 230, 2 Kbytes */
#define FLASH_ADDR_PAGE_231     ((uint32_t)0x08073800)  /* Base @ of Page 231, 2 Kbytes */
#define FLASH_ADDR_PAGE_232     ((uint32_t)0x08074000)  /* Base @ of Page 232, 2 Kbytes */
#define FLASH_ADDR_PAGE_233     ((uint32_t)0x08074800)  /* Base @ of Page 233, 2 Kbytes */
#define FLASH_ADDR_PAGE_234     ((uint32_t)0x08075000)  /* Base @ of Page 234, 2 Kbytes */
#define FLASH_ADDR_PAGE_235     ((uint32_t)0x08075800)  /* Base @ of Page 235, 2 Kbytes */
#define FLASH_ADDR_PAGE_236     ((uint32_t)0x08076000)  /* Base @ of Page 236, 2 Kbytes */
#define FLASH_ADDR_PAGE_237     ((uint32_t)0x08076800)  /* Base @ of Page 237, 2 Kbytes */
#define FLASH_ADDR_PAGE_238     ((uint32_t)0x08077000)  /* Base @ of Page 238, 2 Kbytes */
#define FLASH_ADDR_PAGE_239     ((uint32_t)0x08077800)  /* Base @ of Page 239, 2 Kbytes */
#define FLASH_ADDR_PAGE_240     ((uint32_t)0x08078000)  /* Base @ of Page 240, 2 Kbytes */
#define FLASH_ADDR_PAGE_241     ((uint32_t)0x08078800)  /* Base @ of Page 241, 2 Kbytes */
#define FLASH_ADDR_PAGE_242     ((uint32_t)0x08079000)  /* Base @ of Page 242, 2 Kbytes */
#define FLASH_ADDR_PAGE_243     ((uint32_t)0x08079800)  /* Base @ of Page 243, 2 Kbytes */
#define FLASH_ADDR_PAGE_244     ((uint32_t)0x0807a000)  /* Base @ of Page 244, 2 Kbytes */
#define FLASH_ADDR_PAGE_245     ((uint32_t)0x0807a800)  /* Base @ of Page 245, 2 Kbytes */
#define FLASH_ADDR_PAGE_246     ((uint32_t)0x0807b000)  /* Base @ of Page 246, 2 Kbytes */
#define FLASH_ADDR_PAGE_247     ((uint32_t)0x0807b800)  /* Base @ of Page 247, 2 Kbytes */
#define FLASH_ADDR_PAGE_248     ((uint32_t)0x0807c000)  /* Base @ of Page 248, 2 Kbytes */
#define FLASH_ADDR_PAGE_249     ((uint32_t)0x0807c800)  /* Base @ of Page 249, 2 Kbytes */
#define FLASH_ADDR_PAGE_250     ((uint32_t)0x0807d000)  /* Base @ of Page 250, 2 Kbytes */
#define FLASH_ADDR_PAGE_251     ((uint32_t)0x0807d800)  /* Base @ of Page 251, 2 Kbytes */
#define FLASH_ADDR_PAGE_252     ((uint32_t)0x0807e000)  /* Base @ of Page 252, 2 Kbytes */
#define FLASH_ADDR_PAGE_253     ((uint32_t)0x0807e800)  /* Base @ of Page 253, 2 Kbytes */
#define FLASH_ADDR_PAGE_254     ((uint32_t)0x0807f000)  /* Base @ of Page 254, 2 Kbytes */
#define FLASH_ADDR_PAGE_255     ((uint32_t)0x0807f800)  /* Base @ of Page 255, 2 Kbytes */

/**
  * @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)

#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))

/**
  * @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);
uint32_t FLASH_GetAddrPageIdx(uint32_t Addr);
uint32_t FLASH_GetAddrPageOffset(uint32_t StartAddr, uint32_t EndAddr);
uint32_t FLASH_GetAddrPageBank(uint32_t Addr);

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_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 -------------------------------------------------------------*/
/* 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  Gets the page index of a given address
  * @param  Addr: Address of the FLASH Memory
  * @retval The page index of a given address
  */
uint32_t FLASH_GetAddrPageIdx(uint32_t Addr)
{
    uint32_t PageIdx = 0;

    if (Addr < (FLASH_BASE + FLASH_BANK_SIZE))
    {
        /* Bank 1 */
        PageIdx = (Addr - FLASH_BASE) / FLASH_PAGE_SIZE;
    }
    else
    {
        /* Bank 2 */
        PageIdx = (Addr - (FLASH_BASE + FLASH_BANK_SIZE)) / FLASH_PAGE_SIZE;
    }

    return PageIdx;
}

/**
  * @brief  Gets the bank of a given address
  * @param  Addr: Address of the FLASH Memory
  * @retval The bank of a given address
  */
uint32_t FLASH_GetAddrPageBank(uint32_t Addr)
{
    if (Addr < (FLASH_BASE + FLASH_BANK_SIZE))
    {
        /* Bank 1 */
        return FLASH_BANK_1;
    }
    else
    {
        /* Bank 2 */
        return FLASH_BANK_2;
    }
}

/**
  * @brief  Gets the page index offset of the given address
  * @param  StartAddr: Address of the FLASH Memory
  * @param  EndAddr: Address of the FLASH Memory
  * @retval The page index offset of the given address
  */
uint32_t FLASH_GetAddrPageOffset(uint32_t StartAddr, uint32_t EndAddr)
{
    uint32_t PageNum = 0;

    PageNum = FLASH_GetAddrPageIdx(EndAddr) - FLASH_GetAddrPageIdx(StartAddr) + 1;

    return PageNum;
}

/**
  * @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);
}

上面是我修改的HAL库Flash驱动,大部分功能还没有验证,希望大家帮忙测试,提出问题,一起完善。

下面给出我的结构体读写程序,因为是项目里的,我就不全部放出来了,大家按需修改,我是参考官方flash擦除编程的DEMO,因为只支持双字写入,稍稍花了点时间。本着授人以鱼不如授人以渔的态度,希望大家自己修改,复制粘贴学不到啥东西,共勉。有错误,或者有更好的代码,希望大家也教教我,学无止境。就比如判断写入是否正常那里,我试了直接读取和比较内存空间2种方法,应该是都可以的,因为我读配置也是这样读上来的。我不喜欢设置粉丝可见,如果觉得有用,点个赞或者点个收藏,谢谢啦。

#define PID_CFG_FLASH_ADDR_START    FLASH_ADDR_PAGE_31 // Page31 in DBANK mode
#define PID_CFG_FLASH_ADDR_END      (FLASH_ADDR_PAGE_31 + FLASH_PAGE_SIZE - 1U) // 1Page 2KB 
#define PID_CFG_FLASH_PAGE_IDX      31U
#define PID_CFG_FLASH_PAGE_NUM      1U

#define PID_CFG_STRUCT_SIZE64       (sizeof(PID_ObjectTypeDef) % 8U ? \
                                        sizeof(PID_ObjectTypeDef) / 8U : \
                                        sizeof(PID_ObjectTypeDef) / 8U + 1)

typedef struct
{
    float P;
    float I;
    float D;

    int ILimitMIN;
    int ILimitMAX;
    int ISeparate;
    int Accuracy;
    int Mode;
    int Waveform;
    int Enable;

    int OutputMAX;
    int OutputMED;
    int OutputMIN;
    int OutputInterval;
    int OutputPolarity;

    int SPInputMAX;
    int SPInputMIN;
    int FBInputMAX;
    int FBInputMIN;

    int FilterMethod;
    int FilterSampleNum;

    int AnalogInputCH1;
    int AnalogInputCH2;
    int AnalogOutput;

    int DigitalInputCH1;
    int DigitalInputCH2;
    int DigitalOutputCH1;
    int DigitalOutputCH2;
} PID_ObjectTypeDef;

int PID_CntlrConfigSave(void)
{
    int Ret = ERROR;
    uint8_t Idx = 0;
    uint32_t PageErr = 0;
    uint32_t WriteAddr = PID_CFG_FLASH_ADDR_START;

    FLASH_EraseInitTypeDef FlashErase =
    {
        .TypeErase = FLASH_TYPEERASE_PAGES,
        .Banks = FLASH_BANK_1,
        .Page = PID_CFG_FLASH_PAGE_IDX,
        .NbPages = PID_CFG_FLASH_PAGE_NUM,
//        .Banks = FLASH_GetAddrPageBank(PID_CFG_FLASH_ADDR_START),
//        .Page = FLASH_GetAddrPageIdx(PID_CFG_FLASH_ADDR_START),
//        .NbPages = FLASH_GetAddrPageOffset(PID_CFG_FLASH_ADDR_START, PID_CFG_FLASH_ADDR_END),
    };

    /* Unlock the Flash to enable the flash control register access *************/
    FLASH_Unlock();
    /* Clear OPTVERR bit set on virgin samples */
    FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
    /* Erase the user Flash area
    (area defined by PID_CFG_FLASH_ADDR_START and PID_CFG_FLASH_ADDR_END) *******/
    /* Note: If an erase operation in Flash memory also concerns data in the data or instruction cache,
       you have to make sure that these data are rewritten before they are accessed during code
       execution. If this cannot be done safely, it is recommended to flush the caches by setting the
       DCRST and ICRST bits in the FLASH_CR register. */
    if (FLASH_Erase(&FlashErase, &PageErr) != FLASH_OK)
    {
        /*
          Error occurred while page erase.
          User can add here some code to deal with this error.
          PageError will contain the faulty page and then to know the code error on this page,
          user can call function 'FLASH_GetError()'
        */
        PID_LOG_PRINT("Flash erase error code: %x, page: %x.\r\n", FLASH_GetError(), PageErr);
        FLASH_Lock();
        return Ret;
    }

    /* Program the user Flash area word by word
    (area defined by PID_CFG_FLASH_ADDR_START and PID_CFG_FLASH_ADDR_END) *******/
    for (Idx = 0; Idx < PID_CFG_STRUCT_SIZE64; Idx++)
    {
        if (FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, WriteAddr, *((uint64_t *)&PID + Idx)) != FLASH_OK)
        {
            /* Error occurred while writing data in Flash memory.
             User can add here some code to deal with this error */
            FLASH_Lock();
            return Ret;
        }
        WriteAddr += 8; /* Increment to next double word*/
    }
    /* Lock the Flash to disable the flash control register access (recommended
       to protect the FLASH memory against possible unwanted operation) *********/
    FLASH_Lock();

    /* Check if the programmed data is OK*/
//    PID_ObjectTypeDef PIDTmp = *(__IO PID_ObjectTypeDef *)ReadAddr;

    if (!memcmp(&PID, (uint32_t *)PID_CFG_FLASH_ADDR_START, sizeof(PID_ObjectTypeDef)))
    {
        Ret = SUCCESS;
    }

    return Ret;
}

物联沃分享整理
物联沃-IOTWORD物联网 » 使用LL库下的STM32G4x FLASH读写配置结构体详解

发表评论