瑞萨单片机论坛
直播中

jf_34532509

未满1年用户 10经验值
擅长:嵌入式技术
私信 关注
[经验]

【瑞萨RA6E2地奇星开发板试用】内部Code flash和Data flash写入数据并通过OLED显示

瑞萨RA6E2地奇星开发板 Code Flash与Data Flash 写入数据并通过OLED显示

测评环境:e² studio + FSP( Flexible Software Package ) + RA6E2地奇星开发板


一、测评概述

1.1 测评目的

本次测评基于瑞萨RA6E2地奇星开发板,验证其内部 Code FlashData Flash 的读写功能稳定性与可靠性,测试 Flash 擦除、写入、读取及数据验证的全流程可行性,为后续嵌入式项目存储方案提供参考依据。

1.2 硬件与软件环境

类别 参数/配置
开发板 瑞萨RA6E2地奇星开发板
主控芯片 RA6E2(ARM Cortex-M33 内核,主频最高可达200MHz)
Code Flash 规格 256KB,单次写入至少128字节写入
Data Flash 规格 4KB,支持4/8/16字节写入
开发环境 e² studio + FSP
辅助验证 OLED显示屏(I2C接口,用于可视化数据)

image.png

1.3 核心测评流程

  1. 初始化 Flash 控制器,配置 FSP 底层驱动;
  2. 对指定 Flash 块执行擦除操作,验证擦除后空白状态;
  3. 向 Code Flash 与 Data Flash 写入测试数据;
  4. 从 Flash 读取数据,通过 memcmp 函数验证读写一致性;
  5. 通过 OLED 显示读写状态及数据,直观呈现测试结果。

二、测评原理与代码实现

2.1 Flash 读写核心原理

瑞萨RA6E2的 Code Flash 与 Data Flash 均基于 NOR Flash 架构,遵循 "先擦除,后写入" 的操作原则:

  • 擦除:将指定 Flash 块的所有字节置为 0xFF(空白状态);
  • 写入:仅能将 0xFF 位改写为 0,无法直接将 0 改写为 1
  • 验证:读取写入后的数据与原始数据对比,一致则代表读写成功。

2.2 FSP库配置

对于IIC引脚的相关配置在之前帖子中讲过了,试用软件IIC主要就是把SCL和SDA引脚配置成GPIO接口,模拟IIC数据的传输即可。本次主要介绍flash相关FSP库的配置。
步骤如下:

  1. 打开configuration.xml,先配置Clock,开发板有连个晶振,分别是20MHz和32.768kHz。这里一定要20MHz的主控晶振。RTC时钟晶振在这里不做要求。
    image.png
  2. 新增flash堆栈
    image.png
    image.png

通过上述配置就完成了flash的FSP库的配置。


2.3 芯片内部flash Memory结构及地址

通过查阅用户手册,flash memory相关的模块框图如下:
image.png
通过上图我们可以知道应用层通过FACI向闪存子系统发起操作请求,FCU解析FACI转发的命令,结合外部信号生成时序控制逻辑,通过Flash sequencer完成操作的资源调度与流程编排。Flash sequencer根据FCU的控制指令对目标存储介质进行擦除-编程操作。
code flash的存储器地址映射如下图:
image.png

值得注意的是,每个块对应的内存容量不一样,后续代码编写需要注意,本次使用Block 13块进行操作,从0x0003_8000开始编写。
data flash的存储器地址映射如下: image.png
本次代码编写从起始地址0x0800_0000开始编写。

2.4 关键代码实现


本次测评采用 FSP 标准 API 完成 Flash 操作,核心代码逻辑如下:
本次所有有关flash的代码都在hal_entry.c文件下编写:

  1. 对相关地址寄存器的地址进行宏定义
#define FLASH_CF_BLOCK_21   0x00038000U
#define FLASH_DF_BLOCK_0    0x08000000U
#define FLASH_DATA_BLOCK_SIZE (1024*32)
  1. 回调函数实现
volatile bool       interrupt_called;
volatileflash_event_t  flash_event;
voidflash_callback(flash_callback_args_t *p_args)
{
    interrupt_called = true;
    flash_event = p_args->event;
}

通过回调函数,Flash操作完成后,主动触发中断然后调用回调函数并置位标志位,主程序只需要等待标志位即可,从而降低CPU资源占用率。

  1. 擦除flash
// 擦除Code Flash
   R_FLASH_HP_Erase(&g_flash0_ctrl, FLASH_CF_BLOCK_21, 1);
  1. 写入Code Flash
// 写入Code Flash
   R_FLASH_HP_Write(&g_flash0_ctrl, (uint32_t)test_data, FLASH_CF_BLOCK_21, 256);
  1. 读取并验证是否写入成功
memcpy(read_data, (uint8_t *)FLASH_CF_BLOCK_21, 4);
if(memcmp(test_data, read_data, 4) == 0)
{
// OLED显示"code flash写入成功"
OLED_ShowString(1,1,"code flash: OK");
// 显示读取的实际数据
for(uint8_t i=0; i<4; i++) OLED_ShowHexNum(2,1+i*3, read_data[i], 2);
}
else
{
OLED_ShowString(2,1,"code flash: FAIL");
assert(false);
}

data flash相关代码和code flash基本一致,因此不在展示。
整体相关实现代码如下:

#include "hal_data.h"
#include "oled.h"
#include <string.h>  // 仅用于memcpy/memcmp

#if (1 == BSP_MULTICORE_PROJECT) && BSP_TZ_SECURE_BUILD
bsp_ipc_semaphore_handle_t g_core_start_semaphore = {.semaphore_num = 0};
#endif

// Flash 块地址定义
#define FLASH_CF_BLOCK_21   0x00038000U  // Code Flash 21块地址
#define FLASH_DF_BLOCK_0    0x08000000U  // Data Flash 0块地址
#define FLASH_DATA_BLOCK_SIZE (1024*32)   // Code Flash 块容量(32KB)

// Flash 中断回调标志与事件
volatile bool       interrupt_called;
volatile flash_event_t  flash_event;

/**
 * [url=home.php?mod=space&uid=2666770]@Brief[/url] Flash 中断回调函数
 * [url=home.php?mod=space&uid=1902110]@NOTE[/url] 用于感知Flash异步操作(擦除/写入)的完成状态
 */
void flash_callback(flash_callback_args_t *p_args)
{
    interrupt_called = true;    // 置位操作完成标志
    flash_event = p_args->event;// 记录操作结果事件
}

/**
 * @brief 主入口函数:实现Code Flash/Data Flash读写验证
 */
void hal_entry(void)
{
    fsp_err_t err;
    uint8_t test_data[4] = {0x1a,0x24,0x46,0x6a};  // 测试数据(4字节)
    uint8_t read_data[4] = {0};                     // Flash读取数据缓存

    // 初始化OLED(可视化验证结果)
    OLED_Init();
    OLED_Clear();

    // ========== Code Flash 操作流程 ==========
    // 1. 打开Flash控制器
    err = R_FLASH_HP_Open(&g_flash0_ctrl, &g_flash0_cfg);
    assert(FSP_SUCCESS == err);

    // 2. 关闭全局中断,避免Flash操作被干扰
    __disable_irq();
    // 3. 擦除Code Flash 21块(擦除是写入的前提)
    R_FLASH_HP_Erase(&g_flash0_ctrl, FLASH_CF_BLOCK_21, 1);
    // 4. 写入测试数据到Code Flash(最小写入单位256字节)
    R_FLASH_HP_Write(&g_flash0_ctrl, (uint32_t)test_data, FLASH_CF_BLOCK_21, 256);
    // 5. 恢复全局中断
    __enable_irq();

    // 6. 读取+验证Code Flash数据
    memcpy(read_data, (uint8_t *)FLASH_CF_BLOCK_21, 4);
    if(memcmp(test_data, read_data, 4) == 0)
    {
        // OLED显示Code Flash写入成功
        OLED_ShowString(1,1,"code flash: OK");
        // 显示读取的实际数据(十六进制)
        for(uint8_t i=0; i<4; i++) OLED_ShowHexNum(2,1+i*3, read_data[i], 2);
    }
    else
    {
        OLED_ShowString(2,1,"code flash: FAIL");
        assert(false);  // 验证失败触发断言,定位错误
    }

    // ========== Data Flash 操作流程 ==========
    // 1. 重置中断完成标志
    interrupt_called = false;
    // 2. 擦除Data Flash 0块 + 等待中断回调确认完成
    R_FLASH_HP_Erase(&g_flash0_ctrl, FLASH_DF_BLOCK_0, 1);
    while(!interrupt_called);

    // 3. 写入测试数据到Data Flash(最小写入单位4字节)
    R_FLASH_HP_Write(&g_flash0_ctrl, (uint32_t)test_data, FLASH_DF_BLOCK_0, 4);
    // 4. 等待写入操作完成(轮询状态寄存器)
    flash_status_t status;
    do{R_FLASH_HP_StatusGet(&g_flash0_ctrl, &status);}while(FLASH_STATUS_BUSY == status);

    // 5. 读取+验证Data Flash数据
    memcpy(read_data, (uint8_t *)FLASH_DF_BLOCK_0, 4);
    if(memcmp(test_data, read_data, 4) == 0)
    {
        // OLED显示Data Flash写入成功
        OLED_ShowString(3,1,"data flash: OK");
        // 显示读取的实际数据(十六进制)
        for(uint8_t i=0; i<4; i++) OLED_ShowHexNum(4,1+i*3, read_data[i], 2);
    }
    else
    {
        OLED_ShowString(4,1,"data flash: FAIL");
        assert(false);  // 验证失败触发断言,定位错误
    }

    // 多核相关代码(保留以避免编译错误,单核场景可忽略)
#if (0 == _RA_CORE) && (1 == BSP_MULTICORE_PROJECT) && !BSP_TZ_NONSECURE_BUILD
#if BSP_TZ_SECURE_BUILD
    R_BSP_IpcSemaphoreTake(&g_core_start_semaphore);
#endif
    R_BSP_SecondaryCoreStart();
#if BSP_TZ_SECURE_BUILD
    while(FSP_ERR_IN_USE == R_BSP_IpcSemaphoreTake(&g_core_start_semaphore)){}
#endif
#endif

#if (1 == _RA_CORE) && (1 == BSP_MULTICORE_PROJECT) && BSP_TZ_SECURE_BUILD
    R_BSP_IpcSemaphoreGive(&g_core_start_semaphore);
#endif

#if BSP_TZ_SECURE_BUILD
    R_BSP_NonSecureEnter();
#endif
}

// 安全相关空函数(保留以避免编译错误)
#if BSP_TZ_SECURE_BUILD
FSP_CPP_HEADER
BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable ();
BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable (){}
FSP_CPP_FOOTER
#endif

三、测评结果与分析

3.1 功能验证结果

测试项 测试结果 OLED 显示内容
Code Flash 擦除 成功 -
Code Flash 写入 成功 Code Flash: OK
Code Flash 数据验证 一致 读取数据:1A 24 46 6A
Data Flash 擦除 成功 -
Data Flash 写入 成功 Data Flash: OK
Data Flash 数据验证 一致 读取数据:1A 24 46 6A

3.2 常见问题与解决方案

问题现象 原因分析 解决方案
Flash 写入失败,断言触发 未执行擦除操作,或擦除不彻底 严格遵循"先擦除,后写入"流程,擦除后执行空白检查
Data Flash 操作无中断回调 未注册 Flash 中断回调函数 在 FSP 配置中启用 Flash 中断,并关联回调函数
读取数据与写入数据不一致 写入地址错误,或数据缓存未刷新 确认 Flash 块地址正确性,写入后延时 1ms 再读取

四、测评总结与建议

4.1 测评总结

  1. 瑞萨RA6E2地奇星开发板的 Code FlashData Flash 读写功能稳定可靠,数据验证一致性达 100%,完全满足嵌入式项目的存储需求。
  2. FSP 驱动封装简洁易用,通过标准化 API 即可完成 Flash 全流程操作,降低了开发门槛。
  3. Code Flash 适合存储程序固件或大容量静态数据,Data Flash 适合存储小容量频繁更新的参数(如设备配置信息)。

4.2 应用建议

  1. 存储方案选型
    • 大容量、低更新频率数据 → Code Flash;
    • 小容量、高更新频率数据 → Data Flash;
    • 关键数据建议采用 双备份存储,提升可靠性。
  2. 开发优化建议
    • 擦除和写入操作耗时较长,建议在非中断敏感时段执行;
    • 频繁写入的场景可引入 wear leveling(磨损均衡) 算法,延长 Flash 使用寿命;
    • 利用 Flash 保护功能,对关键数据块进行写保护,防止误操作。

0e3e2a5cc08ff37635aee776c82928df

更多回帖

发帖
×
20
完善资料,
赚取积分