零基础STM32单片机编程入门(二十五) 内部FLASH模拟EEPROM实战含源码

news2024/9/22 11:32:34

文章目录

    • 一.概要
    • 二.FLASH模拟EEPROM的优势
    • 三.FLASH模拟EEPROM的原理
    • 四.数据读写步骤
    • 五.数据转移流程图
    • 六.FLASH模拟EEPROM读写例程
    • 七.CubeMX工程源代码下载
    • 八.小结

一.概要

STM32F103C8T6是一款强大而灵活的微控制器,它的片内Flash存储器可以用来存储有关数据,但没有专门的EEPROM。EEPROM具有较高的可擦写次数,一般FLASH只有1万次擦写次数,EEPROM可以有100万次擦写次数,对于有些写入频繁的数据如果放在FLASH中存储,容易造成FLASH损坏,就有一种FLASH存储模拟EEPROM的方法,可以增加FLASH的擦写次数,不需要外挂一颗EEPROM,可以减少额外硬件成本。

在这里插入图片描述

二.FLASH模拟EEPROM的优势

由于FLASH在写入数据前,需要将FLASH数据先擦除为0xFF,而FLASH擦除时通常为扇区擦除,例如STM32F103C8T6的FLASH页大小为1K字节,这个特性决定了不能简单的将旧数据擦除然后写新数据,因为这样会导致存储在这个页内的其他数据也被擦除,并且也会导致FLASH频繁擦除而降低其使用寿命。
所以FLASH模拟EEPROM的思路是:
1.新数据存储不影响旧数据。
2.尽量减少FLASH擦除次数,延长FLASH使用寿命。

FLASH模拟EEPROM的优点:
1.低成本:可节约一颗EEPROM芯片;
2.存储、读取速度快:通讯速度快于使用I2C或者SPI通讯的EEPROM元件;
3.抗干扰能力强:由于FLASH在单片机内部,不会存在通讯总线被外部干扰的问题;
4.容量可调:可根据实际使用,灵活调整存储空间大小。

三.FLASH模拟EEPROM的原理

首先使用2页FLASH空间,如果0页空间写满数据,那么把0页空间里面的【有效数据】复制到1页,如果1页数据满那么把1页空间里面的【有效数据】复制到0页,这样循环使用,当然如果你想增加使用寿命可以增加多页循环,官方例子只是按2页实现的例子。每页前面4字节保留,其中前2字节是该页状态标志。
在这里插入图片描述

存储结构:

存储的数据格式为数据 + 数据地址,地址和数据都是16位方式存储,每一次存储占用32位也就是4 个字节。图中data列为数据,data address列为数据地址,flash address列为数据存储的实际flash 地址偏移量。例如上图中页0的flash address=12处,数据为0x3003,数据地址为0x0002。

页状态标志:

在第一个数据存储区,存储页状态标志status,页状态标志有3种:
有效状态:VALID_PAGE,status = 0x0000,读取和写数据在此页进行。
数据转移状态:RECEIVE_DATA,status = 0xEEEE,另外一页满了,正在传输有效数据到本页。
擦除状态:ERASED,status = 0xFFFF。
在这里插入图片描述

四.数据读写步骤

数据写入:

每一次写入数据前,都会从页起始地址开始寻找第一个未存储数据的区域(值为0xFFFFFFFF),然后将待写入的数据和数据地址写到未存储数据的区域。例如上图中页0的flash address = 20处,值为0xFFFFFFFF,就是第一个未存储数据的区域。
当知道了页的大小后,就可以算出最大的变量存储个数:页容量/4-1。例如当页大小为1K时,最大可存储的变量数量为1024/4-1=255。需要注意的是,在实际使用中,应该尽量留出较多的空闲容量,这样可以减小FLASH擦除次数,提高FLASH寿命。
另外数据地址不可以超过最大能存储的变量数量,例如当页大小为1K时,最大可存储的变量数量为1024/4-1=255,那么数据地址data address不可以大于255。

数据读取:

每一次读取数据都会从页结束地址开始向前寻找最后一个存储的有效数据,例如现在要读取地址为0x0000 的数据。从上图中看到flash address = 4和flash address = 16都是地址为0x0000的数据,因为最后一次存储的数据为flash address = 16处的数据,所以此时读取地址0x0000的数据为0x1234。

五.数据转移流程图

当一页数据存满了之后,会将数据传输到空闲页,将会执行以下操作(以页0满,页1空为例):
将页1状态标记为数据传输状态(RECEIVE_DATA);
将页0所有有效数据复制到页1(注意是有效数据,不是所有数据,一个地址有效数据就是最新存入的一个数据);
擦除页0;
将页1状态标记为有效状态(VALID_PAGE)。

当两次保存同一虚拟地址的数据时如右图所示:从上到下,最新一个虚拟地址是0X7777对应的数据5454才是有效的,所以等Pag0满了,在转移的过程中只是把7777地址的数据只是把5454转移过去了,5555地址最新的数据是BDBD,6666地址最新的数据是6464。
在这里插入图片描述

六.FLASH模拟EEPROM读写例程

硬件准备:

STLINK接STM32F103C8T6小系统板,STLINK接电脑USB口。

在这里插入图片描述

打开STM32CubeMX软件,新建工程
在这里插入图片描述
Part Number处输入STM32F103C8,再双击就创建新的工程
在这里插入图片描述
配置下载口引脚
在这里插入图片描述
配置外部晶振引脚
在这里插入图片描述

配置系统主频
在这里插入图片描述
配置工程文件名,保存路径,KEIL5工程输出方式
在这里插入图片描述
生成工程
在这里插入图片描述
用Keil5打开工程
在这里插入图片描述
添加代码

添加EEPROM.C文件
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

主要代码

uint16_t VirtAddVarTab[NB_OF_VAR] = {0x5555, 0x6666, 0x7777};
uint16_t VarDataTab[NB_OF_VAR] = {0, 0, 0};
uint16_t VarValue = 0;
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();//初始化1毫秒 Tick

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();//外部8M晶振,系统72M主频

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
  /* Unlock the Flash Program Erase controller */
  HAL_FLASH_Unlock();

  /* EEPROM Init */
  EE_Init();

/* --- Store successively many values of the three variables in the EEPROM ---*/
  /* Store 0x1000 values of Variable1 in EEPROM */
  for (VarValue = 1; VarValue <= 0x1000; VarValue++)
  {
    EE_WriteVariable(VirtAddVarTab[0], VarValue);//0x5555地址存入数据,最后一个数据是0x1000
  }

  /* read the last stored variables data*/
  EE_ReadVariable(VirtAddVarTab[0], &VarDataTab[0]);//从0x5555读取数据


  /* Store 0x2000 values of Variable2 in EEPROM */
  for (VarValue = 1; VarValue <= 0x2000; VarValue++)
  {
    EE_WriteVariable(VirtAddVarTab[1], VarValue);//0x6666地址存入数据,最后一个数据是0x2000
  }

  /* read the last stored variables data*/
  EE_ReadVariable(VirtAddVarTab[0], &VarDataTab[0]);//从0x5555读取数据
  EE_ReadVariable(VirtAddVarTab[1], &VarDataTab[1]);//从0x6666读取数据


  /* Store 0x3000 values of Variable3 in EEPROM */
  for (VarValue = 1; VarValue <= 0x3000; VarValue++)
  {
    EE_WriteVariable(VirtAddVarTab[2], VarValue);//0x7777地址存入数据,最后一个数据是0x3000
  }

  /* read the last stored variables data*/
  EE_ReadVariable(VirtAddVarTab[0], &VarDataTab[0]);//从0x5555读取数据
  EE_ReadVariable(VirtAddVarTab[1], &VarDataTab[1]);//从0x6666读取数据
  EE_ReadVariable(VirtAddVarTab[2], &VarDataTab[2]);//从0x7777读取数据
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */
}
#define ADDR_FLASH_PAGE_32    ((uint32_t)0x08008000) /* Base @ of Page 32, 1 Kbytes */
/* Define the size of the sectors to be used */
#define PAGE_SIZE               (uint32_t)FLASH_PAGE_SIZE  /* Page size */

/* EEPROM start address in Flash */
#define EEPROM_START_ADDRESS  ((uint32_t)ADDR_FLASH_PAGE_32) /* EEPROM emulation start address */

/* Pages 0 and 1 base and end addresses */
#define PAGE0_BASE_ADDRESS    ((uint32_t)(EEPROM_START_ADDRESS + 0x0000))
#define PAGE0_END_ADDRESS     ((uint32_t)(EEPROM_START_ADDRESS + (PAGE_SIZE - 1)))
#define PAGE0_ID               ADDR_FLASH_PAGE_32

#define PAGE1_BASE_ADDRESS    ((uint32_t)(EEPROM_START_ADDRESS + 0x10000))
#define PAGE1_END_ADDRESS     ((uint32_t)(EEPROM_START_ADDRESS + 0x10000 + PAGE_SIZE - 1))
#define PAGE1_ID               ADDR_FLASH_PAGE_96

/* Used Flash pages for EEPROM emulation */
#define PAGE0                 ((uint16_t)0x0000)
#define PAGE1                 ((uint16_t)0x0040)

/* No valid page define */
#define NO_VALID_PAGE         ((uint16_t)0x00AB)

/* Page status definitions */
#define ERASED                ((uint16_t)0xFFFF)     /* Page is empty */
#define RECEIVE_DATA          ((uint16_t)0xEEEE)     /* Page is marked to receive data */
#define VALID_PAGE            ((uint16_t)0x0000)     /* Page containing valid data */

/* Valid pages in read and write defines */
#define READ_FROM_VALID_PAGE  ((uint8_t)0x00)
#define WRITE_IN_VALID_PAGE   ((uint8_t)0x01)

/* Page full define */
#define PAGE_FULL             ((uint8_t)0x80)

调试结果如下图所示,地址0x5555,0x6666,0x7777地址里存的数据都是每个地址空间最后一个存入的数据。
在这里插入图片描述

七.CubeMX工程源代码下载

通过百度网盘分享的文件:25.片上FLASH模拟EEPROM实验.rar
链接:https://pan.baidu.com/s/16gcHMxPJIImZp2323F1wrA
提取码:pkb8
如果链接失效,可以联系博主给最新链接
程序下载下来之后解压就行

八.小结

因为片内Flash的擦写次数有限,所以要加上特定法来增加使用寿命,此算法ST提供了例程,可以移植到程序里直接使用,比较方便。

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

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

相关文章

sqli-labs-php7-master第5-10关

第五关&#xff1a; 根据提示输入ID,随便来个一 输入100&#xff0c;因为数据库没有&#xff0c;所以这里没输出内容 还是先找注入点&#xff1a;输入单引号试试 注入点找到了‘ 查询数据库列数&#xff1b;&#xff1f;id1 order by 4 -- 测试发现order by 3时页面正常&…

C++笔试练习笔记【5】:最小花费爬楼梯(有题目链接) 初识动态规划

文章目录 题目思路代码 动态规划简介**一、什么是动态规划****二、动态规划的应用场景****三、动态规划的基本步骤****四、动态规划的优缺点** 题目 题目链接&#xff1a;https://www.nowcoder.com/practice/9b969a3ec20149e3b870b256ad40844e?tpld230&tpld39751&ru/…

探索人工智能技术的发展导致知识崩溃危险的可能性

概述 本文分析了人工智能&#xff08;AI&#xff09;技术的发展在缩小人类知识库方面的潜力。 作者认为&#xff0c;如果大语言模型&#xff08;LLMs&#xff09;等人工智能技术迅速发展&#xff0c;人工智能生成的内容成为人类接触的大部分信息&#xff0c;那么长尾知识&…

python图表没有正确显示中文,这通常是因为matplotlib的默认设置不支持中文字符,或者相应的字体没有正确加载。

如果图表没有正确显示中文&#xff0c;这通常是因为matplotlib的默认设置不支持中文字符&#xff0c;或者相应的字体没有正确加载。你可以通过指定支持中文的字体来解决这个问题。下面是如何设置matplotlib以确保能够在图表中显示中文的步骤&#xff1a; 方法1&#xff1a;全局…

python的多线程

python的threading模块&#xff0c;它提供了丰富的接口来创建和管理线程。 定义一个函数print_numbers&#xff0c;这个函数将由线程执行。在这个函数中&#xff0c;我们使用一个循环来打印数字&#xff0c;并使用time.sleep(1)来模拟每个数字打印之间有1秒的延迟。 在 if __…

Windows应急响应-排查方式

目录 Windows应急响应排查流程一、账户排查排查方法&#xff08;1&#xff09;查看用户信息&#xff08;2&#xff09;lusrmgr.msc手动查&#xff08;比较麻烦&#xff09;&#xff08;3&#xff09;检测克隆账户 ---可使用安全工具D盾进行检测&#xff0c;同时可以直接查看端口…

群辉NAS利用AList搭建混合云盘⑥挂接腾讯微云

目录 ……接前文 5、挂接腾讯微云 未完待续…… ……接前文 5、挂接腾讯微云 登录AList后台→管理→存储→驱动供选择“腾讯微云”→填写挂接路径 打开“配置文档”(详见前文) 打开配置文档→简体中文→开始→找到腾讯微云部分,可以看到关于Cookie的设置方法。 手工用…

第十二章 元数据管理10分

12.1 引言 如果没有元数据&#xff0c;组织可能根本无法管理其数据。 ISO/IEC11179 元数据注册标准。 元数据管理原则&#xff1a;应归尽归&#xff0c;应收尽收。衡量标准&#xff1a;目录是否完整。&#xff08;去第十二章 元数据管理&#xff09;。 主数据管理&#xff1a;主…

(Javaweb)Ajax,Axios,Vue

目录 一.Ajax 二.Axios 三.前端工程化 四.接口文档的管理平台YAPI 五.Vue项目 六.Vue项目开发流程 一.Ajax 1.通过Ajax从服务器端获取数据 Ajax---JavaScript&#xff08;网页行为&#xff09;XML&#xff08;标记语言--用来存储数据&#xff09; 客户端--浏览器 服务…

【C++】深入理解类和对象(1)

自己打败自己是最可悲的失败&#xff0c;自己战胜自己是最可贵的胜利。&#x1f493;&#x1f493;&#x1f493; 目录 ✨说在前面 &#x1f34b;知识点一&#xff1a;类的定义 • &#x1f330;1.类定义格式 • &#x1f330;2.访问限定符 • &#x1f330;3.类域 &…

人工智能时代,程序员当如何保持核心竞争力?

目录 前言 一.AI辅助编程对程序员工作的影响 二.程序员应重点发展的核心能力 三.人机协作模式下的职业发展规划 结束语 前言 随着AIGC&#xff08;如chatgpt、midjourney、claude等&#xff09;大语言模型接二连三的涌现&#xff0c;AI辅助编程工具日益普及&#xff0c;程序…

C++第三十一弹---C++继承机制深度剖析(下)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 1.菱形继承及菱形虚拟继承 1.1 单继承 单继承&#xff1a;一个子类只有一个直接父类时称这个继承关系为单继承。 Student的直接父类是Person&#xff…

双向循环链表和内核链表

目录 双向循环链表 结构设计 初始化 插入 删除 遍历&#xff08;顺序/逆序&#xff0c;打印输出&#xff09; 查找 主函数 内核链表 内核链表初始化定义 内核链表的插入定义 内核链表的遍历定义 内核链表剔除节点定义 内核链表如何移动节点定义 内核链表的应用 临时补充…

身在职场,不得不提防的几个问题,能让少走许多弯路

职场路本就崎岖&#xff0c;如果再走了弯路&#xff0c;脚下的路将会更漫长且难走。 谁不想一帆风顺&#xff0c;可谁又能一帆风顺&#xff1f;不是人心险恶&#xff0c;而是立场本就不同&#xff0c;为了各自的利益考虑无可厚非。 你可以说凭借能力获取利益&#xff0c;为什…

CVE-2023-37569~文件上传【春秋云境靶场渗透】

# 今天我们拿下CVE-2023-37569这个文件上传漏洞# 经过简单账号密码猜测 账号&#xff1a;admin 密码&#xff1a;password# 找到了文件上传的地方# 我们直接给它上传一句话木马并发现上传成功# 上传好木马后&#xff0c;右键上传的木马打开发现上传木马页面# 直接使用蚁剑进行连…

Linux5:Shell编程——函数、重定向

目录 前言 一、函数 1.函数结构 2.函数实例 3.函数传参 二、重定向 1.输出重定向 2.输入重定向 3.同时使用 4.重定向深入了解 5.垃圾桶 总结 前言 Shell编程将会在本章完结 一、函数 1.函数结构 #!/bin/sh # 函数function fun1() {echo "this is a funtion&q…

【有手就行】:从无到有搭建后端SpringBoot项目

前言 想静下心来写点东西&#xff0c;但是确实想不到该写点啥&#xff0c;可能是少了点感觉吧 &#x1f622;。前面刚整理了下前端VUE&#xff0c;就想了下把后端也一起整理下吧&#xff0c;免得换电脑了安装环境又要弄半天&#xff0c;那就开搞吧 首先 准备环境 1.安装IDEA…

云计算实训21——mysql-8.0.33-linux-glibc安装及使用

一、mysql-8.0.33-linux-glibc安装 安装步骤 1.解压 tar -xvf mysql-8.0.33-linux-glibc2.12-x86_64.tar.xz 2.清空其他环境 rm -rf /etc/my.cnf 3.安装依赖库 yum list installed | grep libaio 4.创建用户 useradd -r -s /sbin/nologin mysql 查看 id mysql 5.创建mysql-fi…

PXE批量网络装机(超详细实验教程)教会你自动化批量安装linux 系统 红帽7

1.创建自动化安装服务器 1.1. 搭建本地厂库 写入rpm.re文件内容 [rhel7]namerhel7baseurlfile:///rhel7gpgcheck0 Yum makecache 测试是否挂载成功 1.2.关闭虚拟机的本地DHCP 1.3下载必要软件 下载图形化脚本自动生成工具方便编写脚本 下载dhcp分配ip httpd 搭建网页 …

数据排序之旅

1、排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#xff0c;若经过排序…