当您在 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。您将调用您的闪存编程驱动程序,它将在下一个可用闪存位置写入新变量的副本。您的 Flash 现在看起来像这样:
Address Key Value
0x0000 0x01 0xAA
0x0002 0x02 0xBB
0x0004 0x01 0xCC
所以你有变量x 的两个副本,但程序将更新指针,使其仅指向它最近出现的位置。前一个只是作为“死区”存在于闪存中。通过从 flash 块的末尾向后搜索开头,查找第一次出现的搜索键 0x01,您始终可以找出哪个是最新的。
这意味着在开机时,查找变量将不是随机访问,而是相当缓慢的线性搜索。
这个算法有几个问题:
- 不是随机访问,而是很慢的搜索。
- 要实现很多额外的复杂性,这会增加出现错误的机会并占用资源。
- 存在相同数据的重复项。这对于任务关键型系统是不可接受的。假设搜索键损坏 - 程序不会一无所获并报告错误,而是会抓取旧数据。
- 根据闪存的性质,您可能希望在闪存中包含校验和。使用上述算法,每个变量都必须有一个单独的校验和,这是对空间的巨大浪费。
- 当闪存扇区已满时,无论如何您都必须擦除它。然后,您必须想出一种将变量临时存储在 RAM 中的方法。它变得复杂。
-
但最重要的是,该行业可能会在任何给定时间被填满,而且很难预测何时。您的程序必须能够处理这种特殊情况,并在它发生时应对较长的擦除时间。这是最坏的情况,实时嵌入式系统必须始终在最坏的情况之后进行设计。
这是一个主要的逻辑缺陷:如果您的程序可以在扇区被填充并且您必须擦除时处理特殊情况,那么为什么它不能每次都处理相同的情况呢?也就是说,由于您的程序无论如何都必须能够处理这个问题,因此您也可以每次都擦除整个扇区。
因此结果表明,该算法只在最好的情况下节省时间,这是毫无用处的,因为无论如何都必须编写它才能在最坏的情况下运行。在最坏的情况下,这是您必须设计的,它根本不会节省任何时间。事实上,算法引入的额外复杂性使得最坏情况需要更多时间。
这就是为什么这些算法在设计上有些混乱。在设计合理的实时系统中,它们只节省闪存写入周期,没有别的。
所以总结一下,我建议不要使用这些算法。相反,选择具有适当数据闪存的 MCU。它将具有更小的扇区、更快的擦除时间和更多的写入周期。