1. 考虑均衡磨损的必要性
对于数据保存而言,常用的非易失性存储器有ROM、FLASH、SD卡、U盘、硬盘等,而对于MCU来说,保存的数据量一般不会太大,常用的也就只剩EEPROM和FLASH了。虽然而考虑到硬件成本、PCB布局等原因,再加上现在的MCU芯片内部FLASH存取速度越来越快,FLASH尺寸越来越大的特点,我们一般会将内部FLASH作为主要存储介质,但FLASH编程只能将bit由1位置0,不能将0位置1,将0置1只能擦除扇区,而扇区往往比编程单位要大很多,哪怕我们只对一个地址写两个字节的数据,也需要擦除整个扇区来完成数据更新,频繁擦写导致flash坏块。
以常用的GD芯片为例,其内部Flash官方寿命是擦写10万次。但是考虑到一些常用的保存场景,比如有将故障诊断的DTC码保存进非易失性存储器中的需求,若按照一天需要保存100次数据到存储器中的频率来算,10万次官方寿命也只够保存2.7年,这是远远不够的。所以我们需要考虑如何提高FLASH的寿命。
2. 均衡磨损原理
磨损均衡算法支持监控以及对不同页面间的擦写操作进行均衡。当不使用损耗均衡算法时,flash页面的磨损速度不均衡。例如,不经常擦写的的页面和经常用来保存数据需要经过擦写的页面相比,经常擦写的页面可能很快达到寿命而损坏,而不经常擦写的页面还能正常使用。均衡磨损算法确保每个参与均衡的页面可以被平等的使用。通过这种均衡磨损操作,把原来只对一个扇区进行磨损的操作分摊到多个扇区,增加了对flash的可擦写次数。
均衡磨损的原理是将一个大数据扇区差分成多个数据帧,轮流写操作数据帧,当所有的数据帧都写过了再擦除重新来过。这样本来我们每次修改都需擦除扇区,优化为现在写满一个扇区再擦除,寿命得到了成倍的提升。这里是对flash单一扇区里的数据帧磨损均衡,同理可以对多个扇区进行扇区磨损均衡。
ST官方已经实现了使用EEPROM 模拟FLASH的均衡
磨损方案
, AN4894应用手册中有详细的使用介绍。这里在ST的基础上进行开发可以节省开发成本并有一定的可靠性。其特性如下:
- Lightweight implementation and reduced footprint
-
Simple API that consists of a few functions to format, initialize, read and write data, and clean up flash memory pages
- User-configured EEPROM size
- Supports 8-bit, 16-bit, and 32-bit variables; supports 96-bit variables for the STM32U5 Series
- Clean-up simplified for the user (background page erase)
- Interrupt servicing is possible during program and erase operations
- At least two flash memory pages to be used for internal data management
- Wear leveling algorithm to increase emulated EEPROM cycling capability
- Increased EEPROM memory endurance versus flash memory endurance
- Robust against asynchronous resets and power failures
- Optional protection for flash memory sharing between the two cores of the STM32WB Series microcontrollers
- Maintenance of cache coherency
3. ST均衡磨损方案分析
下载ST官网固件包之后,可以找到以下核心文件:
eeprom_emul.c
eeprom_emul.h
eeprom_emul_conf_template.h
eeprom_emul_types.h
flash_interface.c
flash_interface.h
移植过程中主要修改interface相关,并配置eeprom_emul_conf_template.h后另存为eeprom_emul_conf.h。具体配置参数可以查看AN4894手册。
我们可以对比手册和代码,来查看ST的均衡磨损方案是如何实现的:
3.1 原则
Flash模拟EEPROM可以有很多种方式来实现,至少需要两组FLASH页面来处理这些非易失性数据,其中第一组页面最初被擦除用来存储新数据和falsh编程,一旦第一组页面中充满了数据,则需要对其进行垃圾处理。第二组页面只收集来自于第一组页面的有效数据,剩下的区域可以用来存储新的数据,一旦完成将有效数据从第一组页面搬运保存到第二组页面的操作,第一组页面就可以被擦除。
每一组页面可以由一个或多个Flash页面组成。每个flash页面需要保存一个32字节的头部信息,每个界面有以下5种可能的状态:
- ERASED: 页面为空(初始状态)
- RECEIVE: 用于在数据传输过程中从其他满页接收数据的页。
- ACTIVE: 该页面用于存储新数据
- VALID: 页面已满。此状态在所有有效数据完全传输到接受页前不会改变
- ERASING: 本页的有效数据已全部转移,本页已准备被擦除
状态间转换图如下:
3.2 空间占用情况
闪存中的最小写宽度是64位(stm32u5系列为128位),因为它的ecc(纠错码)不能关闭;只有0(0x0000 0000 0000 0000)可以写入已编程的非空闪存线。由于Header使用前四个字,当页面大小为2kb时,flash页面最多可以存储252个可变元素,当页面大小为4kb时,最多可以存储508个可变元素。
一个flash页的可能状态是被写入0xAAAA AAAA AAAA AAAA到Header中,可以使用以下过程来确定页面的状态:
• The page is in ERASING state if its fourth line is not erased
• The page is in VALID state if the third line is not erased and the fourth line is erased
•The page is in ACTIVE state if the second line is not erased and the third and fourth
lines are erased
• The page is in RECEIVE state if the first line is not erased and the second, third and
fourth lines are erased
• The page is in ERASED state if the first four lines are erased.
每个变量元素都由一个虚拟地址和一个数据值定义,并将存储在闪存中,用于后续的检索或更新。在所实现的软件中,虚拟地址为16位长,数据值为8位、16位或32位长。驱动程序要求虚拟地址值在0x0001之间(0x0000对应于由驱动程序无效的eeprom元素),以及所需的最大eeprom变量数。此外,由于虚拟地址是16位宽的,eeprom变量的最大数量不能超过0xfffe(0x ffff对应于一个被擦除的闪存线)。此外,变量的数量还受到产品闪存大小的限制。
每个元素还包含一个16位的crc,用于检查元素的完整性。当数据被修改时,与相同虚拟地址关联的修改数据存储在新的闪存位置。数据检索将返回最新的数据值。
读取命令执行活动或有效页面中从最高到最低地址的闪存读取,并且只返回有效数据。如果数据是在给定虚拟地址写入的数据,并且使用crc进行完整性检查,则被认为是有效的。还要注意,在数据传输机制期间,只复制有效的数据。
以stm32l4为例,对1000个eeprom变量的仿真可以存储在两组4个flash页面中(每个页面能够存储252个元素)。当所有元素都被写入一次(或在页面传输之后)时,在触发新的页面传输之前,只能再写入8个元素。在这种情况下,建议添加2个保护页(每组页面一个),以便在触发新的页面传输之前可以执行260次写操作。
3.3 计算eeprom模拟所需的闪存大小:
作为第一个近似值,所需的闪存大小与模拟的eeprom大小和循环能力成正比。
为了存储4000个单独的字节,并且知道每个页面最多可以存储252个字节元素,一组页面必须包含16个Flash页面。第二组是相同的大小,需要在第一个已满时传输数据。如果我们假设用2个保护页,需要34个Flash页。
PAGES_NUMBER = (((((NB_OF_VARIABLES + 252) / 252) * 2U) * 1) + 2)
如果只有16K的FLASH用作均衡磨损,那么能存504个元素
FLASH存储结构:
存储计算公式:
3.5 操作耗时
4. 用户配置
4.1 用户定义宏
EEPROM仿真算法参数应根据应用需要进行配置;它们位于eeprom_emul_conf.h文件中:
- NB_OF_VARIABLES (default 1000, 100 for STM32C0 Series(a)): 非易失性元素,每个元素值为8位、16位、32位或96位。
- START_PAGE_ADDRESS:用于EEPROM仿真的第一个Flash页的地址。
-
CYCLES_NUMBER (default 1): The number of kcycles, X, for the equivalent EEPROM
endurance. If CYCLES_NUMBER equals 10, the emulated EEPROM has an
equivalent endurance of 10 x X kcycles. X is the Flash endurance for the product used
and listed in Table 6: Flash memory endurance. - GUARD_PAGES_NUMBER (default 2):用于减少Flash内存压力的保护页面数。这个数必须是偶数。
- CRC_POLYNOMIAL_LENGTH (default 16): 大多数情况下不需要修改; 一个16位CRC在计算速度和Flash大小方面进行了优化,并提供了非常好的检测率
- CRC_POLYNOMIAL_VALUE (default 0x8005):大多数情况下不需要修改; 这个ANSI CRC在计算速度和闪存大小方面进行了优化,并提供了非常好的检测率。
4.2 用户接口函数
- EE_Format:擦除用于EEPROM模拟的所有flash界面并且写入ACTIVE状态的头部信息到第一个flash页
- EE_Init: 将EEPROM仿真变量配置为初始状态,并在Flash写或擦除操作期间异步复位或断电的情况下将Flash页面恢复到已知的良好状态。擦除需要擦除的Flash页面(例如在ERASE状态下未完全擦除的页面,在erasasing状态下的页面);默认情况下必须使用EE_FORCE_ERASE参数。如果应用程序保证在Flash写或擦除操作期间不会发生异步复位或断电,则可以使用EE_CONDITIONAL_ERASE参数;复位后,应该在访问模拟EEPROM之前系统地调用该函数。
- EE_ReadVariableXXbits: 该函数更新作为参数传递的虚拟地址对应的数据值。只读取最后存储的元素。它返回一个等于EE_OK的Status,除非它在给定的位置找不到任何数据。实现了3个函数用于不同的数据大小(8,16,32 or 64)
- EE_WriteVariableXXbits: 该函数用于更新EEPROM虚拟地址上用作参数传递的参数值。如果用于EEPROM仿真的页面集已满,则触发Flash页面传输并返回等于EE_CLEANUP_REQUIRED的状态。实现了三个函数来处理所有支持的数据大小(XX = 8,16,32或64).
- EE_CleanUp: 擦除处于ERASING状态的页面集。当实时约束较低时,应用程序可以调用该函数。Flash清理是在轮询模式下完成的,所以这个函数在一组用于EEPROM模拟的Flash页面被擦除之前不会返回。
- EE_CleanUp_IT: 擦除处于ERASING状态的页面集。Flash清理在中断模式下启动,因此该函数立即返回,随后的页面擦除由后续中断服务例程完成。
-
FI_DeleteCorruptedFlashAddress: 这个函数可以在NMI下调用来删除损坏的Flash地址来清除Flash接口故障