打印
[其他ST产品]

MCU之FLASH模拟EEPROM

[复制链接]
172|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
v26g7l|  楼主 | 2023-5-29 16:09 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
原始文件ST 官方有例子和文档:AN2594
http://www.st.com/mcu/familiesdocs-110.html
看到不少网上使用官方例子程序不成功的问题,我估计大概是没详细阅读官方文档的原因吧,也许很多人没理
解官方例子的原理。那么下面就详细说明一下原理再说如何优化。
原理如下:



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



使用特权

评论回复
沙发
v26g7l|  楼主 | 2023-5-29 16:10 | 只看该作者
下面的图显示数据在FLASH 中的保存格式:

保存数据是16 位的,后面16 位是该数据的虚拟地址,注意:1 个数据有唯1 个虚拟地址,地址必须为:0~0xfffe
范围内(每页将按4 字节分块,1 块保存1 个16 位数据)。下面继续说明16 位虚拟地址的作用。

使用特权

评论回复
板凳
v26g7l|  楼主 | 2023-5-29 16:10 | 只看该作者

Figure 3 显示了数据更新的过程:
1. 写数据
假设保存的数据虚拟地址是0X7777,那么程序写数据是从当前有效页页首地址开始查询虚拟地址位置为

0XFFFF 的空间,如果是0XFFFF 那么该位置可以保存数据;如果不是,那么继续找下1 个位置,如果本

页无0XFFFF 的空间那么表示本页已满,那么将本页【有效数据】复制到另外1 页继续保存数据。
当两次保存同一虚拟地址的数据时如下图所示:从上到下,第2 个虚拟地址是0X7777 对应的数据1245 才

使用特权

评论回复
地板
v26g7l|  楼主 | 2023-5-29 16:11 | 只看该作者
是有效的。清楚了这点,那么读数据要怎么处理基本就明白了。

使用特权

评论回复
5
v26g7l|  楼主 | 2023-5-29 16:11 | 只看该作者
2. 读数据
读数据时是从有效页的末尾地址开始检测是否是有效数据,如果是那么立即返回,程序是通过虚拟地址判断有
效数据的,第1 个匹配的虚拟地址的数据才是有效的
3. 页满时处理数据
说到这里,看到不少使用例子程序不成功的问题,那么就请注意下面了,他们的错误估计是下面的原因造成的。
当1 页写满时其实里面有很多无效数据,你只需要将【有效数据】复制到另外1 页就成。如何复制有效数据呢?
我想很多人估计忽略了 【#define NumbOfVar ((uint8_t)0x03) /* Variables' number */】,NumbOfVar 就是你程
序中实际要保存的数据量,这个必须与实际保持一致,不能多也不能少,这个如果不一致,那么在换页时将出
错,没换页之前倒是没问题的,原因在于:程序在换页时将根据NumbOfVar 的值复制有效数据的个数,如果比
实际少,那么换页时将丢失数据,如果比实际多那么将出现旧数据覆盖最新数据

使用特权

评论回复
6
v26g7l|  楼主 | 2023-5-29 16:12 | 只看该作者
错误的例子:
/* Variables' number */
#define NumbOfVar ((uint8_t)0x05)
uint16_t VirtAddVarTab[NumbOfVar] = {0, 1, 2};
//NumbOfVar 定义的比用的多实际是{0, 1, 2, 0, 0},虚拟地址0 的数据换页后将出现旧数据覆盖最新数据
int main(void)
{
uint16_t temp;
for (VarValue = 0; VarValue < 100; VarValue++)
{
EE_WriteVariable(VirtAddVarTab[0], VarValue+10);
}
for (VarValue = 0; VarValue < 500; VarValue++)
{
EE_WriteVariable(VirtAddVarTab[1], VarValue);
temp=0;
EE_ReadVariable(0, &temp);//不换页读出数据是对的,换页后读出数据错误
}
}
//====================================================================================
/* Variables' number */
#define NumbOfVar ((uint8_t)0x03)
uint16_t VirtAddVarTab[NumbOfVar] = {0, 1, 2};
//NumbOfVar 定义为3,下面用到虚拟地址超过VirtAddVarTab 表里面的值
int main(void)
{
uint16_t temp;
for (VarValue = 0; VarValue < 100; VarValue++)
{
EE_WriteVariable(VirtAddVarTab[0], VarValue);
}
for (VarValue = 0; VarValue < 50; VarValue++)
{
EE_WriteVariable(3, VarValue+2);
}
for (VarValue = 0; VarValue < 200; VarValue++)
{
EE_WriteVariable(2, VarValue);
temp=0;
EE_ReadVariable(3, &temp);//不换页读出数据是对的,换页后读出数据错误
}
}

使用特权

评论回复
7
v26g7l|  楼主 | 2023-5-29 16:13 | 只看该作者
STM32 FLASH 模拟EEPROM 使用注意:
不少人问该程序的FLASH 保存数据多少和使用寿命
保存数据多少跟FLASH 页大小有关,如果页大小是1K 那么只能保存1024/4-1=256-1 个16 位数据,如果你保
存8 位数,你可以2 个8 位数据组合后保存或者直接保存,如果保存32 位数据那就拆成2 个16 位保存,当然
关于寿命
现在STM32 的FLASH 寿命是10000 次,
如果你保存255 个数据那么每次修改1 个数据FLASH 就要擦写1 次,如果你保存1 个数据,那么你修改255
次该页才擦1 次,继续用另外1 页,建议保存数据个数不要超过50%,当然如果你的数据基本都不修改你保存
255 个也是没有任何问题(你的数据都不修改根本不用关心寿命问题了:)。
STM32 FLASH 模拟EEPROM 优化
官方例程中读写数据每次要查询读写位置,写数据是从页首地址开始查询,读地址是从页末地址查询。
假如只有1 个数据,读数据时效率是很低的,要查到最后才能找到有效数据,
如果页快满了写数据效率也很低,读效率反而好一点了。
实际程序中记录下一个可以写数据的位置将提高数据的读写效率,这样的话:写数据就是立即写不用查询,读
数据不从页末地址查询,而是从最后1 个写入数据处查询,这样特别在页数据少时效率提高不少。优化过的例
子代码只需要增加很少部分就能实现。

使用特权

评论回复
8
v26g7l|  楼主 | 2023-5-29 16:13 | 只看该作者
个人觉得,模拟固然是一根不错的方法,但风险也很大。如果模拟程序出错的话,数据很难恢

复,并且问题定位也不是很容易。用来保存关键数据的存储区的读写应该尽量使用简单的方案,

以确保数据的可靠性。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

25

主题

117

帖子

0

粉丝