前言

最近使用到瑞萨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();//不知道为什么读写太快调试会出bugNOP();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读写操作详细探讨相关推荐

  1. HC32 flash 读写操作

    flash 读写操作 HC32 flash 简介 HC32 flash 操作和时钟之间的关系 Flash 的读写操作 解锁寄存器 单次编程无回读功能 单编程有回读 连续编程 擦除功能 全擦除功能 综合 ...

  2. 【FPGA】SPI协议详解及对flash读写操作

    FPGA基于SPI实现对flash读写操作 概括 一.SPI协议.flash讲解 1.SPI协议 2.flash (1)WREN (2)RDID (3)WRSR (4)READ (5)PP (6)SE ...

  3. (超详细)STM32芯片Flash读写操作讲解和代码(寄存器版本)

    关于Flash,官方的解释为:Flash为32位宽的存储单元,可用于存储代码和数据常量.Flash模块位于微控制器内存映射中的特定基址--.而对于我们来说,只要知道Flash闪存区是一个掉电后也不会清 ...

  4. NAND FLASH 读写操作 简介

    NAND FLASH 内存详解与读写寻址方式 一.内存详解 NAND闪存阵列分为一系列128kB的区块(block),这些区块是 NAND器件中最小的可擦除实体.擦除一个区块就是把所有的位(bit)设 ...

  5. Xilinx ZYNQ 7000学习笔记三(qspi flash读写操作)

    参考文献:Zynq-7000 SoC Technical Reference Manual (UG585)-ch12 Quad-SPI Flash Controller 一.nor Flash介绍 z ...

  6. ZYNQ-QSPI Flash读写操作

    学习内容 本文首先介绍Flash和QSPI Flash控制器的相关内容,然后使用 QSPI Flash 控制器,开发板上的 QSPI Flash 进行写. 读操作.通过对比读出的数据是否等于写入的数据 ...

  7. 瑞萨,尝试flash刷写代码放到ram中,OK

    因为项目需要,要把boot代码里的flash驱动程序放到ram里,而且不能再flash里保存,之前没用过瑞萨的芯片,找了一大堆的参考文件看,后来发现还没有看它的说明文档好使. 把cc-rl用户手册看了 ...

  8. Esp8266的Flash读写操作以及Flash上传文件

    1.Flash的读写操作 Esp8266的Flash为4M,其中1M用于存储程序,其他的空间有一部分用于系统,3M中剩下的大部分空间可以用来存放文件. #include <FS.h> St ...

  9. c语言指针flash,STM32F103RCT6之FLASH读写操作

    一.STM32F103的FLASH简介 1.如图所示,STM32F103内部FLASH存储区分为三个区域:主存储区.信信息块和闪存存储器接口寄存器. 储存储区是我们读写FLASH的主要的存储区,MCU ...

最新文章

  1. 外星人台式电脑_戴尔外星人Alienware m17笔记本19款真机评测
  2. yolov3模型识别不出训练图片_YOLOv3训练自己的模型
  3. 基于spark mllib_Spark高级分析指南 | 机器学习和分析流程详解(下)
  4. 被低估的css滤镜,你所不知道的 CSS 滤镜技巧与细节
  5. Python中的高效的集合操作
  6. 卡在登陆界面进不去_穿越火线第十三年:需要的不仅仅是新界面,重要还是留住旧玩家...
  7. apache应用进阶
  8. CSS:你真的会用 z-index 吗?
  9. 制作FAT12软盘以查看软盘的根目录条目+文件属性+文件内容
  10. python numpy.random模块中提供啦大量的随机数相关的函数
  11. 英特尔中国祝贺高亭宇夺冠:至强CPU提供更精准训练支持
  12. 一份完整的建模文档需涵盖的模块与指标
  13. javascript 网页自动跳转
  14. OpenFileDialog/SaveFileDialog 中 Filter用法?
  15. 中国首台云电脑全面解析——天霆云计算董事长谈天霆专访
  16. 网络安全学习路线(顶级白帽黑客)
  17. 微信授权登录:移动端[unionid](一)
  18. csgo笔记本党设置4:3画面
  19. 滴滴新算法让你应对女友?道翰天琼认知智能机器人平台API接口大脑为您揭秘-64
  20. word、excel文档内容更新技术方案

热门文章

  1. php报纸源码,PHP报纸在线阅读程序 电子读报系统 杂志在线阅读源码 DM阅读源码...
  2. 我们文本分析了贾跃亭2017年全部公开信,发现他近期喜欢用“责任”“致歉”...
  3. 纽约摄影教程读书笔记
  4. 云计算时代——本质、技术、创新、战略
  5. Anti-Roll Bar -- 防倾杆
  6. 沪江前端由H5页面引起的一场前端数据结构讨论 1
  7. JS设计“网页在线编辑器”
  8. 将来我一定将他(科比)讲给你听!特别是你在遇到坎坷,感到迷茫的时候!
  9. 新手入门WPF之TreeView控件(一)
  10. 美拍应该如何引流?如何利用美拍引流?美拍引流方法