一种使用Flash模拟EEPROM的策略

背景

前些年在工作的时候,需要给一款车辆仪表的产品,从AVR平台升级到ST平台。当时遇到的问题是原先AVR芯片内部有EEPROM,但是ST芯片内部没有。产品需要动态保存数据,IO已经全部用完,没有多余的IO来外挂EEPROM芯片。后来发现ST官方提供了一种使用芯片内部Flash来模拟EEPROM的方案,尝试用在了产品上,表现挺稳定的。这种方案在汽车电子上有比较广泛的应用,在这里分享给大家。

为什么要用Flash模拟EEPROM?

首要原因,那肯定是因为穷。

  • 因为Flash的空间可以做的比较大,一般我们的产品,不管是片内Flash或者外部的Flash,都不会把所有扇区全部用完,都有剩余的空间,如果能利用这部分空间就可以节省外部E2的成本。
  • 有些产品对PCB空间要求非常高,没有多余的空间来外挂存储器,也可以省下PCB的空间。

例如:

  • 报警器:就是设计成没有外部EEPROM,需要把内部Flash当做EEPROM使用,因为保存的都是配置参数,内部Flash的擦写寿命也能满足需求。
  • 海外项目:因为要支持远程升级,所以设计成外挂一片EEPROM和一片Flash。如果能用Flash模拟EEPROM,就可以只要一片Flash就可以了。

Flash和EEPROM的差异

Flash(NOR FLASH)和EEPROM的特性有较大的差异,一般情况下不能直接等同使用,需要通过软件模拟来达到相同的效果。我们先来看一下他们的差异:

物理结构的不同

  • EEPROM可以随机访问和修改任何一个字节,可以往每个bit写入0或者1。具有较高的可靠性,但是电路较复杂、成本较高。
  • Flash不再以字节为最小单位,而是以块为最小单位,内部电路被设计成编程时只能将1写成0,而不能将0写成1。所以内部电路就简化了很多,数据密度可以做的更高,容量可以做的更大,成本也降低了。

擦除/编程时间的不同

  • EEPROM随机寻址编程周期5ms,无需擦除时间;
    在这里插入图片描述

  • 片内Flash双字(64bit)编程时间81us,页Page(2K)擦除时间22ms。(以STM32L476为例)
    在这里插入图片描述

  • 外部Flash单字节编程时间为8us,但是因为扇区较大(4KB),导致擦除时间达到100ms。

在这里插入图片描述

可以看到就编程速度而言,Flash的速度要远远高于EEPROM,很适用于在意外情况快速保存关键数据。但是Flash擦除需要较长时间,而EEPROM则不需要擦除。Flash的编程速度快,是建立在扇区已擦除的基础上。所以通过软件算法,尽可能减少擦除次数,就可以让Flash达到EEPROM一样的效率。

编程/擦除条件不同

  • EEPROM和外部Flash,他们片内都有独立的CPU,一旦启动编程就无需MCU的CPU参与,只有电源掉电才会中断写操作,所以适当调整解耦电容的大小,就可以保证外部芯片的完整编程操作。
  • 片内Flash的编程/擦除操作,需要CPU直接参与,受电源和CPU的影响。如果在编程/擦除期间CPU因为异常导致中断或者停止,会影响编程/擦除的正确性。

储存容量的不同

  • 目前的EEPROM只能做到几十K或者几百K,很少有超过512K的。
  • Flash一般都能做轻松到M级别的容量。

编程寿命的不同

  • EEPROM的编程寿命最长,可达100w次,储存时间可达100年。
    在这里插入图片描述
  • 片内Flash一般寿命只有1w次。储存时间根据储存环境在10-30年之间
    在这里插入图片描述
  • 外部Flash编程寿命可以做到10w次,储存时间20年。
    在这里插入图片描述

数据传输速度的不同

  • EEPROM采用I2C通讯,标准速度为100K,高速为400K,如果是模拟可能速度更低。
  • 外部Flash采用SPI,可以轻松达到M级别的传输速度,芯片最高支持104M的数据传输速度。
  • 片内Flash的数据传输速度是CPU指令级别的。

使用Flash模拟EEPROM的方案

  • 使用Flash模拟EEPROM的方式有很多种,这里只对比了其中的2种。

  • 使用外部Flash和内部Flash只是介质上和一些性能上的差异,原理可以通用。

一般的Flash当做EEPROM的方案

把需要储存在EEPROM中的数据,直接储存在Flash扇区中。
为了避免在擦除/编程过程的异常导致写入数据失败,需要使用备份区域。这个方式和我们目前直接使用EEPROM一样。

优点

  • 无需外挂EEPROM就可以实现掉电保存效果,充分利用了内部Flash空间。
  • 如果使用的是内部Flash,因为内部Flash和RAM是统一编址,CPU可以直接访问Flash上的数据。因为参数储存的地址是确定的,可以通过取地址的方式读取参数。上电可以不用把参数从Flash中读取放到RAM中,在需要使用的时候,直接通过地址寻址的方式获取参数。Flash中的数据有编程保护,避免被意外修改。不会出像现在RAM中意外被破坏的问题。当需要修改变量时,从Flash中读取到RAM中,修改完成再写入Flash。这种方式很适合保存校准参数等一些不容易变动的厂内设置的参数。

缺点

  1. 寿命问题,Flash的擦写寿命远远不如EEPROM,直接这样存储必须考虑寿命问题。
  2. 因为Flash芯片的特性,任何一个变量的修改,都必须全部擦除,重新写一遍,会进一步降低Flash的寿命。
  3. 效率问题,每次的编程都需要先擦除,再写入,整个过程耗费较长时间,效率不如外部EEPROM,耗费较长时间会增加数据丢失的风险。

ST官方提供的实现Flash损耗均衡方案

ST官方提供了一种至少使用2个扇区来模拟EEPROM的方案。后来NXP也提供了模拟EEPROM的方案,实现方案有差异,但是原理是类似的。

实现原理

  1. 假设我们现在有4个变量需要动态储存,每个变量的类型都是uint16_t类型。
    在这里插入图片描述

  2. 现在我们给每个变量,分配一个虚拟地址,虚拟地址要求每个变量对应的虚拟地址必须唯一,且不能为全FF(因为全FF是Flash擦除后的值)。假设约定虚拟地址是2字节组成,所以范围是0x0000~0xFFFE。
    在这里插入图片描述

  3. 假设业务需求需要保存的参数顺序为:参数2→参数3→参数4→参数1→参数3→参数4→参数4→参数3
    保存后数据在Flash的地址空间内的情况如图
    在这里插入图片描述
    在物理地址上,分别写入虚拟地址和参数值。和EEPROM中保存数据在固定的物理地址不一样,在Flash中保存数据的物理地址,每次都不一样,但是同一变量对应的虚拟地址是一样的,我们通过在物理地址中查询虚拟地址,来找到对应变量的值。

  4. 按照上面的思路,在Flash中储存数据,总会遇到Flash扇区存满的情况。存满后需要将Flash扇区擦除,才能重新写入数据。如果在擦除期间或者写入期间发生异常,将导致参数丢失。所以需要使用2个Flash扇区来保存数据。当发现当前扇区的剩余空间不足以写入下一次数据,就需要将数据迁移到另一个扇区。
    在这里插入图片描述
    在数据迁移的过程,需要根据参数虚拟地址列表,在原扇区中,从后往前查找最新的储存值,将其迁移到新的扇区,只有确保数据全部迁移到新扇区,才能擦除原扇区。2个扇区如此往复就完成了模拟EEPROM的效果。

  5. 上面描述的是写过程,在上电后需要读取Flash中的数据,必须从起始地址开始读,读到虚拟地址和参数值都是FF的时候,说明该地址是当前储存的最新地址,从该地址开始往前读取各个参数的最新值。

扇区的状态

前面提到了上电需要从扇区中读取最新的参数值,但是此时有2个扇区,该从哪个扇区读取数据呢?
ST提供的方案将每个扇区的前2个字节作为扇区的状态字节,将扇区分为3种状态:已擦除状态(状态值0xFFFF)、正在接收状态(状态值0xEEEE)、可用状态(状态值0x0000)

  • 将当前正在写入数据的扇区的状态标记为可用状态(0x0000)
  • 如果发生数据迁移,则先将新扇区状态标记为正在接收状态(0xEEEE),等待数据迁移完成后,将状态标记为可用状态(0x0000),然后将原扇区擦除,擦除后的状态自动变为已擦除状态(0xFFFF)

这里的状态切换巧妙地利用了Flash在不擦除的情况下,只能将数据位从1改为0的特点,来保存扇区在数据启动迁移的状态和迁移完成的状态。实现了Flash在不擦除情况下写2次数据,不产生错误的效果。

按照上面的每个扇区可能出现的3种状态,在2个扇区的情况下,上电后可能出现排列组合总共有9种情况
在这里插入图片描述
虽然有9种情况,但是可以归类一下:

正常情况下,不会出现2个扇区状态一样的情况。如果出现说明扇区异常,需要初始化扇区。
在所有的扇区中,有且只有1个扇区的状态是可用状态,才是正常状态。
当上电后发现一个扇区处于接收状态,且前一个扇区是可用状态,说明数据迁移过程中遇到异常,还未迁移完成,需要重新执行迁移操作。

总结

使用Flash模拟EEPROM的优缺点都很明显。

优点
  1. 使用这种方式最大的好处就是通过软件的方式延长了Flash的寿命,达到和E2一样的效果
    在这里插入图片描述

    以STM32L476为例,单个扇区大小2KB,储存字节数4字节(虚拟地址2字节+参数值2字节),扇区最大擦写次数1w,扇区数2个,可以实现1000w+次的使用寿命,比EEPROM正常的100w次要高出不少。

  2. 编程速度非常快,减少意外数据丢失发生的概率。扇区擦除的频次大大降低,而且不发生在写数据前,数据保存的效率大大提供。

  3. 不需要额外的器件成本,不增加PCB面积。

局限性
  1. 每次擦除需要耗费较长的时间(20ms),而且有一个点要注意:如果代码和数据在同一Bank中,内部Flash在擦除/编程期间,是不可读的,CPU无法取指令,导致CPU暂停,需要强制等待擦除/编程结束才能执行其他指令。
    在这里插入图片描述

    如果这期间发生的中断/异常事件没有得到执行,可能导致其他的问题。针对这个问题,STM32L476的设计成双Bank的方式,只要将代码和数据储存在不同的Bank,就可实现同步读写,但是低端的MCU或者国产的MCU,普遍都会有这个问题。
    在这里插入图片描述

  2. 在iap升级的时候,如果代码和模拟EEPROM之间预留的扇区不足,新的固件可能覆盖到储存的扇区,导致出现数据丢失的问题。
    按照这样的方式,编译器并不知道哪些地址用来模拟EEPROM,对于一些代码和数据界限把握不好的项目,可以告诉编译器模拟EEPROM的区域,编译器会帮我们管理这块区域。但是这样带来的代价就是,每次更新程序都会把模拟EEPROM区域的值初始化。
    MDK的方式:

    const uint8_t para_flash_area[128*1024] __attribute__((at(0x08100000)));
    

    IAR的方式:

    #pragma location=0x08100000
    
    const uint8_t para_flash_area[128*1024];
    
  3. 编程限制,比如STM32F1系列最小编程单位为字(2字节),且地址必须为偶数(2字节对齐),STM32L476最小编程单位为双字(8字节)且必须双字(8字节)地址对齐,可能带来操作的不方便和导致部分空间浪费。
    在这里插入图片描述

  4. 官方的方案只针对uint16_t型的变量,储存其他类型需要自己拓展,而且没有校验的算法。

  5. 官方对上电后2个扇区如果状态一样,直接全部格式化。因为格式化后全部数据都清零了,其实数据中可能存在准确的数据,但是因为状态问题导致异常。可以尝试从数据区读取数据,做一定的数据还原规则。

  6. 因为数据储存的物理地址是不确定的,每次读取数据,都要从后往前查询,读取效率不如EEPROM,但是考虑到数据读取只在上电时执行,一般情况可以接受。

注意事项
  1. 模拟EEPROM的扇区可以定义从第2个扇区开始的任何一个扇区,但是不能定义在首扇区,因为这个扇区默认是Boot的启动地址,存放着系统的中断向量表。每次中断发生时都需要跳转到首地址查询中断向量,不可用于其他功能。
  2. 因为Flash擦除需要较长的时间,为了避免在保存数据时遇到存储区满,需要先擦除再存储的问题,要始终保证当前正在写入的扇区的下一扇区处于已擦除状态。

优化

  1. 一般我们保存的对象,都不会是单一类型,并且加入校验码。我们可以扩展一下,储存联合体对象,这样的做法是为了提高我们数据读取和数据迁移的效率,但是浪费了一些空间。
    在这里插入图片描述

  2. 因为方案只提供了2个扇区来模拟EEPROM,为了损耗均衡,可以使用更多的扇区来模拟,模拟出来的扇区寿命可以成倍增加。
    在这里插入图片描述

  3. 其实我们的产品中,不仅仅局限于运行一套这样的机制,只要Flash空间充足,我们可以运行多套这样的机制。比如针对容易变化的累积量、脉冲状态、故障记录,我们可以单独拿出来做一套这样的机制。这种情况也适合于参数和运行数据分开存储,因为运行数据更新频繁,会导致配置参数读取效率低。

  4. 因为只用了2个扇区,所以正常情况下,只有一个扇区处于可用状态,另一个扇区处于已擦除状态。如果我们使用更多的扇区来模拟,只需保证下一个扇区处于已擦除状态,其他扇区可以一直保持数据。利用这些历史数据,我们可以做数据的可靠性分析:

    • 比如最新的数据出现异常,可以继续往前读取历史数据,保证数据不会出现较大的差异。
    • 可以作为数据安全的依据,比如汽车的里程只可能增加不可能减少,表的累积量也只可能增加,不可能减少。
    • 像之前其他项目组分享的,出现主区和备区的数据都异常,累积量会清零,后来改成主区、备区都异常直接卡死,等待人工上门处理。如果是模拟EEPROM的话,如果最后一次数据出现异常,可以继续往前读取,直到读取到正常的数据。数据值可能有偏差,但是不会出现直接清零的情况。可能有一定的改善效果。
  5. 因为有大量的历史数据,可以利用历史数据分析设备的一些行为,帮助我们定位bug。

总结

  1. 本文分享了一种利用Flash来模拟EEPROM的方法,利用Flash扇区空间大的特点,通过软件实现虚拟地址的方式,多次写入数据,来减少Flash擦除次数,规避了Flash擦写次数不足的问题。在扇区写满后做数据迁移操作,实现各个Flash扇区的损耗均衡。而且发挥编程速度快的特点,适用于在意外情况快速保存关键数据。达到和外部EEPROM一样的效果,节约了成本。
  2. 但是利用片内Flash一定要注意Flash擦除期间,CPU停止的问题。
  3. 每种方式都有各自的优缺点,没有最好的,只有合适的。可以将几种方式结合起来使用,让产品表现的更稳定。
  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flash模拟EEPROM策略一种Flash存储器模拟EEPROM存储器的方法。EEPROM(Electrically Erasable Programmable Read-Only Memory)是一种可编程的只读存储器,它可以被多次写入和擦除,因此非常适合存储需要经常更新的数据。 Flash存储器与EEPROM存储器的主要区别在于,Flash存储器的整个块必须被擦除才能写入新的数据,而EEPROM存储器可以直接写入单个字节的数据。因此,为了将Flash存储器模拟EEPROM存储器,需要采用一些特殊的策略一种常见的Flash模拟EEPROM策略是分配一个Flash块用于存储EEPROM数据,并使用一个指针来跟踪下一个可用的EEPROM地址。当需要写入新的EEPROM数据时,首先将整个EEPROM块的数据读入RAM中,然后在RAM中更新相应的数据,并将整个RAM数据块写回Flash存储器。由于Flash存储器必须按块擦除,因此每次写入EEPROM数据时,需要检查是否需要擦除整个Flash块。如果需要擦除,则需要将整个EEPROM块的数据复制到另一个Flash块中,并擦除原始Flash块,然后将数据写回新的Flash块。 另一种常见的Flash模拟EEPROM策略使用一组循环Flash块,每个块包含一个固定数量的可用EEPROM地址。当需要写入新的EEPROM数据时,首先从当前块中查找空闲EEPROM地址,如果当前块已满,则将数据写入下一个块中。当所有的块都被写满后,将数据从第一个块开始覆盖,直到所有的块都被覆盖过一次。这种策略可以最小化Flash存储器的擦除次数,并延长Flash存储器的使用寿命。 无论采用哪种策略Flash模拟EEPROM都需要考虑数据的可靠性和一致性。由于Flash存储器的写入操作可能会失败或产生错误,因此需要采用一些纠错和恢复机制来确保数据的正确性。此外,还需要考虑多任务和多线程的情况,以确保数据的一致性和完整性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值