我正在复制here
的摘录闪存擦除周期很长 - 非常长 - 擦除闪存扇区可能需要几秒钟。此外,由于保证擦除/重写周期的数量通常是有限的(通常约10,000或高达100,000),我们不能仅因为一个变量发生变化而擦除整个扇区。 方法是“牺牲”整个扇区以进行可变存储。在该扇区中,变量存储在表中。如果变量发生变化,则不会被覆盖,而是丢弃旧值并生成表中的新条目。
我不明白上述说法。
为什么在添加要修改为新数据项的数据而不是“就地”修改现有数据时,我们不必擦除扇区?是因为我们要存储新数据的扇区中的空闲区域是用0x00还是0xFF预先擦除的?
如果上述问题的答案为“是”,是否可以避免在下述情况下的擦除周期?
我正在将系统日志写入闪存。一旦闪存区域完全填满系统日志,我需要删除最早的日志条目并将其替换为最新的日志条目。在这种情况下,我想不出一个可以避免擦除的情况。我之前从未使用过闪存驱动程序。任何帮助将不胜感激。我不是母语为英语的人,希望这个问题不含糊。谢谢。
答案 0 :(得分:3)
当您在MCU上没有任何正确的数据闪存(或eeprom)时,会弹出这类问题,因此您需要使用程序闪存来存储数据。程序闪存的工作方式与数据闪存的工作方式相同,但由于它不应经常被擦除,因此可以使用物理上较小的电路。因为,通常:物理电路越小,擦除扇区越大。
问题在于擦除大型闪存扇区需要花费大量时间。但是一旦整个扇区被擦除(通常所有单元都设置为1),您就可以写入任何擦除的存储器位置一次。基本上你总是可以将1变为0,而不是在没有擦除的情况下将0变为1。所以你确实可以写,因为该区域是预先擦除的。这样的写入几乎不需要擦除。
因此,存在各种或多或少混淆的算法来利用这一点。这不是我推荐的解决方案,但我可以解释一下,因为不幸的是它很常见:
假设您在闪存扇区内有两个变量,您需要立即更新。每个都有1个字节的数据。然后,您还将为每个变量提供一个唯一的搜索键(不能是已删除的闪存单元的值)并将其存储为:
Address Key Value
0x0000 0x01 0xAA
0x0002 0x02 0xBB
您将拥有一些类似
的程序结构typedef struct
{
uint8_t key;
uint8_t val;
} flash_var;
const flash_var* x = (flash_var*)0x0001;
const flash_var* y = (flash_var*)0x0002;
接下来,您要将x
的值更改为0xCC。您可以调用闪存编程驱动程序,它会在下一个可用的闪存位置写入新变量的副本。你的闪光灯现在看起来像这样:
Address Key Value
0x0000 0x01 0xAA
0x0002 0x02 0xBB
0x0004 0x01 0xCC
因此,您有两个变量x
的副本,但程序会将指针更新为仅指向最新出现的变量。前一个只是坐在闪光灯中,作为死亡空间"。通过从闪存块的末尾搜索,向后朝向开头查找搜索键0x01的第一个匹配项,您始终可以找出哪个是最新的。
这意味着在开机时,找到变量不是随机访问,而是一个非常慢的线性搜索。
此算法存在以下几个问题:
但最重要的是,该行业可能在任何特定时间都被填补,而且可能很难预测何时。您的程序必须能够处理这种特殊情况,并在发生时应对长擦除时间。这是最糟糕的情况,实时嵌入式系统必须始终在最坏情况之后进行设计。
这是主要的逻辑缺陷:如果你的程序可以处理特殊情况,当扇区被填满并且你必须擦除时,为什么它不能每次都处理相同的情况呢?也就是说,既然你的程序必须能够处理这个问题,你也可以每次擦除整个扇区。
事实证明,该算法只能在最佳情况下节省时间,这是无用的,因为无论如何必须在最坏情况下编写函数。在最糟糕的情况下,这是你必须设计的,它根本不会节省任何时间。实际上,算法引入的额外复杂性使得最坏情况需要花费更多时间。
这就是为什么这些算法在每个设计上有些混淆。在设计合理的实时系统中,它们只保存闪存写入周期,而不是其他任何东西。
总而言之,我建议不要使用这些算法。相反,选择具有适当数据闪存的MCU。它将具有更小的扇区,更快的擦除时间和更多的写周期。