21 内部FLASH读写实验

开发板官网(https://www.edevkit.com/)
本系列教程目录:
- 瑞萨单片机开发教程[一] e2-studio + DAP LINK开发环境搭建
- 瑞萨单片机开发教程[二] Keil/RASC开发环境搭建
- 瑞萨单片机开发教程[三] 新建RASC工程
- 瑞萨单片机开发教程[四] GPIO输出——led实验
- 瑞萨单片机开发教程[五] GPIO输入——按键key实验
- 瑞萨单片机开发教程[六] ICU外部中断实验
- 瑞萨单片机开发教程[七] SCI UART串口通信实验
- 瑞萨单片机开发教程[八] DMAC 串口数据传输实验
- 瑞萨单片机开发教程[九] ADC 电压采集实验
内部 FLASH 读写实验
本章目标:
- 了解 RA6M5 处理器的内部 FLASH 地址分布;
- 学会使用 RASC 配置 FLASH 模块、使用 API 函数;
硬件设计
(略,参见配套硬件原理图)
软件设计
新建工程
复制一份“07 SCI_UART_Recv_send”修改文件夹名“21 Internal_Flash”,双击打开 EBF_RA6M5.uvprojx 工程,通过 Keil 的新建文件新建 flash.c 和 flash.h,保存到目录下的 src 文件夹下。
FSP 配置
Keil 界面选择“Tools”→“RA Smart Configurator”打开 RASC 界面。
添加 FLASH HP Stack 模块。


| Code Flash Programming Enable | 选择 Enabled,允许对 Code Flash 进行编程 |
|---|---|
| Data Flash Programming Enable | 选择 Enabled,允许对 Data Flash 进行编程 |
| Name | 设置模块名 |
| Data Flash Background Operation | 设置是否启用 Data Flash 的后台操作功能 |
| Callback | 设置后台操作的中断回调函数。这里设置为 flash_df_bgo_callback |
| Flash Ready Interrupt Priority | 设置 Flash 就绪中断优先级。这里设置为优先级 2 |
| Flash Error Interrupt Priority | 设置 Flash 错误中断优先级。这里设置为优先级 2 |
配置完成之后可以按下快捷键“Ctrl + S”保存,最后点击“Generate Project Content”按钮,让软件自动生成配置代码即可。
程序设计
设计 Code Flash 读写测试程序,首先擦除指定块,检测块是否是空白块,是空白块则写入一组数据,接着读出这一组数据,对比读写数据是否相同,相同则 Flash 读写成功。
- 打开 Flash HP 设备
R_FLASH_HP_Open(flash_ctrl_t * const p_api_ctrl, flash_cfg_t const * const p_cfg);
- 擦除 Flash 块
R_FLASH_HP_Erase(flash_ctrl_t * const p_api_ctrl, uint32_t const address, uint32_t const num_blocks);
第二个参数指定 Flash 地址,第三个参数指定块数量。
- 检测块是否是空白块,也就是数据是否都是 0xFF。
R_FLASH_HP_BlankCheck(flash_ctrl_t * const p_api_ctrl,
uint32_t const address,
uint32_t num_bytes,
flash_result_t * blank_check_result);
- 写入数据
R_FLASH_HP_Write(flash_ctrl_t * const p_api_ctrl,
uint32_t const src_address,
uint32_t flash_address,
uint32_t const num_bytes);
- 读取数据
memcpy(read_buffer, (uint8_t *) CODE_FLASH_TEST_BLOCK, CODE_FLASH_TEST_DATA_SIZE);
读取数据只需通过取地址的方式就可以读取数据。
程序编写
在 flash_hp.h 头文件里定义初始化函数、读写测试函数。
#ifndef __BSP_INTERNAL_FLASH_H
#define __BSP_INTERNAL_FLASH_H
#include "hal_data.h"
#include "stdio.h"
#define CODE_FLASH_PRINTF(format, ...) printf("[Code FLASH Operation] "format"\r\n", ##__VA_ARGS__)
/* 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 */
/* CODE FLASH 测试 */
#define CODE_FLASH_TEST_BLOCK 0x00100000U /* 避免覆盖程序代码数据 */
#define CODE_FLASH_TEST_DATA_SIZE 128 /* 只测试128个字节数据 */
/* 擦除的 FLASH 的块数 */
#define BLOCK_NUM 1
void flash_hp_Init(void);
void codeflash_ReadWrite_test(void);
#endif
在 flash_hp.c 里实现初始化函数、中断函数和读写函数。
#include "flash_hp.h"
//初始化flash
void flash_hp_Init(void)
{
fsp_err_t err = R_FLASH_HP_Open(&g_flash0_ctrl, &g_flash0_cfg);
if (FSP_SUCCESS != err)
{
printf("Flash_HP_Open API failed!\r\n");
printf("Returned Error Code: 0x%x\r\n", err);
while(1);
}
err = R_FLASH_HP_StartUpAreaSelect(&g_flash0_ctrl, FLASH_STARTUP_AREA_BLOCK0, true);
if (err != FSP_SUCCESS)
{
printf("Flash_HP_StartUpAreaSelect API failed\r\n");
printf("Returned Error Code: 0x%x\r\n", err);
while(1);
}
}
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_hp_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;
}
}
//读写测试函数
void codeflash_ReadWrite_test(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) (index + 1);
}
CODE_FLASH_PRINTF("Erase Code Flash");
/* 禁用中断,以避免P/E过程中产生中断导致操作失败
注:这里禁用中断后,不能使用串口打印 */
__disable_irq();
err = R_FLASH_HP_Erase(&g_flash0_ctrl, CODE_FLASH_TEST_BLOCK, BLOCK_NUM);
__enable_irq();
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_flash0_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("The Flash block is blank.");
}
else if (FLASH_RESULT_NOT_BLANK == blank_check_result)
{
CODE_FLASH_PRINTF("The Flash block is not blank. Do not write data into it.");
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("The %d bytes of data read into the read_buffer should all be 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("Write flash data %d bytes:", CODE_FLASH_TEST_DATA_SIZE);
for (index = 0; index < CODE_FLASH_TEST_DATA_SIZE; index++)
{
printf("%d ", write_buffer[index]);
}
printf("\r\nEnd.\r\n");
__disable_irq();
err = R_FLASH_HP_Write(&g_flash0_ctrl, (uint32_t)write_buffer, CODE_FLASH_TEST_BLOCK, CODE_FLASH_TEST_DATA_SIZE);
__enable_irq();
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 successful");
/* 读取 Code Flash 数据 */
memcpy(read_buffer, (uint8_t *) CODE_FLASH_TEST_BLOCK, CODE_FLASH_TEST_DATA_SIZE);
CODE_FLASH_PRINTF("read flash data %d bytes:", CODE_FLASH_TEST_DATA_SIZE);
for (index = 0; index < CODE_FLASH_TEST_DATA_SIZE; index++)
{
printf("%d ", 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("Read and write data are the same, Code Flash successful test\r\n");
}
else
{
CODE_FLASH_PRINTF("Read and write data are not same, Code Flash failed test\r\n");
while(1);
}
}
在 hal_entry.c 文件中添加头文件 flash_hp.h,在 hal_entry 入口函数初始化,按键事件中执行测试函数。
void hal_entry(void)
{
/* TODO: add your own code here */
led_Init(); // LED 初始化
uart0_Init();
flash_hp_Init();
printf("START FLASH HP READ WRITE TEST \r\n");
while(1)
{
if(key_Scan(KEY1_PIN))
{
LED1_TOGGLE();
codeflash_ReadWrite_test();
}
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
下载验证
连接下载器,将程序编译并下载到开发板之后,按下复位按键来复位开发板,程序启动初始化完成后,观察实验结果。
按下按键执行 Flash 读写函数,首先擦除块检查是否是空白块,写入 1~128 共 128 个数据,然后再读出 128 个数据,对比读写数据是否相同,相同则测试成功。
