微控制器EEPROM耗损均衡是否有通用算法?

Nab*_*bil 10 avr arduino eeprom

我正在开发一个Arduino库,它可以最大限度地延长AVR EEPROM的使用寿命.它需要您想要存储的变量数量,其余部分.这是我的尝试,在所有情况下都不起作用.

背景资料

Atmel表示,每个存储器单元的额定写入/擦除周期为100,000次.他们还提供了一份应用说明,介绍了如何进行磨损均衡.以下是应用笔记的摘要.

通过在两个存储器地址之间交替写入,我们可以将擦除/写入增加到200,000个周期.三个存储器地址为您提供300,000个擦除/写入周期,依此类推.要自动执行此过程,将使用状态缓冲区来跟踪下一次写入的位置.状态缓冲区的长度也必须与参数缓冲区的长度相同,因为也必须对其进行磨损均衡.由于我们无法存储下一个写入的索引,因此我们在状态缓冲区中增加相应的索引.

这是一个例子.

   <------------------- EEPROM -------------------->  
   0                                               N
   -------------------------------------------------
       Parameter Buffer    |     Status Buffer     |
   -------------------------------------------------

   Initial state.
   [ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 ]

   First write is a 7. The corresponding position
   in the status buffer is changed to previous value + 1.
   Both buffers are circular.
   [ 7 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 ]

   A second value, 4, is written to the parameter buffer 
   and the second element in the status buffer becomes
   the previous element, 1 in this case, plus 1.
   [ 7 | 4 | 0 | 0 | 0 | 0 | 1 | 2 | 0 | 0 | 0 | 0 ]

   And so on
   [ 7 | 4 | 9 | 0 | 0 | 0 | 1 | 2 | 3 | 0 | 0 | 0 ]
Run Code Online (Sandbox Code Playgroud)

为了确定下一次写入应该发生的位置,我们来看一下元素之间的区别.如果前一个元素+ 1不等于下一个元素,那么下一个写入应该发生.例如:

   Compute the differences by starting at the first
   element in the status buffer and wrapping around. 
   General algo: previous element + 1 = current element
   1st element:  0 + 1 = 1 = 1st element (move on)
   2nd element:  1 + 1 = 2 = 2nd element (move on)
   3rd element:  2 + 1 = 3 = 3rd element (move on)
   4th element:  3 + 1 = 4 != 4th element (next write occurs here)

   [ 7 | 4 | 9 | 0 | 0 | 0 | 1 | 2 | 3 | 0 | 0 | 0 ]
                 ^                       ^
                 |                       |

   Another edge case to consider is when the incrementing values
   wrap around at 256 because we are writing bytes. With the
   following buffer we know the next write is at the 3rd element 
   because 255 + 1 = 0 != 250 assuming we are using byte arithmetic.

   [ x | x | x | x | x | x | 254 | 255 | 250 | 251 | 252 | 253 ]
                                          ^
                                          | 

   After we write at element 3 the status buffer is incremented 
   to the next value using byte arithmetic and looks like this.
   255 + 1 = 0 (wrap around)
   0 + 1 != 251 (next write is here)

   [ x | x | x | x | x | x | 254 | 255 |  0  | 251 | 252 | 253 ]
                                                ^
                                                | 
Run Code Online (Sandbox Code Playgroud)

上面的这些示例显示了如何为一个变量延长EEPROM的寿命.对于多个变量,想象一下将EEPROM分成具有相同数据结构的多个段,但是更小的缓冲区.

问题

我有上面描述的工作代码.我的问题是当缓冲区长度> = 256时算法不起作用.这是发生的事情

   Buffer length of 256. The last zero is from
   the initial state of the buffer. At every index
   previous element + 1 == next element. No way to
   know where the next write should be.

   <-------------- Status Buffer ------------>
   [  1  |  2  | ... | 253 | 254 | 255 |  0  ]


   A similar problem occurs when the buffer length
   is greater than 256. A lookup for a read will think
   the next element is at the first 0 when it should be at 
   255.
   <-------------------- Status Buffer ------------------>
   [  1  |  2  | ... | 253 | 254 | 255 |  0  |  0  |  0  ]
Run Code Online (Sandbox Code Playgroud)

我该如何解决上述问题?有没有更好的方法来跟踪下一个元素的写入位置?

Jim*_*myB 15

关于一般EEPROM寿命延长的一些想法:

  1. EEPROM单元通常由(通过硬件)以两步操作写入:首先,擦除单元,即设置为全1(0b11111111 = 0xff),然后写入要写入的位(实际上只有0)实际上是写的.只能通过实际写操作将位设置为0.将位从0更改为1需要擦除整个单元格,然后重新写入新值.

  2. 如果EEPROM单元已经包含要写入的相同值 - 这可能是或多或少要写入(重新)写入的数据的情况 - 根本不需要写入单元格,从而减少该写入操作的磨损为0.可能需要检查单元格内容以确定是否需要写入它而不是始终向其写入新值.

  3. 如果在新值中存在任何1位,其中存储值具有0位(即,如果StoredValue & NewValue != NewValue),则上述的组合导致仅在写入之前擦除单元的方法.如果新值(StoredValue & NewValue == NewValue)不需要0 - > 1位转换,则无需擦除单元.

  4. AVR分别提供用于擦除和写入EEPROM单元的单独指令,以支持上述机制.

  5. 当执行读取 - 比较 - 擦除 - 写入而不仅仅是擦除 - 写入操作时,数据传输到EEPROM的最坏情况速度当然会下降.然而,这有可能完全跳过某些/大多数单元的擦除 - 写入操作,这可能会降低相对速度损失.

针对您目前的问题,请考虑以上几点:为什么不使用单个位来存储下一个写入位置?

例:

状态缓冲区初始化为所有1:

Bit number: 0123 4567 89AB CDEF
Value:      1111 1111 1111 1111
Run Code Online (Sandbox Code Playgroud)

在访问EEPROM中的值之前,找到状态缓冲区中的前1位.该位的数量表示(循环)参数缓冲区的当前"头"的地址.

每次推进参数缓冲区时,将状态缓冲区中的下一位设置为0:

Bit number: 0123 4567 89AB CDEF
Value:      0111 1111 1111 1111
Run Code Online (Sandbox Code Playgroud)

然后

Value:      0011 1111 1111 1111
Run Code Online (Sandbox Code Playgroud)

然后

Value:      0001 1111 1111 1111
Run Code Online (Sandbox Code Playgroud)

等等.

这可以在擦除整个单元的情况下完成,因此每次更新时只会"磨损"一位状态缓冲区 - 如果写入的数据也只有一个0位.
例如,要将存储的值0111转换为新值0011,要写入的数据应为1011(data = ( newValue XOR oldValue ) XOR 0xff),除了我们实际想要更改的单个位之外,保持所有位不变.

一旦状态缓冲区耗尽(全部为0),它将被完全擦除,并且全部重新开始.

这里明确的一点是,每个参数缓冲区只需要保持一位状态,与Atmel应用笔记相比,它只消耗1/8的内存.此外,找到下一个写入位置也会快得多,因为只需要状态缓冲区上的1/8读取操作.(编辑:不正确,因为EEPROM读取的性能几乎为零性能,而所需的位移可能需要几十个周期.)

但另一个注意事项是:您认为使用256+参数缓冲单元实际上是否有用?例如,当处理设备上1024字节的总可用EEPROM时,单元将变得非常小. - 并且100000个周期乘以256是相当大量的写入操作,如果需要大量数字,则算法中可能存在错误,或者根本不应将EEPROM用于此目的.作为替代方案,在某些情况下,外部NVRAM将是一个不错的选择.

访问时间也可能是这里的一个方面:当尝试在具有256字节状态缓冲区的参数缓冲区中查找并读取3字节大小的元素时,在最坏的情况下将需要256(+3)个读取操作案例 - 巨大的开销!

有一个关于EEPROM单元工作原理的非常具有说明性的文件,包括恶化的原因和原因:

意法半导体:"设计人员如何充分利用意法半导体的串行EEPROM",应用笔记AN2014

  • 是和否:当它改变它的状态时,每个位都会被磨损.因此,将0写入已经存在0的位置或将1写入1的位置不会产生明显的磨损.当将0011写入先前包含0111的单元时,只有一个位必须从1变为0.通过"写入"执行从1到0的更改,从0到1的更改只能通过"擦除"执行; 擦除总是影响整个字节.如果不需要擦除_并且实际上没有执行任何操作,则单独保存 - 最佳情况 - 擦除 - 写入周期的总磨损的50%+. (3认同)
  • @endolith这取决于... AVR的内部EEPROM和(一些?)STM32的闪存提供单独的擦除和写入操作,其中写入只能将1更改为0,必然使写入1的位保持不变。其他设备可能支持也可能不支持这些“不擦除写入”命令。 (2认同)