瑞萨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();//不知道为什么读写太快调试会出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读写操作详细探讨相关推荐
- HC32 flash 读写操作
flash 读写操作 HC32 flash 简介 HC32 flash 操作和时钟之间的关系 Flash 的读写操作 解锁寄存器 单次编程无回读功能 单编程有回读 连续编程 擦除功能 全擦除功能 综合 ...
- 【FPGA】SPI协议详解及对flash读写操作
FPGA基于SPI实现对flash读写操作 概括 一.SPI协议.flash讲解 1.SPI协议 2.flash (1)WREN (2)RDID (3)WRSR (4)READ (5)PP (6)SE ...
- (超详细)STM32芯片Flash读写操作讲解和代码(寄存器版本)
关于Flash,官方的解释为:Flash为32位宽的存储单元,可用于存储代码和数据常量.Flash模块位于微控制器内存映射中的特定基址--.而对于我们来说,只要知道Flash闪存区是一个掉电后也不会清 ...
- NAND FLASH 读写操作 简介
NAND FLASH 内存详解与读写寻址方式 一.内存详解 NAND闪存阵列分为一系列128kB的区块(block),这些区块是 NAND器件中最小的可擦除实体.擦除一个区块就是把所有的位(bit)设 ...
- Xilinx ZYNQ 7000学习笔记三(qspi flash读写操作)
参考文献:Zynq-7000 SoC Technical Reference Manual (UG585)-ch12 Quad-SPI Flash Controller 一.nor Flash介绍 z ...
- ZYNQ-QSPI Flash读写操作
学习内容 本文首先介绍Flash和QSPI Flash控制器的相关内容,然后使用 QSPI Flash 控制器,开发板上的 QSPI Flash 进行写. 读操作.通过对比读出的数据是否等于写入的数据 ...
- 瑞萨,尝试flash刷写代码放到ram中,OK
因为项目需要,要把boot代码里的flash驱动程序放到ram里,而且不能再flash里保存,之前没用过瑞萨的芯片,找了一大堆的参考文件看,后来发现还没有看它的说明文档好使. 把cc-rl用户手册看了 ...
- Esp8266的Flash读写操作以及Flash上传文件
1.Flash的读写操作 Esp8266的Flash为4M,其中1M用于存储程序,其他的空间有一部分用于系统,3M中剩下的大部分空间可以用来存放文件. #include <FS.h> St ...
- c语言指针flash,STM32F103RCT6之FLASH读写操作
一.STM32F103的FLASH简介 1.如图所示,STM32F103内部FLASH存储区分为三个区域:主存储区.信信息块和闪存存储器接口寄存器. 储存储区是我们读写FLASH的主要的存储区,MCU ...
最新文章
- 外星人台式电脑_戴尔外星人Alienware m17笔记本19款真机评测
- yolov3模型识别不出训练图片_YOLOv3训练自己的模型
- 基于spark mllib_Spark高级分析指南 | 机器学习和分析流程详解(下)
- 被低估的css滤镜,你所不知道的 CSS 滤镜技巧与细节
- Python中的高效的集合操作
- 卡在登陆界面进不去_穿越火线第十三年:需要的不仅仅是新界面,重要还是留住旧玩家...
- apache应用进阶
- CSS:你真的会用 z-index 吗?
- 制作FAT12软盘以查看软盘的根目录条目+文件属性+文件内容
- python numpy.random模块中提供啦大量的随机数相关的函数
- 英特尔中国祝贺高亭宇夺冠:至强CPU提供更精准训练支持
- 一份完整的建模文档需涵盖的模块与指标
- javascript 网页自动跳转
- OpenFileDialog/SaveFileDialog 中 Filter用法?
- 中国首台云电脑全面解析——天霆云计算董事长谈天霆专访
- 网络安全学习路线(顶级白帽黑客)
- 微信授权登录:移动端[unionid](一)
- csgo笔记本党设置4:3画面
- 滴滴新算法让你应对女友?道翰天琼认知智能机器人平台API接口大脑为您揭秘-64
- word、excel文档内容更新技术方案
热门文章
- php报纸源码,PHP报纸在线阅读程序 电子读报系统 杂志在线阅读源码 DM阅读源码...
- 我们文本分析了贾跃亭2017年全部公开信,发现他近期喜欢用“责任”“致歉”...
- 纽约摄影教程读书笔记
- 云计算时代——本质、技术、创新、战略
- Anti-Roll Bar -- 防倾杆
- 沪江前端由H5页面引起的一场前端数据结构讨论 1
- JS设计“网页在线编辑器”
- 将来我一定将他(科比)讲给你听!特别是你在遇到坎坷,感到迷茫的时候!
- 新手入门WPF之TreeView控件(一)
- 美拍应该如何引流?如何利用美拍引流?美拍引流方法