单片机-FLASH软件模拟EEPROM,提升flash保存重要参数的使用寿命

目录

  1. FLASH和EEPROM读写数据的对比
  2. FLASH模拟EEPROM的原理
  3. FLASH模拟EEPROM的优点
  4. 实战项目工程代码

1. FLASH和EEPROM读写数据的对比

1.1 擦除操作

  • EEPROM通常支持按单字节擦除和写入,这一特性使其非常适合需要频繁更新小量数据的应用场景。
  • Flash存储器通常是按块、扇区、页擦除的,这意味着即使只更改一个字节的数据,也需要擦除整个扇区或页。

1.2 写入速度

  • EEPROM的写入速度通常比Flash快,尤其是在擦除和重新编程单个字节时。
  • Flash的写入速度较慢,特别是在擦除操作期间,因为它涉及到更大的存储区域。

1.3 写入次数限制

  • EEPROM和Flash都有写入次数限制,但EEPROM通常设计为能够承受更多的擦写循环,通常达到百万次级别。
  • Flash存储器的擦写次数限制较低,通常在10,000到100,000次之间;对于频繁擦写数据的操作,易造成flash损坏。

1.4 数据保持能力

  • EEPROM 的数据保持时间通常较长,一般可达到 20 年以上,这使得它非常适合长期保存关键数据的应用场景。
  • FLASH 的数据保持时间通常在 10 年左右,虽然也能满足大多数应用的需求,但相较于 EEPROM 仍略逊一筹。

基本特性对比

特性FLASHEEPROM
存储单元结构NAND(共享位线,高密度)/ NOR(独立位线,高速)双晶体管结构(控制栅 + 存储栅,支持字节操作)
擦除单位Block(块擦除)Byte(字节级擦除)
写入速度0.2-10ms/page1-10ms/byte
擦除时间1-300ms/block (NOR Flash)字节擦除与写入同时完成(无需单独擦除步骤)
擦写次数10K-100K次100K-1M次
存储密度高(适合大容量存储)低(适合小数据存储)
功耗较高(需要高压擦除)较低
典型应用场景操作系统存储、文件系统、代码 / 数据批量存储配置参数存储、少量数据频繁读写

2. FLASH模拟EEPROM的原理

2.1 划分区域

  • 单片机的 FLASH 通常由多个页或扇区组成,在写入数据前,必须按页或扇区进行擦除操作;
  • 可将 FLASH 按页或扇区划分为多个区域,其中一个区域用于存储程序代码,其他区域则可用于数据存储,从而实现模拟 EEPROM 的功能;

2.2 擦除和写数据

  1. 在首次使用或存储空间不足时,对模拟 EEPROM 的 FLASH 区域进行整体擦除,并记录 FLASH 剩余空间大小,其初始值等于分配给模拟 EEPROM 的总空间大小;
  2. 通过偏移写地址写入新的数据,每次写入后,FLASH 剩余空间大小相应减去写入数据的长度;
  3. 当 FLASH 空间即将写满或剩余空间不足时,重复执行步骤 1 和步骤 2,即先擦除整个区域,再重新写入数据;

2.3 磨损均衡技术

  • 为了延长 FLASH 的使用寿命,通常会采用磨损均衡技术,将擦写操作均匀分布到各个块或扇区,避免某些区域过早损坏。
  • 常见的磨损均衡算法有简单的顺序循环、基于计数的动态分配以及更复杂的基于使用频率的智能分配等。

2.4 数据结构设计

  • 为了实现高效的数据管理,通常会设计特定的数据结构,如使用索引表记录每个数据项的存储位置和状态。
  • 在写入新数据时,可以采用追加写入的方式,而不是直接覆盖原有数据,从而提高写入效率并减少擦除次数。

2.5 FLASH读写操作示意图

在这里插入图片描述


在这里插入图片描述


3. FLASH模拟EEPROM的优点

  • 降低设计成本:EEPROM通常提供较小的存储容量,但单位容量的成本可能较高。Flash存储器通常提供更大的存储容量,且单位容量的成本较低;
  • 提升写数据速度:FLASH模拟EEPROM,写数据前是先整体擦除区域,再对同一个扇区或页分段写入数据,写入过程中不再涉及擦除FLASH操作,提升了写数据的速度;
  • 提高写数据次数:FLASH擦写数据是有次数限制的,单个扇区或页通过偏移地址可多次写数据后再擦除 FLASH,极大减少了FLASH擦除次数,提高了可写数据的次数;
  • 提高集成度:使用单片机的内置Flash减少了外部组件的数量,简化了电路设计和布局;

核心优势对比

优势维度传统EEPROMFLASH模拟方案
硬件成本需要独立芯片复用现有FLASH
存储容量通常<512KB可扩展至MB级别
系统集成度需要额外接口电路片上集成
可移植性依赖特定硬件纯软件实现
功耗表现静态功耗较高静态功耗趋近于零

4. 实战项目工程代码

可使用的flash 软件模拟EEPROM lib功能库 :https://download.csdn.net/download/weixin_43176196/90797265

flash 软件模拟EEPROM 项目工程程序 :https://download.csdn.net/download/weixin_43176196/90789413


需将功能模块 eepromSoft 添加到工程下,并实现 flash读写操作.具体应用如下

4.1 通用接口定义

eepromSoft.h
//  eeprom 可分配使用的个数, 最大不超过 5
#define  EPROM_SOFT_NUM   (5 )// 分配页缓冲区大小, 该数值必须为使用 pageSize 最大的一个 EEPROM_SOFT_INDEX 的 pageSize
#define EPROM_BUF_DATA_SIZE  (128 * 2)typedef int32_t EEPROM_SOFT_INDEX; // 索引类型// eeprom 错误码
typedef enum{EPROM_ERR_NORMAL     = 0, // 正常EPROM_ERR_UNDEFINE,       // 未知错误,可能为传入的参数无效EPROM_ERR_MEMBUF,         // 数据缓冲区设置出错EPROM_ERR_READ,           // 读失败EPROM_ERR_WRITE,          // 写错误EPROM_ERR_ERASE,          // 擦除扇区错误EPROM_ERR_OVER_DATA,      // 数据过大,会超出页大小EPROM_ERR_PAGE_SIZE,      // 分配的页大小不正确EPROM_ERR_ADDR,           // 分配的 falsh 的 adrrStart 或 adrrEnd 不正确EPROM_ERR_FUNC,           // 设置的函数指针无效EPROM_ERR_SOFT_NUM,       // 分配的个数不足,需设大 EPROM_SOFT_NUMEPROM_ERR_NUM_SET,        // EPROM_SOFT_NUM 设置不符合要求EPROM_ERR_LOCK,           // 加锁失败EPROM_ERR_INDEX,          // 未知索引号EPROM_ERR_NUM
}EPROM_ERR;/* eeprom 基准信息数据结构 */
typedef struct{uint32_t adrrStart; // 分配的 flash 扇区起始地址(此地址必须为4的倍数,且不能为0)uint32_t adrrEnd;   // 分配的 flash 扇区结束地址(此地址必须为4的倍数)// 设置的页大小(该数值必须是 128 的倍数),进行 falsh 的页分配,单位:Byte // 页大小并不代码实际存放数据的最大长度,每页里面会保存基准信息uint32_t pageSize;  /* flash 读函数指针,// flashAddr,要读取数据的flash地址;// buf, 读取数据缓冲的指针// readLen, 要读取数据的长度(单位:字节)// 返回, 实际读取到的数据长度  */uint32_t (*fRead)(uint32_t flashAddr, uint8_t *buf, uint32_t readLen);/* flash 写函数指针(不带擦除扇区写入数据),// flashAddr,开始写入数据的flash地址;// data, 要写入的数据指针// len, 要写入的数据长度(单位:字节)// 返回, 0, 写入成功//       1, 写入失败*/uint8_t (*fWrite)(uint32_t flashAddr,const uint8_t *data, uint32_t len);/* flash 擦除函数指针,// flashAddr,要擦除flash数据的起始地址;// len, 要擦除数据的数据长度(单位:字节)// 返回, 0, 成功//       1, 失败*/uint8_t (*fErase)(uint32_t flashAddr, uint32_t len);}EPROM_SOFT_INFO;// 声明外部其他文件定义的 crc32校验函数; 若只模拟一个eeprom 可进行简单求和校验 .
extern uint32_t AlgorithCrc32(const uint8_t* data, uint32_t len); /*!* * @param[in]  timeOutMs :毫秒超时时间* @param[out] none* @return 1 :加锁成功* @return 0 :加锁失败* * @brief  声明外部其他文件定义的 加锁保护机制.* @note  . 主要用于 EPROM_BUF_DATA_SIZE 分配的数据缓冲区资源保护;*        */
extern uint8_t EepromSoftLock(uint32_t timeOutMs);extern void EepromSoftUnLock(void); // 释放锁/*!* * @param[in]  num : eeprom 可模拟的个数,取值 1 - 10;* @param[in]  pageBuf : 要分配的页缓冲区指针;* @param[in]  pageSize : 页缓冲区的大小(必须为 128的倍数,单位:字节);* @param[out] none* @return EPROM_ERR 错误码* * @brief  模拟eeprom 公共资源分配设置* @note   . 必须在 EepromSoftInit() 函数使用前,调用该函数执行一次;*        */
EPROM_ERR EepromSoftSet(uint8_t num, uint8_t* pageBuf, uint32_t pageSize);/*!* * @param[in]  info : 要配置的基准信息结构指针* @param[out] errCode :EPROM_ERR 返回指针,存放返回的错误码* @return >=0的数值 : eeprom 索引号* @return 其他值: 出错* * @brief  初始化基准信息,若成功,就返回对应 eeprom 的索引号.* @note   . 该操作会检测分配的地址是否正确,并获取上次写入有效数据的基准信息;*         . 若未获取到有效基准信息,就擦除扇区,从起始地址开始读/写数据;*        */
EEPROM_SOFT_INDEX EepromSoftInit(EPROM_SOFT_INFO* info, EPROM_ERR* errCode);/*!* * @param[in]  index :eeprom索引号* @param[in]  buf :数据缓冲区,存放读取到的数据* @param[in]  readLen :要读取的数据长度* @param[out] errCode :EPROM_ERR 返回指针,存放返回的错误码* @return 实际读取到的数据长度* * @brief  读数据* @note   . 从上次成功写入数据的地址读取数据;*        */
uint32_t EepromSoftRead(EEPROM_SOFT_INDEX index, uint8_t *buf, uint32_t readLen, EPROM_ERR* errCode);/*!* * @param[in]  index :eeprom索引号* @param[in]  data: 要写入的数据指针* @param[in]  len:要写入的数据长度* @param[out] none* @return PROM_ERR 错误码* * @brief  写入数据* @note   . 写数据之前会将上次成功写入的地址进行偏移,若数据成功写入数据,就记录该地址;*        */
EPROM_ERR EepromSoftWrite(EEPROM_SOFT_INDEX index, uint8_t *data, uint32_t len);

使用方法:

    1. 在其他文件,实现函数 AlgorithCrc32()、 EepromSoftLock() 、 EepromSoftUnLock() ;
    1. EepromSoftSet() 分配模拟 eeprom的个数,页缓冲区(多个eeprom的公共缓冲区);
      该函数必须在 EepromSoftInit() 使用之前调用一次;
    1. EepromSoftInit() 初始化 eeprom 的基准信息,初始化成功会返回 EEPROM_SOFT_INDEX 索引号 ;
    1. EepromSoftRead()、 EepromSoftWrite() 进行数据读、写操作;

4.2 接口应用

/* USER CODE BEGIN 4 */// 定义 crc32校验
uint32_t AlgorithCrc32(const uint8_t* data, uint32_t len)
{uint32_t val = 0;for(int i = 0; i < len; i++){val += data[i];}return val;
}// 定义保护锁机制
uint8_t s_LockStatus = 0;
uint8_t EepromSoftLock(uint32_t timeOutMs)
{s_LockStatus = 1;return s_LockStatus;
}// 释放锁
void EepromSoftUnLock(void)
{s_LockStatus = 0;
}// 定义模拟 eeprom 缓冲区
static uint8_t s_EepromBuf[EPROM_BUF_DATA_SIZE];EEPROM_SOFT_INDEX s_EepronIndex1 = -1, s_EepronIndex2 = -1; // 定义索引变量void TestFlashHandle(void)
{unsigned char buf[20 + 8] = {0x11, 0x22, 0x33, 0x44, 0x55};EPROM_ERR errCode = 0;uint32_t len = 0;EPROM_SOFT_INFO eepromInfo;// 设置 eeprom 公共资源分配errCode =  EepromSoftSet(EPROM_SOFT_NUM, s_EepromBuf, EPROM_BUF_DATA_SIZE);// 模拟 第一个 eepromeepromInfo.adrrStart = FLASH_ADDR_SECTOR_9;   // 设置模拟eeprom 的扇区起始地址eepromInfo.adrrEnd   = FLASH_ADDR_SECTOR_10; // 设置模拟eeprom 的扇区结束地址eepromInfo.pageSize  = 128; // 每次写数据操作的flash大小(最小128)eepromInfo.fErase    = FlashHardErase;eepromInfo.fRead     = FlashHardRead;eepromInfo.fWrite    = FlashWriteNoErase;s_EepronIndex1 = EepromSoftInit(&eepromInfo, &errCode); // 初始化基准信息if(s_EepronIndex1 < 0) {return ;}// 写数据buf[16] = 0xAA;buf[17] = 0xBB;buf[18] = 0xCC;buf[19] = 0xDD;errCode = EepromSoftWrite(s_EepronIndex1, buf, 20);// 读数据memset(buf, 0, 20);len = EepromSoftRead(s_EepronIndex1, buf, 20, &errCode);// 模拟 第二个 eepromeepromInfo.adrrStart = FLASH_ADDR_SECTOR_10;   // 设置模拟eeprom 的扇区起始地址eepromInfo.adrrEnd   = FLASH_ADDR_SECTOR_11; // 设置模拟eeprom 的扇区结束地址eepromInfo.pageSize  = EPROM_BUF_DATA_SIZE; // 每次写数据操作的flash大小(最小128)eepromInfo.fErase    = FlashHardErase;eepromInfo.fRead     = FlashHardRead;eepromInfo.fWrite    = FlashWriteNoErase;s_EepronIndex2 = EepromSoftInit(&eepromInfo, &errCode); // 初始化基准信息if(s_EepronIndex2 < 0) {return ;}// 写数据buf[16] = 0xA1;buf[17] = 0xB1;buf[18] = 0xC1;buf[19] = 0xD1;errCode = EepromSoftWrite(s_EepronIndex2, buf, 20);// 读数据memset(buf, 0, 20);len = EepromSoftRead(s_EepronIndex2, buf, 20, &errCode);// 再读取 s_EepronIndex1 ,观察是否有变化memset(buf, 0, 20);len = EepromSoftRead(s_EepronIndex1, buf, 20, &errCode);}




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/904793.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

探索Stream流:高效数据处理的秘密武器

不可变集合 stream流 Stream流的使用步骤&#xff1a; 先得到一条Stream流&#xff08;流水线&#xff09;&#xff0c;并把数据放上去 使用中间方法对流水线上的数据进行操作 使用终结方法对流水线上的数据进行操作 Stream流的中间方法 注意1&#xff1a;中间方法&#xff0…

vue3笔记(自存)

1. Vue3简介 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;n 经历了&#xff1a;4800次提交、40个RFC、600次PR、300贡献者 官方发版地址&#xff1a;Release v3.0.0 One Piece vuejs/core 截止2023年10月&#xff0c;最…

实验4 mySQL查询和视图

一、实验目的 掌握SELECT语句的基本语法多表连接查询GROUP BY的使用方法。ORDER BY的使用方法。 二、实验步骤、内容、结果 实验内容&#xff1a; 实验4.1数据库的查询 目的与要求 (1)掌握SELECT语句的基本语法。 (2)掌握子查询的表示。 (3)掌握连接查询的表示。 (4)掌…

【bug】fused_bias_act_kernel.cu卡住没反应

简述 在推理人脸修复face restoration算法 GPEN的时候&#xff0c;发现有时候fused_bias_act_kernel.cu卡住没反应。 解决 清理下缓存&#xff0c;让程序自己再编译下

.net/C#进程间通信技术方案总结

C# 应用进程间通信(IPC)技术方案 进程间通信(Inter-Process Communication, IPC)是不同进程之间交换数据和消息的机制。以下是C#中常用的IPC技术方案&#xff1a; 1. 命名管道(Named Pipes) 适用于本地机器上的进程通信&#xff0c;支持双向通信。 ​​服务端示例​​&…

阿里云服务器数据库故障排查指南?

阿里云服务器数据库故障排查指南? 以下是针对阿里云服务器&#xff08;如ECS自建数据库或阿里云RDS等托管数据库&#xff09;的故障排查指南&#xff0c;涵盖常见问题的定位与解决方案&#xff1a; 一、数据库连接失败 检查网络连通性 ECS自建数据库 确认安全组规则放行数据库…

深度学习 ———— 迁移学习

迁移学习原理 什么是迁移学习&#xff1f; 迁移学习利用在大规模数据集&#xff08;如ImageNet&#xff09;上预训练的模型&#xff0c;改装小数据集&#xff08;如CIFAR-10&#xff09;。优势&#xff1a; 减少训练时间&#xff1a;预训练模型已学习通用特征&#xff08;如边…

单调栈模版型题目(3)

单调栈型题目贡献法 基本模版 这是数组a中的 首先我们要明白什么叫做贡献&#xff0c;在一个数组b{1,3,5}中&#xff0c;连续包含1的连续子数组为{1}&#xff0c;{1,3}&#xff0c;{1,3,5}&#xff0c;一共有三个&#xff0c;这三个数一共能组成6个连续子数组&#xff0c;而其…

日常知识点之随手问题整理(思考单播,组播,广播哪个更省带宽)

新入职的公司在某些场景下无脑使用组播技术&#xff0c;自己突然就意识到一个问题&#xff1a;单播&#xff0c;组播&#xff0c;广播&#xff0c;哪个更省带宽&#xff1f; 有所收获&#xff0c;做点笔记&#xff0c;仅仅是个人理解~ 1&#xff1a;简单理解 单播&#xff1…

R1-Omni

一、Omni概述 Omni 文本视频音频&#xff0c;全模态。 R1Omni 强化学习全模态。 二、Omni举例-humanOmni humanOmni&#xff1a;以人体姿态和人物交互为中心的全模态模型。 visual projector有3个&#xff0c;分别负责人脸标签、姿态检测、人和物交互。有点像moe。text enc…

linux中的日志分割

1.问题背景&#xff0c;nginx日志过大不好删除 [rootlocalhost cron.daily]# cd /lk/nginx/log/ [rootlocalhost log]# ll 总用量 2386188 -rw-r--r--. 1 root root 2078699697 5月 9 13:02 access.log -rw-r--r--. 1 root root 11138 5月 6 10:28 error.log [rootloc…

华为云Flexus+DeepSeek征文|从开通到应用:华为云DeepSeek-V3/R1商用服务深度体验

前言 本文章主要讲述在华为云ModelArts Studio上 开通DeepSeek-V3/R1商用服务的流程&#xff0c;以及开通过程中的经验分享和使用感受帮我更多开发者&#xff0c;在华为云平台快速完成 DeepSeek-V3/R1商用服务的开通以及使用入门注意&#xff1a;避免测试过程中出现部署失败等问…

【机器学习-线性回归-5】多元线性回归:概念、原理与实现详解

线性回归是机器学习中最基础且广泛应用的算法之一&#xff0c;而多元线性回归则是其重要扩展。本文将全面介绍多元线性回归的核心概念、数学原理及多种实现方式&#xff0c;帮助读者深入理解这一强大的预测工具。 1. 多元线性回归概述 1.1 什么是多元线性回归 多元线性回归(…

GOC指令

网络版GoC常见绘图命令说明 &#xff08;V3.8&#xff09; 目录 l 基本画图命令 fd, bk, lt, rt l 设置笔状态命令 c, rgb, size, up, down l 状态命令 show, hide, speed, showXY, wait, pause, cls, clsRec l 增强画图命令 o, oo, e, ee, r, rr l 坐标命令 moveTo, lineTo, g…

Qt获取CPU使用率及内存占用大小

Qt 获取 CPU 使用率及内存占用大小 文章目录 Qt 获取 CPU 使用率及内存占用大小一、简介二、关键函数2.1 获取当前运行程序pid2.2 通过pid获取运行时间2.3 通过pid获取内存大小 三、具体实现五、写在最后 ​ 一、简介 近期在使用软件的过程中发现一个有意思的东西。如下所示&a…

期刊论文写作注意点

下面给出关于期刊写作的几个关键注意点 一、摘要突出创新点 最重要的是论文的摘要&#xff0c;因为在论文送审的时候&#xff0c;编辑如果没有时间&#xff0c;最先看的就是摘要。摘要要写好。如果投的是顶刊&#xff0c;在摘要里面尽量不要写是在什么方法的基础上进行改进之类…

Swagger 3.0 中注解详细示例

Swagger 3.0 提供了丰富的注解来详细描述 API 的请求和响应。以下是一个使用 Operation、Parameter、RequestBody 和 ApiResponse 注解的示例&#xff0c;展示了如何设置请求头、请求参数、路径变量、请求体和响应体。代码中未使用 DTO 对象&#xff0c;而是使用 Map 来传递参数…

切比雪夫不等式专题习题解析

切比雪夫不等式专题习题解析 前言 本文为概率论习题集专栏的切比雪夫不等式专题习题解析,针对习题篇中的10道题目提供详细解答。希望通过这些解析帮助大家深入理解切比雪夫不等式的应用和意义。 一、基础概念题解析 习题1解析: 错误。切比雪夫不等式适用于任何具有有限方…

软件测试的概念

需求的概念 开发模型 测试模型 1. 什么是需求 在多数软件公司&#xff0c;会有两部分需求&#xff0c;⼀部分是⽤⼾需求&#xff0c;⼀部分是软件需求。 1.1 ⽤⼾需求 ⽤⼾需求&#xff1a;可以简单理解为甲⽅提出的需求&#xff0c;如果没有甲⽅&#xff0c;那么就是终端⽤⼾…

前端面试每日三题 - Day 29

这是我为准备前端/全栈开发工程师面试整理的第29天每日三题练习&#xff1a; ✅ 题目1&#xff1a;Web Components技术全景解析 核心三要素 Custom Elements&#xff08;自定义元素&#xff09; class MyButton extends HTMLElement {constructor() {super();this.attachShado…