基于嵌入式MCU数据Flash的数据存储及管理方法研究与实现

  摘要:本文设计了一种利用MCU内部数据Flash存储非易失性数据的方法,它将数据Flash的若干扇区划分为多个数据分区,不同数据分区存储数据在不同历史时间的拷贝,最新数据分区存储最新的数据拷贝;在数据读操作进行时,计算最新数据拷贝的Flash存储位置,直接读取该地址;在数据写操作进行时,判断数据写入位置是否已经被擦除,如果写入位置未擦除,将数据写入下一个分区,同时将当前分区中的其他数据复制到下一个分区;如果写入位置已经擦除,直接将数据写入当前分区中。该方法实现了类似EEPROM的数据读写方式,操作方便,应用接口简单,而且可以尽量避免扇区擦除操作,提高存储效率,同时提高MCU内部数据Flash的使用寿命。

  引言

  在嵌入式系统设计中,经常需要存储一些非易失性的数据,在笔者开发的电动汽车仪表盘中,需要存储总里程、小计里程、电机故障等其他信息,采用支持对字节读写的EEPROM实现数据存储,操作起来和RAM一样简单方便,但同时会在大批量产品的生产中带来成本问题和维护问题。在有数据Flash的MCU中,采用数据Flash代替EEPROM实现非易失性的存储,便可以节约成本且无需维护,笔者所设计的仪表盘采用内置4KB数据闪存的MC9S12HY32做为处理器,足以满足仪表盘数据存储要求。用Flash存储数据的传统方式是为每个数据分配固定的存储地址,由于Flash在进行写操作时需要先擦除数据所在的整个扇区[1],对一个数据进行写操作便会造成对扇区内其他数据的擦除,由于擦除操作耗时较长,不仅效率低,影响嵌入式系统的实时性,而且为了避免丢失其他数据需要相当复杂的处理,对MCU的RAM空间也有一定的要求。如果写入数据失败,会造成所写入数据的丢失,如果在擦除扇区后发生掉电,便会造成扇区内所有数据的丢失。不仅如此,由于每次写入操作都需要先擦除扇区,以擦除次数表征的Flash使用寿命也无法满足产品生命周期的要求。本文提供一种利用MCU内部数据Flash存储非易失性数据的方法[2],它不仅操作方便,应用接口简单,而且可以尽量避免扇区擦除操作,提高存储效率,同时提高MCU内部数据Flash的使用寿命。

  总体设计

  通过在MCU数据Flash上建立多个数据分区,存储数据的多个拷贝,避免对Flash固定地址的反复擦除,提高Flash的使用寿命,同时通过数据读写方法的设计和数据分区的管理,避免对Flash扇区的不必要擦除,并最终实现和EEPROM读写很类似的应用接口。具体地,首先根据嵌入式系统的应用需求和MCU内部数据Flash的扇区大小,合理设置数据分区大小和个数,将数据Flash的若干扇区划分为多个数据分区。在每个数据分区的起始地址设置分区状态字[3],反映数据分区的存储历史时间,不同数据分区存储数据在不同历史时间的拷贝,当前数据分区存储最新的数据拷贝;同时为每个数据条目建立数据状态字,反映该数据在所在分区内存储地址是否已经被擦除。

  系统上电后,首先根据数据分区状态字查找存储最新数据的分区,将之设置为最新数据分区,并设置其分区状态字为最新分区状态字。在数据读操作进行时,根据最新数据分区及数据在分区内的偏移地址计算最新数据拷贝的Flash存储位置,直接读取该地址。在数据写操作进行时,首先计算该数据的Flash存储地址,然后根据数据状态字判断数据所在的Flash存储地址是否已经被擦除[4],如果写入位置已经擦除,直接将数据写入当前分区中,其他数据保持不变;如果写入位置未擦除,进行分区拷贝操作,即将数据写入下一个分区,将当前分区中的其他数据依次复制到下一个分区,同时将下一个分区设置为最新数据分区,更新最新分区状态字并存储在最新数据分区首地址位置。其设计流程如图1所示。

  具体实施

  数据分区设计

  首先根据嵌入式系统的应用需求和MCU内部数据Flash的扇区大小,合理设置数据分区大小和个数,将数据Flash的若干扇区划分为多个数据分区,其取值均为2的n次幂,分区以0,1,2…进行编号,个数不大于256。分区大小和个数的设置和数据Flash的扇区长度匹配起来,满足以下公式:

  分区大小*分区个数=扇区大小*扇区个数 (1)

  在每个数据分区的起始地址设置分区状态字,反映数据分区的存储历史时间,在分区擦除后的第一次写操作完成后更新。设置数据条目的格式为data id+data,data id取值区间为[0,254],为每个数据条目的data id和data分配偏移地址,建立数据序列,组织数据分区,数据分区的格式为:分区状态字+dataid1+data1+dataid2+data2…。数据分区这样的存储结构非常适合需要进行多个独立数据存储的嵌入式系统应用,通过数据分区的格式定义,对其某个数据的寻址非常简单。

  数据读取操作

  数据读取操作在最新数据分区上进行,首先通过数据条目的data id进行偏移地址查表,然后根据最新数据分区编号进行地址计算,计算公式如下:

  地址=0号分区首地址+(最新数据分区编号*分区大小)+偏移地址 (2)

  和EEPROM的读取方式一样,直接读取该地址便可以得到数据[5],读取操作不会改变最新数据分区及其状态字。其软件实现如下所示:

  void ReadEeprom(uint16_t data_id, void *dest_addr,uint16_t size)

  {

  u_EepromWord eedata;

  uint16_t src_addr;

  src_addr = GetDataAddrFromItsId(data_id);

  src_addr += (Active_bank * EEPROM_SIZE_BYTES);

  while(0 != size){

  eedata.word = READFLASH16(src_addr);

  *(uint8_t*)dest_addr = eedata.byte.msb;

  ((uint8_t*)dest_addr)++;

  src_addr++;

  size--;

  }

  }

  数据写操作

  在数据条目的设计中,将数据条目的data id存储地址同时做为数据状态字的存储地址,在写操作时通过数据状态字判断该数据条目地址是否执行过擦除操作,从而避免不必要的分区拷贝和扇区擦除操作,降低数据写操作代价[6]。首先根据公式(2)计算数据在最新数据分区的存储地址,读取其data id存储位置得到数据状态字,判断该数据在最新数据分区的写入地址是否已经被擦除。如果数据状态字等于0xff,表明写入地址已经被擦除,按照Flash的写操作命令序列在data id地址处写入data id,在data地址处写入data;如果数据状态字不等于0xff,表明写入地址未被擦除,需要进行分区拷贝操作,操作完成后更新最新分区及最新分区状态字。数据写操作流程如图2所示。

  通过为每个数据建立状态字来表示是否已经在当前分区上进行了存储操作,数据1的存储操作便不会影响数据2的存储,数据2仍然能够在当前分区上进行存储,而不会每次只要有数据的写操作都会造成所有的数据在分区之间的搬移,这样不仅提高了写操作的效率,而且会进一步提高Flash的使用寿命[7]。

  分区拷贝操作

  在进行数据在分区间的拷贝操作时,首先备份当前最新分区状态字和最新数据分区编号,然后更新最新数据分区编号,查看最新数据分区首地址是否是Flash扇区首地址,如果是,执行扇区擦除操作[8],然后按照Flash的写操作命令序列在当前最新数据分区的data id地址处写入data id,在data地址处写入data,然后将备份数据分区内的其他数据复制到当前最新数据分区中。

  最新数据分区及状态字更新

  最新数据分区编号的更新算法为:将最新数据分区编号加一,判断其结果,如果最新数据分区编号等于分区个数,设置最新数据分区编号为0。状态字更新算法为:判断所备份最新数据分区状态字是否等于0xfe,如果等于0xfe,设置最新分区状态字为0,否则最新分区状态字加一,然后将最新分区状态字写入当前最新数据分区状态字地址,即数据分区首地址位置。

  掉电存储

  如果嵌入式系统在写操作期间掉电,由于在发生掉电时最新数据分区状态字还没有更新,再次上电时查找到的最新数据分区仍然是写操作进行前的那个数据分区,通过在写入操作完成后更新状态字的方式保证了即使发生了掉电,重新上电后数据仍能恢复为原来的数据分区中的数据。上电时通过分区状态字查找最新数据分区的算法如下:

  (1)当存在取值为0的分区状态字时,小于分区数的最大状态字代表的分区为最新数据分区;

  (2)当不存在取值为0的分区状态字时,最大状态字代表的分区为最新数据分区;该算法实现流程如附图3所示,上电后经过该算法处理后,可以得到最新数据分区编号和最新数据分区状态字,其软件代码如下所示:

  static void FindNewestBankByStatusWord(void)

  {

  Bool roll_over;

  uint8_t bank;

  uint16_t bank_status;

  uint16_t largest_status = 0;

  Bool erased = TRUE;

  roll_over = StatusWordIsRollover();

  for(bank = 0;bank < EEPROM_BANKS;bank++){

  bank_status = READFLASH16(EEPROM_START + (bank * EEPROM_SIZE_BYTES));

  if(FLASH_ERASED_WORD != bank_status){

  erased = FALSE;

  if(TRUE == roll_over){

  if((bank_status < EEPROM_BANKS) && (bank_status >= largest_status)){

  Active_bank = bank;

  largest_status = bank_status;

  }

  }else{

  if(bank_status > largest_status){ Active_bank = bank;

  largest_status = bank_status;

  }

  }

  if(TRUE == erased){ Active_bank = 0;

  (void)EraseEepromBank(EEPROM_START);

  }

  }

  具体应用

  在笔者开发的电动汽车仪表盘中,需要存储总里程、小计里程、电机故障等信息,按照前文所述的方法,首先建立数据分区,为每个数据条目建立ID,该ID同时可表征数据状态字:

  ID=1,对应电机故障,大小为1个字节;

  ID=2,对应总里程,大小为4个字节;

  ID=3,对应小计里程,大小为2个字节;

  各数据条目在分区内的格式如下:

  分区状态字Status_word(1个字节)+电机故障ID(1个字节)+电机故障(1个字节)+总里程ID(一个字节)+总里程(4个字节)+小计里程ID(1个字节)+小计里程(2个字节);

  经计算,实际存储需求为11个字节,为了计算的方便,设定分区大小为2的幂,选为16;MC9S12HY32内置数据Flash的扇区大小为256个字节,为了保证掉电不丢失数据,必须占用至少两个扇区,根据公式(1),分区个数选定为32。数据Flash可擦写次数为10万次,在每次数据写操作都会引起数据分区轮转的最坏情况下,数据可擦写次数为10万*32=320万次,大于EEPROM的可擦写100万次的使用寿命,可以满足产品生命周期要求。

  如前文所述,在进行数据读/写操作时,首先通过数据条目ID进行偏移地址查表,计算数据在分区内的相对地址,其实现函数如下:

  uint16_t GetDataAddrFromItsId(uint16_t data_id)

  {

  uint16_t addr;

  if(EE_MOTOR_ERR == data_id){

  addr = EEPROM_START + 1;

  }else if(EE_MILES_TOTAL == data_id){

  addr = EEPROM_START + 3;

  }else if(EE_MILES_RELATIVE == data_id){

  addr = EEPROM_START + 8;

  }else{

  addr = EEPROM_START; /* not valid data id,so give unvalid addr */

  }

  return addr;

  }

  EE_MOTOR_ERR、EE_MILES_TOTAL、EE_MILES_RELATIVE即表示数据条目ID的宏。

  在行车过程中,当小计里程改变时,不仅在液晶屏上实时更新小计里程数据,同时需要把更新后的小计里程写在数据Flash中,其具体函数如下:

  void MilesRelativeStore(void)

  {

  if(Miles_relative != Miles_relative_stored){

  (void)WriteEeprom(EE_MILES_RELATIVE,&Miles_relative,sizeof(Miles_relative));

  Miles_relative_stored = Miles_relative;

  }

  }

  由上述函数可见,应用本专利所设计方法,可以屏蔽底层实现细节,提供给应用层简单、清晰、和EEPROM一样简便的接口。

  结语

  本文从Flash特性出发研究并实现一种高效的数据存储及管理方法,其实现层面实现与EEPROM同样的应用接口,具有很高的使用价值,同时有效利用了MCU的内部资源,提高了MCU Data Flash的使用寿命,使之可以满足产品生命周期要求,并节约了产品的BOM成本,所设计方法在笔者设计的汽车仪表盘中得到实际应用和长时间验证,运行效果良好,具有很好的实用价值。

  参考文献:

  [1] 刘会忠,景瑞霞.损耗均衡算法在Flash管理中的应用研究[J].河北省科学院学报,2009,(2):4-6

  [2] 山东省科学院自动化研究所.一种利用MCU内部数据Flash存储非易失性数据的方法:中国,201210074452.5[P].2012-09-12

  [3] 易军,曹龙汉,周熙.FLASH存储管理子系统的设计与实现[J].微计算机信息,2008,(8):86-87

  [4] 叶树梅,季爱明,俞一彪.嵌入式系统中Flash存储管理策略[J].科学技术与工程,2011,(17):77-79

  [5] 梁海浪,蔡李隆.dsPIC30F6014内部EEPROM读写C程序设计及其应用[J] .电子产品世界,2005,(11):96-98

  [6] 杜伟庆,苏凯雄.嵌入式系统中NOR Flash 的分块管理与实现[J] .计算机与数字工程,2009,(12):80-82

  [7] 王标,周新志,罗志平.嵌入式系统中Nand Flash写平衡的研究[J].微计算机信息,2008,(14):8-10

  [8] 陆林燕,王鲁静,郑正奇.NAND FLASH编程实现研究分析[J].计算机技术与发展,2008,(3):118-120

  刘源杨 马建辉 庄汝科 王岗 山东省汽车电子重点实验室 山东省科学院自动化研究所(山东 济南250014)

……
关注读览天下微信, 100万篇深度好文, 等你来看……
阅读完整内容请先登录:
帐户:
密码: