42. 读写内部FLASH

42.1. RA MCU的内部FLASH存储器

RA MCU内部的 Flash 存储器包含代码闪存(Code Flash)、 数据闪存(Data Flash)、选项设置存储器(Option-Setting Memory)和工厂闪存(Factory Flash)。 其中,代码闪存用于保存用户代码,其中包含用户的 Boot 启动代码; 数据闪存用于保存用户静态数据;选项设置存储器用于保存和变更 MCU 复位后的一些状态; 工厂闪存保存了芯片出厂自带的一段固定的 BootLoader 程序,用户无法访问。

RA6M5 的 Flash 的地址分布及范围大小见下表。

RA6M5 内部 Flash 存储器

Flash 所在地址范围

Flash 存储器

Flash 空间大小

0x0000_0000 ~ 0x002F_FFFF

代码闪存(Code Flash)

最大 2 MB

0x0100_80F0 ~ 0x0100_81B3

工厂闪存(Factory Flash)

-

0x0100_A100 ~ 0x0100_A2FF

选项设置存储器(Option-Setting Memory)

512 Bytes

0x0800_0000 ~ 0x0800_1FFF

数据闪存(Data Flash)

8 KB

RA4M2 的 Flash 的地址分布及范围大小见下表。

RA4M2 内部 Flash 存储器

Flash 所在地址范围

Flash 存储器

Flash 空间大小

0x0000_0000 ~ 0x0007_FFFF

代码闪存(Code Flash)

最大 512 KB

0x0100_80F0 ~ 0x0100_81B3

工厂闪存(Factory Flash)

-

0x0100_A100 ~ 0x0100_A2FF

选项设置存储器(Option-Setting Memory)

512 Bytes

0x0800_0000 ~ 0x0800_1FFF

数据闪存(Data Flash)

8 KB

RA2L1 的 Flash 的地址分布及范围大小见下表。

RA2L1 内部 Flash 存储器

Flash 所在地址范围

Flash 存储器

Flash 空间大小

0x0000_0000 ~ 0x0003_FFFF

代码闪存(Code Flash)

最大 256 KB

0x4010_0000 ~ 0x4010_1FFF

数据闪存(Data Flash)

8 KB

注:
  • 野火启明6M5开发板的 RA6M5 芯片的 Code Flash 大小是 2 MB。 也有部分型号的 RA6M5 的 Code Flash 大小是 1.5 MB 或 1 MB 的。

  • 野火启明4M2开发板的 RA4M2 芯片的 Code Flash 大小是 512 KB。 也有部分型号的 RA4M2 的 Code Flash 大小是 384 KB 或 256 KB 的。

  • 野火启明2L1开发板的 RA2L1 芯片的 Code Flash 大小是 256 KB。 也有部分型号的 RA2L1 的 Code Flash 大小是 128 KB 的。

本章节重点讲解如何使用代码闪存和数据闪存,对于选项设置存储器感兴趣的读者可自行研究。

42.1.1. 代码闪存和数据闪存

启明6M5开发板板载的 RA6M5 芯片(R7FA6M5BH3CFC)内部具有空间大小为 2 Mbytes 的用户代码闪存区域,主要用于存放用户代码,代码一般从代码闪存执行。 RA6M5 同时还具有空间大小为 8 Kbytes 的用户数据闪存区域, 数据闪存更适合用于存储数据,它可以被擦除和写入,而且相对于代码闪存,数据闪存具有更多的重新编程/擦除周期。

启明6M5开发板板载的 RA6M5 型号的代码闪存和数据闪存的对比如下:

启明6M5:代码闪存 vs 数据闪存

对比项

Code flash memory

Data flash memory

存储容量

2 Mbytes

8 Kbytes

擦除后的值

0xFF

Undefined(不确定)

地址区域

● Linear 模式下当做一块区域来使用

0x0000_0000 - 0x001F_FFFF

● Dual 模式下分成两块区域来使用

0x0000_0000 - 0x000F_FFFF

0x0020_0000 - 0x002F_FFFF

0x0800_0000 - 0x0800_1FFF

编程和擦除单元

● 编程单元为 128 bytes

● 擦除单元为 1 block (8 KB or 32 KB)

● 编程单元为 4/8/16 bytes

● 擦除单元为 64/128/256 bytes

启明4M2开发板板载的 RA4M2 型号的代码闪存和数据闪存的对比如下:

启明4M2:代码闪存 vs 数据闪存

对比项

Code flash memory

Data flash memory

存储容量

512 Kbytes

8 Kbytes

擦除后的值

0xFF

Undefined(不确定)

地址区域

0x0000_0000 - 0x0007_FFFF

0x0800_0000 - 0x0800_1FFF

编程和擦除单元

● 编程单元为 128 bytes

● 擦除单元为 1 block (8 KB or 32 KB)

● 编程单元为 4/8/16 bytes

● 擦除单元为 64/128/256 bytes

启明2L1开发板板载的 RA2L1 型号的代码闪存和数据闪存的对比如下:

启明2L1:代码闪存 vs 数据闪存

对比项

Code flash memory

Data flash memory

存储容量

256 Kbytes

8 Kbytes

擦除后的值

0xFF

Undefined(不确定)

地址区域

0x0000_0000 - 0x0003_FFFF

0x4010_0000 ~ 0x4010_1FFF

编程和擦除单元

● 编程单元为 4 bytes

● 擦除单元为 2 Kbytes

● 编程单元为 1 bytes

● 擦除单元为 1 Kbytes

42.1.2. 代码闪存和数据闪存的结构

42.1.2.1. RA6M5 的代码闪存

RA6M5 的代码闪存具有线性模式(Linear Mode)和双重模式(Dual Mode)的功能, 在这两种模式下代码闪存分别对应使用两种不同的地址映射方式。 代码闪存还具有启动区交换(Boot Swap)、块交换(Block Swap)等高级功能。

无论是在 Linear 还是在 Dual 模式下,代码闪存都是由大小为 8 Kbytes 和 32 Kbytes 的块(Block)组成。 在对代码闪存进行擦除操作时,需要按块进行擦除,也就是说至少需要擦除 8 Kbytes 或 32 Kbytes 大小的区域。 代码闪存擦除后的值为 0xFF。

RA6M5 的 Code Flash 在 线性模式(Linear Mode) 下的结构如下图所示:

图

RA6M5 的 Code Flash 在 双重模式(Dual Mode) 下的结构如下图所示:

图

42.1.2.2. RA4M2 的代码闪存

RA4M2 的代码闪存也是由大小为 8 Kbytes 和 32 Kbytes 的块(Block)组成。 在对代码闪存进行擦除操作时,需要按块进行擦除,也就是说至少需要擦除 8 Kbytes 或 32 Kbytes 大小的区域。 代码闪存擦除后的值为 0xFF。

RA4M2 的 Code Flash 的固定结构如下图所示:

图

42.1.2.3. RA6M5 和 RA4M2 数据闪存

RA6M5 和 RA4M2 的数据闪存空间大小都只有 8 Kbytes,用于保存用户数据。 两者的数据闪存都由大小为 64 Bytes 的块(Block)组成,擦除单元为 64/128/256 bytes。

注意:数据闪存擦除后的值为不确定值,而不能保证为 0xFF。 应使用空白检查来确定内存是否已被擦除但尚未编程。

RA6M5 和 RA4M2 的 Data Flash 的结构如下图所示:

图

42.1.2.4. RA2L1 的代码闪存

RA2L1 的代码闪存是由大小为 2 Kbytes 的块(Block)组成。 在对代码闪存进行擦除操作时,需要按块进行擦除,需要擦除 2 Kbytes 大小的区域。 代码闪存擦除后的值为 0xFF。

RA2L1 的 Code Flash 的固定结构如下图所示:

图

42.1.2.5. RA2L1 数据闪存

RA2L1 的数据闪存空间大小也是 8 Kbytes,用于保存用户数据。 与 RA6M5 和 RA4M2 的不同,RA2L1 的数据闪存由大小为 1 KB 的块(Block)组成,擦除单元为 1 KB。

RA2L1 的 Data Flash 的结构如下图所示:

图

42.1.3. 代码闪存和数据闪存的后台操作(BGO)

RA MCU 的内部Flash模块具有后台操作(Background Operation (BGO))的功能。 比如,当使用数据闪存并启用后台操作(BGO)模式时,仍然可以访问用户 ROM、RAM 和外部存储器。

需要注意,RA MCU 的不同子系列之间的 BGO 后台操作功能有所差别。

参考 RA MCU 的硬件手册,BGO 后台操作功能的简要描述如下:

RA6M5
  • The code flash memory can be read while the code flash memory is being programmed or erased.

  • The data flash memory can be read while the code flash memory is being programmed or erased.

  • The code flash memory can be read while the data flash memory is being programmed or erased.

RA4M2
  • The data flash memory can be read while the code flash memory is being programmed or erased.

  • The code flash memory can be read while the data flash memory is being programmed or erased.

RA2L1
  • Code flash memory can be read during data flash memory programming.

RA6M5RA4M2 它们的内部 Flash 后台操作的相同之处在于:

  • 当对代码闪存进行编程/擦除操作时,可以同时对数据闪存进行读操作。

  • 反过来,当对数据闪存进行编程/擦除操作时,可以同时对代码闪存进行读操作。

RA6M5 不同于 RA4M2 之处在于:

  • 当对代码闪存进行编程/擦除操作时,还可以同时对(不同分区的)代码闪存进行读操作。

然而实现这一点是有限制条件的,能够同时进行操作的必须是不同分区的代码闪存, 具体是如何划分的分区,请读者查阅 RA6M5 的硬件手册“Flash Memory”章节中的“Background Operation”小节, 其中有一张表“Conditions under which Background Operation is Usable”, 清晰地罗列了用户在操作内部 Flash 时可以使用后台操作的所有情况。

RA2L1 由于其内部 Flash 外设与 RA6M5/RA4M2 的差别比较大,所以其内部 Flash 的后台操作功能仅支持以下一点:

  • 当对数据闪存进行编程操作时,可以同时对代码闪存进行读操作

42.2. RA6M5/RA4M2 内部FLASH的框图分析

RA6M5 和 RA4M2 这两个RA子系列MCU的内部 FLASH 模块其结构是基本一致的。

与 RA6M5、RA4M2 内部 Flash 相关的模块框图如下图所示。

图

42.2.1. Flash定序器和FACI命令

如图中 ① 所示。

相关信号:

  • FCLK:Flash 时钟输入信号

  • FIFERR:Flash 接口错误中断信号引脚

  • FRDYI:Flash 就绪中断信号引脚

Flash 时钟源(FCLK)

Flash 时钟源是 Flash 外设在执行所有 Flash 操作时使用的时钟。 调用 flash_api_t::open 函数时,如果 Flash 时钟源无效,那么它将返回 FSP_ERR_FCLK。 一旦打开了 Flash API,如果 Flash 时钟源频率发生了更改, 则必须调用 flash_api_t::updateFlashClockFreq API函数来通知 API 已更改 Flash 时钟源频率。 不这样做可能导致 Flash 操作失败,并且可能损坏部件。

中断

仅当使用数据闪存 BGO 模式时才需要启用 Flash 就绪中断。 在这种模式下,应用程序可以启动数据闪存操作,然后使用用户提供的回调函数异步通知其完成或错误。 回调函数传递了一个包含事件信息的结构,这些信息指示回调事件的源(例如,flash_api_t::FLASH_EVENT_ERASE_COMPLETE)。 当 FLASH FRDYI 中断被启用时,相应的 ISR 将在 FLASH 驱动程序中定义。 如果用 flash_api_t::open API 注册了用户回调函数,ISR 将调用该函数。

FLASH HP 支持额外的 FLASH 错误中断, FLASH HP 启用 BGO 模式,那么 FLASH 就绪中断和 FLASH 错误中断都必须启用(分配优先级)。

Flash 定序器(Flash Sequencer)和 FACI 命令(FACI Commands)

FACI 根据特定的 FACI 命令控制 FCU。

下表列出了所有的 FACI 命令(FACI Commands):

FACI Commands

FACI 命令

功能

Program

编程用户代码闪存和数据闪存

Block erase

擦除用户代码闪存和数据闪存

Multi block erase

擦除数据闪存

P/E suspend

暂停编程或擦除过程

P/E resume

恢复编程或擦除过程

Status Clear

初始化Flash寄存器(FSTATR 和 FASTAT)

Forced Stop

强制停止 FACI 命令的执行,并且初始化Flash寄存器(FSTATR 和 FASTAT)

Blank Check

检查数据闪存是否是空白的

Configuration set

设置选项设置存储器(option-setting memory)

42.2.2. 内部Flash存储器

如图中 ② 所示。

此部分包含了用户可通过 Flash 接口操作的内部 Flash 存储器:

  • 代码闪存(Code Flash Memory)

  • 数据闪存(Data Flash Memory)

  • 选项设置存储器(Option-Setting Memory)

针对代码闪存,还有一个高速缓存器(Flash Cache)用来加速对代码闪存的访问操作。

42.2.3. FLBIU、FHBIU

如图中 ③ 所示。

CPU 通过 FLBIU、FHBIU 这两者来访问内部 Flash 存储器:

  • PLBIU(Peripheral Low speed Bus Interface Unit)是外设低速总线接口单元, CPU 可通过此访问数据闪存和 FACI。该总线接口的最大时钟频率为 FCLK 的 50 MHz

  • PHBIU(Peripheral High speed Bus Interface Unit)是外设高速总线接口单元, CPU 可通过此访问代码闪存(高速缓存器)和选项设置存储器。该总线接口的最大时钟频率为 ICLK 的 200 MHz

42.3. RA2L1 内部FLASH的框图分析

与 RA2L1 内部 Flash 有关的模块框图如下图所示。

图

42.3.1. Flash控制块

如图中 ① 所示。

Flash 控制块(Flash Control Block (FCB))其功能是实现 Flash 相关操作的逻辑, 与 RA6M5 和 RA4M2 的 Flash 定序器是类似的。

42.3.2. 内部Flash存储器

如图中 ② 所示。

此部分包含了用户可通过 Flash 接口操作的内部 Flash 存储器:

  • 代码闪存(Code Flash Memory)

  • 数据闪存(Data Flash Memory)

针对代码闪存,还有一个预取缓存器(Prefetch Buffer)用来加速对代码闪存的访问操作。

42.4. 内部FLASH的操作过程及使用注意事项

42.4.1. 内部FLASH的写入过程

擦除

先擦除 Block。 擦除操作将擦除所提供地址所在的整个块。

空白检查

对于 Data Flash,如果需要确认 Block 是否已经被擦除,一定是通过空白检查来判断 Block 是否空白, 而不能通过读取数据是否 0xFF 来判断,因为不能保证擦除的数据闪存块的读取值为0xFF。

一般在擦除操作成功后,也可以省略掉进行空白检查的步骤,直接对已擦除的区域进行下一步编程(写入)操作。

编程(写入)

将数据写入到 Flash。 写入操作必须在页边界上对齐,并且必须是页边界大小的倍数。

42.4.2. 内部FLASH的读取过程

无论 Code Flash 还是 Data Flash 都支持直接通过指定地址读取其中数据。 因此在读取内部FLASH数据时,把地址转换为C语言指针进行读取即可。

42.4.3. 内部FLASH的使用注意事项

注意事项:
  • 不可以对 Code Flash 或 Data Flash 连续两次进行编程(对于选项设置区域可以)。 若要编程(写入)已经被编程过了的区域,需要先擦除该目标区域。

  • 必须确保在数据闪存操作期间不访问数据闪存,这包括可能访问数据闪存的中断。

  • 不能保证擦除的数据闪存块的读取值为 0xFF。应使用空白检查来确定内存是否已被擦除但尚未编程。

代码闪存的使用注意事项

  • 在对代码闪存进行写入、擦除或空白检查时无法访问代码闪存。

  • 在修改访问窗口、选择启动区域或设置 ID 代码时,无法访问代码闪存。

  • 为了支持修改代码闪存,所有的支持代码必须驻留在 RAM。这只有在代码闪存编程启用时才能完成。

  • 代码闪存不支持 BGO 模式,因此代码闪存操作函数在完成之前不会返回。

  • 默认情况下,矢量表驻留在代码闪存中。如果在代码闪存操作期间发生中断, 那么代码闪存将被访问以获取中断的起始地址,并发生错误。最简单的解决方法是在代码闪存操作期间禁用中断。 另一种选择是将矢量表复制到 RAM,相应地更新 VTOR(矢量表偏移寄存器),并确保任何中断服务例程在 RAM 外执行。

  • 类似地,必须确保在多线程环境中,当代码闪存操作正在进行时,从代码闪存运行的线程不能变为活动线程。

42.5. 实验:读写内部FLASH

42.5.1. 新建工程

对于 e2 studio 开发环境:

拷贝一份我们之前的 e2s 工程 “19_UART_Receive_Send”, 然后将工程文件夹重命名为 “40_Internal_Flash”,最后再将它导入到我们的 e2 studio 工作空间中。

对于 Keil 开发环境:

拷贝一份我们之前的 Keil 工程 “19_UART_Receive_Send”, 然后将工程文件夹重命名为 “40_Internal_Flash”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。

工程新建好之后,在工程根目录的 “src” 文件夹下面新建 “internal_flash” 文件夹, 再进入 “internal_flash” 文件夹里面新建内部Flash驱动的源文件和头文件:“bsp_internal_flash.c” 和 “bsp_internal_flash.h”。 工程文件结构如下。

文件结构
40_Internal_Flash
├─ ......
└─ src
   ├─ led
   │  ├─ bsp_led.c
   │  └─ bsp_led.h
   ├─ debug_uart
   │  ├─ bsp_debug_uart.c
   │  └─ bsp_debug_uart.h
   ├─ internal_flash
   │  ├─ bsp_internal_flash.c
   │  └─ bsp_internal_flash.h
   └─ hal_entry.c

42.5.2. FSP配置

使用内部 Flash 不涉及到任何引脚,因此不需要配置引脚。 打开 FSP 配置界面,在“Stacks”配置页中加入 Flash 模块,按照如下图所示进行配置即可。

图

Flash的属性描述如下表所示。

Flash 属性介绍

Flash 属性

描述

Code Flash Programming Enable

选择Enabled,允许对 Code Flash 进行编程

Data Flash Programming Enable

选择Enabled,允许对 Data Flash 进行编程

Name

设置模块名字为 g_flash

Data Flash Background Operation

设置是否启用 Data Flash 的后台操作功能

Callback

设置后台操作的中断回调函数。这里设置为 flash_df_bgo_callback

Flash Ready Interrupt Priority

设置Flash就绪中断优先级。这里设置为优先级 2

Flash Error Interrupt Priority

设置Flash错误中断优先级。这里设置为优先级 2

42.5.3. 查看工程的FLASH空间分布

首先查看工程的FLASH空间分布, 确定未使用到的代码闪存区域,确保使用这些空闲区域内的存储空间时不会篡改应用程序空间的代码数据。

42.5.4. 内部FLASH相关宏定义

Code Flash 和 Data Flash 都有很多个 Block, 在例程的代码中,仅通过宏定义列出了部分的 Block,如下所示。 要注意,RA6M5 和 RA4M2 的 Code Flash 中存在两种不同大小的 Block, 一种大小为 8 KB,另一种大小为 32 KB。

代码清单 40‑1 FLASH相关宏定义
 /* Code Flash */
 #define FLASH_HP_CF_BLOCK_SIZE_32KB (32*1024)   /* Block Size 32 KB */
 #define FLASH_HP_CF_BLOCK_SIZE_8KB  (8*1024)    /* Block Size 8KB */

 #define FLASH_HP_CF_BLOCK_0         0x00000000U /*   8 KB: 0x00000000 - 0x00001FFF */
 #define FLASH_HP_CF_BLOCK_1         0x00002000U /*   8 KB: 0x00002000 - 0x00003FFF */
 #define FLASH_HP_CF_BLOCK_2         0x00004000U /*   8 KB: 0x00004000 - 0x00005FFF */
 #define FLASH_HP_CF_BLOCK_3         0x00006000U /*   8 KB: 0x00006000 - 0x00007FFF */
 #define FLASH_HP_CF_BLOCK_4         0x00008000U /*   8 KB: 0x00008000 - 0x00009FFF */
 #define FLASH_HP_CF_BLOCK_5         0x0000A000U /*   8 KB: 0x0000A000 - 0x0000BFFF */
 #define FLASH_HP_CF_BLOCK_6         0x0000C000U /*   8 KB: 0x0000C000 - 0x0000DFFF */
 #define FLASH_HP_CF_BLOCK_7         0x0000E000U /*   8 KB: 0x0000E000 - 0x0000FFFF */
 #define FLASH_HP_CF_BLOCK_8         0x00010000U /*  32 KB: 0x00010000 - 0x00017FFF */
 #define FLASH_HP_CF_BLOCK_9         0x00018000U /*  32 KB: 0x00018000 - 0x0001FFFF */
 #define FLASH_HP_CF_BLOCK_10        0x00020000U /*  32 KB: 0x00020000 - 0x00027FFF */
 #define FLASH_HP_CF_BLOCK_11        0x00028000U /*  32 KB: 0x00020000 - 0x00027FFF */
 #define FLASH_HP_CF_BLOCK_12        0x00030000U /*  32 KB: 0x00020000 - 0x00027FFF */
 #define FLASH_HP_CF_BLOCK_13        0x00038000U /*  32 KB: 0x00020000 - 0x00027FFF */
 #define FLASH_HP_CF_BLOCK_14        0x00040000U /*  32 KB: 0x00020000 - 0x00027FFF */
 //......


 /* Data Flash */
 #define FLASH_HP_DF_BLOCK_SIZE      (64)        /* Block Size 64B */

 #define FLASH_HP_DF_BLOCK_0         0x08000000U /* 大小为 64 B:  0x08000000U - 0x0800003F */
 #define FLASH_HP_DF_BLOCK_1         0x08000040U /* 大小为 64 B:  0x08000040U - 0x0800007F */
 #define FLASH_HP_DF_BLOCK_2         0x08000080U /* 大小为 64 B:  0x08000080U - 0x080000BF */
 #define FLASH_HP_DF_BLOCK_3         0x080000C0U /* 大小为 64 B:  0x080000C0U - 0x080000FF */


 /* CODE FLASH 测试 */
 #define CODE_FLASH_TEST_BLOCK       0x00100000U /* 避免覆盖程序代码数据 */
 #define CODE_FLASH_TEST_DATA_SIZE   128         /* 只测试128个字节数据 */
 /* DATA FLASH 测试 */
 #define DATA_FLASH_TEST_BLOCK       FLASH_HP_DF_BLOCK_0
 #define DATA_FLASH_TEST_DATA_SIZE   FLASH_HP_DF_BLOCK_SIZE

 /* 擦除的 CODE/DATA FLASH 的块数 */
 #define BLOCK_NUM   1   //2

42.5.5. 内部FLASH初始化函数

内部 FLASH 初始化函数如下所示,在函数 FLASH_HP_Init 中调用了 R_FLASH_HP_Open 进行 FLASH 初始化。

代码清单 40‑2 内部FLASH初始化函数
 void FLASH_HP_Init(void)
 {
     /* 打开 Flash_HP */
     fsp_err_t err = R_FLASH_HP_Open(&g_flash_ctrl, &g_flash_cfg);
     /* 处理错误 */
     if (FSP_SUCCESS != err)
     {
         printf("Flash_HP_Open API failed!\r\n");
         printf("Returned Error Code: 0x%x\r\n", err);
         while(1);
     }
 }

42.5.6. FLASH中断回调函数

FLASH 中断回调函数如下所示。

代码清单 40‑3 FLASH 中断回调函数
 volatile bool flash_erase_complete_flag = false;
 volatile bool flash_write_complete_flag = false;
 volatile bool flash_event_not_blank = false;
 volatile bool flash_event_blank = false;
 volatile bool flash_event_error = false;
 volatile flash_event_t flash_event_error_type;


 /* DATA FLASH 操作完成中断回调 */
 void flash_callback(flash_callback_args_t *p_args)
 {
     flash_event_t flash_event = p_args->event;

     if (FLASH_EVENT_ERASE_COMPLETE == flash_event)
     {
         flash_erase_complete_flag = true;
     }
     else if (FLASH_EVENT_WRITE_COMPLETE == flash_event)
     {
         flash_write_complete_flag = true;
     }
     else if (FLASH_EVENT_BLANK == flash_event)
     {
         flash_event_blank = true;
     }
     else if (FLASH_EVENT_NOT_BLANK == flash_event)
     {
         flash_event_not_blank = true;
     }
     else
     {
         flash_event_error = true;
         flash_event_error_type = flash_event;
     }
 }

42.5.7. CODE FLASH操作函数

CODE FLASH 操作函数如下所示。

代码清单 40‑4 CODE FLASH 操作函数
 /* Code Flash 操作函数 */
 void FLASH_HP_CodeFlash_Operation(void)
 {
     fsp_err_t err;
     flash_result_t blank_check_result = FLASH_RESULT_BLANK;
     uint8_t write_buffer[CODE_FLASH_TEST_DATA_SIZE] = {0};
     uint8_t read_buffer[CODE_FLASH_TEST_DATA_SIZE] = {0};
     uint8_t index;

     /* 写入测试数据到 write_buffer */
     for (index = 0; index < CODE_FLASH_TEST_DATA_SIZE; index++)
     {
         write_buffer[index] = (uint8_t) ('A' + (index % 26));
     }

     CODE_FLASH_PRINTF("擦除 Code Flash");

     /* 禁用中断,以避免P/E过程中产生中断导致操作失败
       注:这里禁用中断后,不能使用串口打印 */
     /* Disable interrupts to prevent vector table access while code flash is in P/E mode. */
     __disable_irq();
     err = R_FLASH_HP_Erase(&g_flash_ctrl, CODE_FLASH_TEST_BLOCK, BLOCK_NUM);
     /* Enable interrupts after code flash operations are complete. */
     __enable_irq();
     /* Error Handle */
     if (FSP_SUCCESS != err)
     {
         CODE_FLASH_PRINTF("Erase API failed");
         CODE_FLASH_PRINTF("Returned Error Code: 0x%x", err);
         while(1);
     }
     CODE_FLASH_PRINTF("Erase successful");


     /** 空白检查:擦除 Code Flash 完成后,进行空白检查
     *  对 Code Flash 进行空白检查,并不需要在硬件层面(FCU)的操作,
     *  实际上是用软件检查 Code Flash 指定区域的数据是否全为0xFF,
     *  这与对 Data Flash 进行空白检查的情况是不同的。
     */
     err = R_FLASH_HP_BlankCheck(&g_flash_ctrl, CODE_FLASH_TEST_BLOCK, FLASH_HP_CF_BLOCK_SIZE_8KB, &blank_check_result);
     /* Error Handle */
     if (FSP_SUCCESS != err)
     {
         CODE_FLASH_PRINTF("BlankCheck API failed");
         CODE_FLASH_PRINTF("Returned Error Code: 0x%x", err);
         while(1);
     }
     CODE_FLASH_PRINTF("BlankCheck API Successful");

     /* 验证空白检查的结果 */
     if (FLASH_RESULT_BLANK == blank_check_result)
     {
         CODE_FLASH_PRINTF("Flash块是空白的");
     }
     else if (FLASH_RESULT_NOT_BLANK == blank_check_result)
     {
         CODE_FLASH_PRINTF("Flash块不是空白的,不要向其中写入数据");
         while(1);
     }

     /** 读取 Code Flash 空白数据
     *  可以不使用 R_FLASH_HP_BlankCheck 函数,自己来验证 Code Flash 区域是否空白
     *  对于 Code Flash 来说,这段代码与调用 R_FLASH_HP_BlankCheck 函数的作用是一样的
     */
     memcpy(read_buffer, (uint8_t *) CODE_FLASH_TEST_BLOCK, CODE_FLASH_TEST_DATA_SIZE);
     CODE_FLASH_PRINTF("读出到 read_buffer 里的%d字节数据应全部是0xFF:", CODE_FLASH_TEST_DATA_SIZE);
     for (index = 0; index < CODE_FLASH_TEST_DATA_SIZE; index++)
     {
         printf("%X ", read_buffer[index]);
     }
     printf("\r\nEnd.\r\n");


     /* 写入测试数据到 Code Flash */
     CODE_FLASH_PRINTF("写入 Code Flash: ");
     /* Disable interrupts to prevent vector table access while code flash is in P/E mode. */
     __disable_irq();
     err = R_FLASH_HP_Write(&g_flash_ctrl, (uint32_t)write_buffer, CODE_FLASH_TEST_BLOCK, CODE_FLASH_TEST_DATA_SIZE);
     /* Enable interrupts after code flash operations are complete. */
     __enable_irq();
     /* Error Handle */
     if (FSP_SUCCESS != err)
     {
         CODE_FLASH_PRINTF("Write API failed");
         CODE_FLASH_PRINTF("Returned Error Code: %d", err);
         while(1);
     }
     CODE_FLASH_PRINTF("Writing flash data is successful");


     /* 读取 Code Flash 数据 */
     memcpy(read_buffer, (uint8_t *) CODE_FLASH_TEST_BLOCK, CODE_FLASH_TEST_DATA_SIZE);
     CODE_FLASH_PRINTF("打印 read_buffer 里的%d字节测试数据: ", CODE_FLASH_TEST_DATA_SIZE);
     for (index = 0; index < CODE_FLASH_TEST_DATA_SIZE; index++)
     {
         printf("%c ", read_buffer[index]);
     }
     printf("\r\nEnd.\r\n");


     /* 对比 write_buffer 和 read_buffer 数据 */
     if (0 == memcmp(read_buffer, write_buffer, CODE_FLASH_TEST_DATA_SIZE))
     {
         CODE_FLASH_PRINTF("数据对比一致,Code Flash 测试成功\r\n");
     }
     else
     {
         CODE_FLASH_PRINTF("数据对比不一致,Code Flash 测试失败\r\n");
         while(1);
     }
 }

42.5.8. DATA FLASH操作函数

DATA FLASH 操作函数如下所示。

代码清单 40‑5 DATA FLASH 操作函数
 /* Data Flash 操作函数 */
 void FLASH_HP_DataFlash_Operation(void)
 {
     fsp_err_t err;
     flash_result_t blank_check_result = FLASH_RESULT_BLANK;
     uint8_t write_buffer[DATA_FLASH_TEST_DATA_SIZE] = {0};
     uint8_t read_buffer[DATA_FLASH_TEST_DATA_SIZE] = {0};
     uint8_t index;

     /* 写入测试数据到 write_buffer */
     for (index = 0; index < DATA_FLASH_TEST_DATA_SIZE; index++)
     {
         write_buffer[index] = (uint8_t) ('A' + (index % 26));
     }

     /* 擦除 Data Flash */
     DATA_FLASH_PRINTF("擦除 Data Flash");
     err = R_FLASH_HP_Erase(&g_flash_ctrl, DATA_FLASH_TEST_BLOCK, BLOCK_NUM);
     /* Error Handle */
     if (FSP_SUCCESS != err)
     {
         DATA_FLASH_PRINTF("Erase API failed");
         DATA_FLASH_PRINTF("Returned Error Code: %d", err);
         while(1);
     }
     DATA_FLASH_PRINTF("Erase successful");


     /* 如果使能了BGO功能,等待擦除完成事件标志位 */
     if (true == g_flash_cfg.data_flash_bgo)
     {
         DATA_FLASH_PRINTF("BGO has enabled");
         while (!flash_erase_complete_flag);
         flash_erase_complete_flag = false;
     }
     DATA_FLASH_PRINTF("Erase successful");

     /* 空白检查:擦除 Data Flash 完成后,进行空白检查 */
     err = R_FLASH_HP_BlankCheck(&g_flash_ctrl, DATA_FLASH_TEST_BLOCK, FLASH_HP_DF_BLOCK_SIZE, &blank_check_result);
     /* Error Handle */
     if (FSP_SUCCESS != err)
     {
         DATA_FLASH_PRINTF("BlankCheck API failed");
         DATA_FLASH_PRINTF("Returned Error Code: %d", err);
         while(1);
     }

     /* 验证空白检查的结果 */
     if (FLASH_RESULT_BLANK == blank_check_result)
     {
         DATA_FLASH_PRINTF("Flash块是空白的");
     }
     else if (FLASH_RESULT_NOT_BLANK == blank_check_result)
     {
         DATA_FLASH_PRINTF("Flash块不是空白的,不要向其中写入数据");
         while(1);
     }
     else if (FLASH_RESULT_BGO_ACTIVE == blank_check_result)
     {
         /* Wait for callback function to set flag */
         while (!(flash_event_not_blank || flash_event_blank));

         DATA_FLASH_PRINTF("BGO has enabled");
         if (flash_event_not_blank)
         {
             DATA_FLASH_PRINTF("Flash块不是空白的,不要向其中写入数据");
             /* Reset Flag */
             flash_event_not_blank = false;
             while(1);
         }
         else
         {
             DATA_FLASH_PRINTF("Flash块是空白的");
             /* Reset Flag */
             flash_event_blank = false;
         }
     }

     /* 写入测试数据到 Data Flash */
     DATA_FLASH_PRINTF("写入 Data Flash: ");
     err = R_FLASH_HP_Write(&g_flash_ctrl, (uint32_t)write_buffer, DATA_FLASH_TEST_BLOCK, DATA_FLASH_TEST_DATA_SIZE);
     /* Error Handle */
     if (FSP_SUCCESS != err)
     {
         DATA_FLASH_PRINTF("Write API failed");
         DATA_FLASH_PRINTF("Returned Error Code: %d", err);
         while(1);
     }

     /* 如果使能了BGO功能,等待写入完成事件标志位 */
     if (true == g_flash_cfg.data_flash_bgo)
     {
         DATA_FLASH_PRINTF("BGO has enabled");
         while (!flash_write_complete_flag);     /* Note:如果写入没被擦除的块,会触发错误中断,会在这里卡死 */
         flash_write_complete_flag = false;
     }
     DATA_FLASH_PRINTF("Write successful");

     /* 读取 Data Flash 数据 */
     memcpy(read_buffer, (uint8_t *) DATA_FLASH_TEST_BLOCK, DATA_FLASH_TEST_DATA_SIZE);
     DATA_FLASH_PRINTF("打印 read_buffer 里的%d字节测试数据: ", DATA_FLASH_TEST_DATA_SIZE);
     for (index = 0; index < DATA_FLASH_TEST_DATA_SIZE; index++)
     {
         printf("%c ", read_buffer[index]);
     }
     printf("\r\nEnd.\r\n");


     /* 对比 write_buffer 和 read_buffer 数据 */
     if (0 == memcmp(read_buffer, write_buffer, DATA_FLASH_TEST_DATA_SIZE))
     {
         DATA_FLASH_PRINTF("数据对比一致,Data Flash 测试成功\r\n");
     }
     else
     {
         DATA_FLASH_PRINTF("数据对比不一致,Data Flash 测试失败\r\n");
         while(1);
     }
 }

42.5.9. hal_entry入口函数

hal_entry 入口函数如下所示。

代码清单 40‑6 hal_entry 入口函数
 /* 用户头文件包含 */
 #include "led/bsp_led.h"
 #include "debug_uart/bsp_debug_uart.h"
 #include "internal_flash/bsp_internal_flash.h"


 extern volatile bool code_flash_enable;
 extern volatile bool data_flash_enable;


 void hal_entry(void)
 {
     /* TODO: add your own code here */
     fsp_err_t err = FSP_SUCCESS;

     LED_Init();         // LED 初始化
     Debug_UART4_Init(); // SCI4 UART 调试串口初始化

     /* Flash 模块初始化 */
     FLASH_HP_Init();

     /* Setup Default  Block 0 as Startup Setup Block */
     err = R_FLASH_HP_StartUpAreaSelect(&g_flash_ctrl, FLASH_STARTUP_AREA_BLOCK0, true);
     if (err != FSP_SUCCESS)
     {
         printf("Flah_HP_StartUpAreaSelect API failed\r\n");
         printf("Returned Error Code: 0x%x\r\n", err);
         while(1);
     }


     printf("这是一个内部 Code / Data Flash 的使用例程\r\n");
     printf("打开串口助手发送以下指令,对 Flash 进行相应的操作\r\n");
     printf("\t指令   ------  操作\r\n");
     printf("\t 0   ------  操作 Code Flash\r\n");
     printf("\t 1   ------  操作 Data Flash\r\n");
     printf("\t=======================================\r\n\r\n");

     while(1)
     {
         if (true == code_flash_enable)
         {
             code_flash_enable = false;

             /* 测试 Code Flash */
             printf("测试 Code Flash...\r\n");
             FLASH_HP_CodeFlash_Operation();
         }

         if (true == data_flash_enable)
         {
             data_flash_enable = false;

             /* 测试 Data Flash */
             printf("测试 Data Flash...\r\n");
             FLASH_HP_DataFlash_Operation();
         }

         LED1_TOGGLE;
         R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS);
     }


 #if BSP_TZ_SECURE_BUILD
     /* Enter non-secure code */
     R_BSP_NonSecureEnter();
 #endif
 }

42.5.10. 下载验证

把编译好的程序下载到开发板。 用USB TYPE-C线连接开发板“USB TO UART”接口跟电脑,在电脑端打开串口调试助手。 复位开发板,根据提示进行操作。