我是靠谱客的博主 醉熏酒窝,最近开发中收集的这篇文章主要介绍瑞萨R78族Flash读写操作详细探讨,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言

最近使用到瑞萨R78族的MCU,准备做一个关于掉电保存参数配置的功能,需求大概是对200多个参数在掉电瞬间保存到芯片flash空间中,网上关于瑞萨MCU的flash读写操作教程也比较少,于是笔者结合在开发过程中遇到一些问题,写下这篇文章和大家一起探讨。

FDL和EEL

瑞萨官方为RL78族MCU提供了两个有关flash读写的函数库,分别是FDL(Data Flash Libraries)和EEL(EEPROM Emulation Library)顾名思义就是Flash操作函数库和eeprom操作函数库。这两个函数库及其文档都可以在瑞萨官网进行下载,另一份重要文档就是《数据闪存库的比较和应用》容易被大家忽视。值得注意是EEL是基于FDL的,也就是说使用EEL的话需要加入FDL,使用FDL不用加入EEL。
那么Flash读写方式和eeprom读写方式的区别是什么,事实上,Flash是eeprom的一种,传统的eeprom是可以单字节访问和修改的,这种eeprom一般存储量较小,后来诞生的Flash,直接通过块擦除,大大减少了电路复杂度,增加了存储密度。而瑞萨的Flash Data区都是Flash类型的。
在这里插入图片描述
FDL就是提供了Flash空间的操作接口,EEL是一个基于FDL的仿真库,实现类似操作eeprom的接口,换言之实际上还是操作Flash,是瑞萨官方通过程序进行模拟的。

FDL和EEL对比优缺点

官方提供的FDL有三个版本,分别是T01、T02、T04,基于不用FDL的EEL有两个版本,分别是T01、T02,暂时没有T04。不同版本FDL区别如下:
在这里插入图片描述
在这里插入图片描述
EEL T01和T02资源对比:
在这里插入图片描述
EEL的读写速度在文档好像没有列出来,笔者猜可能与对应FDL有直接关系。然后是EEL读写寿命更长:
在这里插入图片描述
换言之,FDL在使用上更方便,但是Flash寿命更低,EEL使用相对复杂,但是寿命更长。

FDL移植与改进

下面详细介绍FDL与EEL的移植过程,笔者使用CS+ for CX CA的版本,FDL使用T02版本,编译器是CA78K0R

1.获取FDL

访问瑞萨电子官网,搜索EEL,因为前面介绍了EEL是基于FDL的,所以下载了这个就包含了两个库文件。找到下图的文件并下载
在这里插入图片描述
下载好后解压,安装到相应版本CS+:
在这里插入图片描述
下一步会让你选择库的存放目录,安装好之后就可以看到库文件夹了:
在这里插入图片描述
下面转入到CS+进行移植:
在工程目录上新建EEL和FDL的文件夹,将安装下来的库文件里面的lib和simple里的相关文件通通加入到工程,并新建一个我们自己的函数文件:Save_Data.c和Save_Data.h。
在这里插入图片描述
下面开始结束函数的编写:
官方文档介绍了FDL T02的初始化和读写过程,因此笔者的移植也是基于此,首先上流程图:
在这里插入图片描述
其次是官方源码:

//官方源码
extern __far const fdl_descriptor_t fdl_descriptor_str;
fdl_status_t my_fdl_status_enu;
__near fdl_request_t request;
fdl_u08 buffer[5];
   
/* initialization */
my_fdl_status_enu = FDL_Init((__far fdl_descriptor_t*)&fdl_descriptor_str );
if(my_fdl_status_enu != FDL_OK)  
    ErrorHandler();
FDL_Open();
   
/* request structure initialization */
request.index_u16 = 0x0000;
request.data_pu08 = (__near fdl_u08*) 0x0000;
request.bytecount_u16 = 0x0000;
request.command_enu = (fdl_command_t)0xFF;
request.status_enu = FDL_ERR_PARAMETER;
   
/* erase block 0 */
request.index_u16 = 0x0000;
request.command_enu = FDL_CMD_ERASE_BLOCK;
FDL_Execute(&request);
while(request.status_enu == FDL_BUSY)  
    FDL_Handler();
if(request.status_enu != FDL_OK)  
    ErrorHandler();
   
/*blank Check*/
request.index_u16 = 0x0000;
request.bytecount_u16 = 0x0005;
request.command_enu = FDL_CMD_BLANKCHECK_BYTES;
FDL_Execute(&request);
while(request.status_enu == FDL_BUSY)  
    FDL_Handler();
if(request.status_enu != FDL_OK)  
    ErrorHandler();
   
/* write pattern 0x123456789A to idx = 0 */
buffer[0] = 0x12;
buffer[1] = 0x34;
buffer[2] = 0x56;
buffer[3] = 0x78;
buffer[4] = 0x9A;
request.index_u16 = 0x0000;
request.data_pu08 = (__near fdl_u08*)&buffer[0];
request.bytecount_u16 = 0x0005;
request.command_enu = FDL_CMD_WRITE_BYTES;
FDL_Execute(&request);
while(request.status_enu == FDL_BUSY)  
    FDL_Handler();
if(request.status_enu != FDL_OK)  
    ErrorHandler();
  
/*iverify*/
request.index_u16 = 0x0000;
request.bytecount_u16 = 0x0005;
request.command_enu = FDL_CMD_IVERIFY_BYTES;
FDL_Execute(&request);
while(request.status_enu == FDL_BUSY)  
    FDL_Handler();
if(request.status_enu != FDL_OK)  
    ErrorHandler();
/* set initial values */
buffer[0] = 0xFF;
buffer[1] = 0xFF;
buffer[2] = 0xFF;
buffer[3] = 0xFF;
buffer[4] = 0xFF;
request.index_u16 = 0x0000;
request.data_pu08 = (__near fdl_u08*)&buffer[0];
request.bytecount_u16 = 0x0005;
request.command_enu = FDL_CMD_READ_BYTES;
FDL_Execute(&request);
if(request.status_enu != FDL_OK)  
    ErrorHandler();
FDL_Close();

初次看这份源码非常头痛,不知在做什么操作,起始拆分开来看也并不复杂。首先看FDL所提供的函数接口:
在这里插入图片描述
需要注意源码中ErrorHandler()函数是报错之后的动作,需要用户自己添加内容,笔者直接设置为return 0;FDL_Execute(&request)函数是根据request结构体参数的不同进行不同动作,可以看上图
源码的功能是对一个buf[5],写进Flash再读出来。结合源码和流程图分析:
1.初始化

/* initialization */
my_fdl_status_enu = FDL_Init((__far fdl_descriptor_t*)&fdl_descriptor_str );
//笔者注:fdl_descriptor_str结构体在simple文件夹的fdl_descriptor.c里定义,作为配置时钟、格式等等
if(my_fdl_status_enu != FDL_OK)  
    ErrorHandler();
FDL_Open();
   
/* request structure initialization */
//笔者注:结构体复位,其实感觉可以不用
request.index_u16 = 0x0000;
request.data_pu08 = (__near fdl_u08*) 0x0000;
request.bytecount_u16 = 0x0000;
request.command_enu = (fdl_command_t)0xFF;
request.status_enu = FDL_ERR_PARAMETER;

2.块擦除,前面介绍了Flash的特性就是不能一个个字节修改,只能整块擦除,也就是单个bit的Flash不能0’置‘1’,只能’1’置‘0’,块擦除就是将整块Flash置‘1’,可以看看初始的Flash内存都是0xFF。

/* erase block 0 */
//笔者注:对0x0000的block进行擦除,request.index_u16不等于0x0000会出错,一个block占1K空间
request.index_u16 = 0x0000;
request.command_enu = FDL_CMD_ERASE_BLOCK;
FDL_Execute(&request);
while(request.status_enu == FDL_BUSY)  
    FDL_Handler();
if(request.status_enu != FDL_OK)  
    ErrorHandler();

3.写操作

/*blank Check*/
//笔者注:blank检查,如果当前blank不为0xff,就是没有擦除到位,抛出错误
request.index_u16 = 0x0000;
request.bytecount_u16 = 0x0005;
request.command_enu = FDL_CMD_BLANKCHECK_BYTES;
FDL_Execute(&request);
while(request.status_enu == FDL_BUSY)  
    FDL_Handler();
if(request.status_enu != FDL_OK)  
    ErrorHandler();
   
/* write pattern 0x123456789A to idx = 0 */
//笔者注:在0x0000的位置写入5个字节数据,内容起始地址是&buffer[0]
buffer[0] = 0x12;
buffer[1] = 0x34;
buffer[2] = 0x56;
buffer[3] = 0x78;
buffer[4] = 0x9A;
request.index_u16 = 0x0000;
request.data_pu08 = (__near fdl_u08*)&buffer[0];
request.bytecount_u16 = 0x0005;
request.command_enu = FDL_CMD_WRITE_BYTES;
FDL_Execute(&request);
while(request.status_enu == FDL_BUSY)  
    FDL_Handler();
if(request.status_enu != FDL_OK)  
    ErrorHandler();
  
/*iverify*/
//笔者注:验证字节有没有正确写入(还不清楚具体是怎么工作的)
request.index_u16 = 0x0000;
request.bytecount_u16 = 0x0005;
request.command_enu = FDL_CMD_IVERIFY_BYTES;
FDL_Execute(&request);
while(request.status_enu == FDL_BUSY)  
    FDL_Handler();
if(request.status_enu != FDL_OK)  
    ErrorHandler();

4.读操作

/* set initial values */
//笔者注:该段代码的逻辑是先对buffer复位,读出内容在对比看是不是和原来一样
buffer[0] = 0xFF;
buffer[1] = 0xFF;
buffer[2] = 0xFF;
buffer[3] = 0xFF;
buffer[4] = 0xFF;
request.index_u16 = 0x0000;
request.data_pu08 = (__near fdl_u08*)&buffer[0];
request.bytecount_u16 = 0x0005;
request.command_enu = FDL_CMD_READ_BYTES;
FDL_Execute(&request);
if(request.status_enu != FDL_OK)  
    ErrorHandler();
FDL_Close();  //笔者注:FDL保护开启

上面是对FDL函数库的API的分析。

改进

显然官方源码只是展示了函数库的使用流程,在实际应用中并不能很好符合我们的使用习惯,因此需要进行改进。笔者的改进逻辑是将读操作和写操作分离开,同时笔者要保存的参数都是2字节的,因此每次操作只操作两字节读写。同时将FDL的块擦除、保护开启、保护关闭进行函数封装,方便调用。

#include "r_cg_macrodriver.h"
#include "r_cg_userdefine.h"

#include "Save_Data.h"
#include "fdl.h"
//全局变量及函数声明
extern __far const fdl_descriptor_t fdl_descriptor_str;
static fdl_status_t my_fdl_status_enu;
static __near fdl_request_t request;
uint8_t flash_init(void);
void flash_close(void);
uint8_t save_data_r(uint8_t ID, uint16_t *data);
uint8_t save_data_w(uint8_t ID, uint16_t *data);
/*----------------------------------------------------------
函数功能:FDL初始化
输入参数:无
返回值:成功1 失败0
----------------------------------------------------------*/
uint8_t flash_init(void){
	
	/* initialization */
	my_fdl_status_enu = FDL_Init((__far fdl_descriptor_t*)&fdl_descriptor_str );
	if(my_fdl_status_enu != FDL_OK)
		return 0;
	FDL_Open();

	/* request structure initialization */
    request.index_u16 = 0x0000;
	request.data_pu08 = (__near fdl_u08*) 0x0000;
	request.bytecount_u16 = 0x0000;
	request.command_enu = (fdl_command_t)0xFF;
	request.status_enu = FDL_ERR_PARAMETER;
	return 1;
}
/*----------------------------------------------------------
函数功能:块擦除
输入参数:无
返回值:成功1,失败0
----------------------------------------------------------*/
uint8_t block_erase(uint16_t block){
    /* erase block 0 */
	request.index_u16 = block;
	request.command_enu = FDL_CMD_ERASE_BLOCK;
	FDL_Execute(&request);
	while(request.status_enu == FDL_BUSY)
		FDL_Handler();
	if(request.status_enu != FDL_OK)
		return 0;
	return 1;
}
/*----------------------------------------------------------
函数功能:EEL保护
输入参数:无
返回值:无
----------------------------------------------------------*/
void flash_close(void){
    /* 终止FDL ------------------------------------------- */ 
	FDL_Close(); 	
}
/*----------------------------------------------------------
函数功能:向Flash写入两字节数据
输入参数:保存到blank的ID, 内容首地址data
返回值:成功1 失败0
----------------------------------------------------------*/
uint8_t save_data_w(uint8_t ID, uint16_t *data){
	
	/*blank Check*/
	request.index_u16 = (uint16_t)ID;
	request.bytecount_u16 = 0x0002;
	request.command_enu = FDL_CMD_BLANKCHECK_BYTES;
	FDL_Execute(&request);
	while(request.status_enu == FDL_BUSY)
		FDL_Handler();
	if(request.status_enu != FDL_OK)
		return 0;
		
	/* write pattern to id */
	request.index_u16 = (uint16_t)ID;
	request.data_pu08 = (__near fdl_u08*)data;
	request.bytecount_u16 = 0x0002;
	request.command_enu = FDL_CMD_WRITE_BYTES;
	FDL_Execute(&request);
	while(request.status_enu == FDL_BUSY)
		FDL_Handler();
	if(request.status_enu != FDL_OK)
		return 0;
		
	/*iverify*/
	request.index_u16 = (uint16_t)ID;
	request.bytecount_u16 = 0x0002;
	request.command_enu = FDL_CMD_IVERIFY_BYTES;
	FDL_Execute(&request);
	while(request.status_enu == FDL_BUSY)
		FDL_Handler();
	if(request.status_enu != FDL_OK)
		return 0;
	
	return 1;
}
/*----------------------------------------------------------
函数功能:从eeprom读两字节数据
输入参数:blank的ID,存放目标首地址data
返回值:成功1 失败0
----------------------------------------------------------*/
uint8_t save_data_r(uint8_t ID, uint16_t *data){
	
	
	/* 初始化读指令 --------------------------------------------- */ 
	request.index_u16 = (uint16_t)ID;
	request.data_pu08 = (__near fdl_u08*)data;
	request.bytecount_u16 = 0x0002;
	request.command_enu = FDL_CMD_READ_BYTES;
	FDL_Execute(&request);
	if(request.status_enu != FDL_OK)
		return 0;
	return 1;
}

测试过程

main函数部分代码,保存test3和test4,读取到test1、test2

    uint8_t status = 0;
	uint16_t test1 = 0x0;
	uint16_t test2 = 0x0;
	uint16_t test3 = 0x1234;
	uint16_t test4 = 0x5678;
	
	flash_init();                    //初始化
	block_erase(0x0000);             //块擦除
	status = save_data_w(0, &test3); //写入
	NOP();//不知道为什么读写太快调试会出bug
	NOP();
	NOP();
	status = save_data_w(2, &test4); //写入
	flash_close();
	
	flash_init();
	status = save_data_r(0, &test1); //读取
	NOP();
	NOP();
	NOP();
	status = save_data_r(2, &test2); //读取
	flash_close();

调试结果
在这里插入图片描述

200个数据写入速度

单位/10ms
也就是将200个数据写入flash需要70ms,在接受范围内。
在这里插入图片描述

EEL

EEL可以对照官方文件的流程进行操作,和FDL分析方法一样,有人看再写。

最后

以上就是醉熏酒窝为你收集整理的瑞萨R78族Flash读写操作详细探讨的全部内容,希望文章能够帮你解决瑞萨R78族Flash读写操作详细探讨所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(61)

评论列表共有 0 条评论

立即
投稿
返回
顶部