黄 华,李晓锋,曾小宝
(张家界航空工业职业技术学院,湖南 张家界 427000)
在嵌入式控制系统中,通常会使用EEPROM保存一些掉电后不丢失内容的参数。但EEPROM的写入次数有限,如果系统需要频繁更改参数,极有可能超过EEPROM的使用寿命,如果用于保存参数的EEPROM因为反复擦写而失效,将导致系统运行需要的重要参数丢失,使系统不能正常可靠的工作。
一般来说EEPROM的寿命是100万次擦写,现在一些低成本单片机使用Flash ROM来模拟EEPROM,保存参数数据,它们往往只有10万次的擦写寿命。如果参数不需要频繁的改动,10万次的擦写次数完全够用了,比如即便10分钟擦写一次,可以工作694年。但是,假如系统10秒擦写一次数据,10万次的使用寿命只够系统运行100000/(360*24) = 11.5天,这时就希望EEPROM能有更长的使用寿命。
各种延长EEPROM使用寿命的基本原理都是利用空间换时间,一般来说嵌入式控制系统需要保存的参数数据只有几个字节,相对而言EEPROM的容量比要存储的数据量大得多。如果我们将这些信息保存到EEPROM中固定的地址处,就会出现这样的情况:一方面保存参数的存储单元因为反复擦写已经损坏,另一方面EEPROM中还剩余大量完好未使用的存储单元。
若我们能将EEPROM所有单元都利用起来,使得数据的擦写均匀分布到EEPROM的每一个单元,就可以大大延长EEPROM的使用使命[1]。比如,如果需要保存的参数总共8个字节,EEPROM的容量有256字节,将参数保存平均分布到256字节后,相当于将EEPROM的寿命提高了256/8倍,10万次的擦写寿命就提高到320万次,这就大大提高了系统的使用寿命,因而提高了系统的可靠性。如果这样的擦写寿命仍然不够使用,只需要提高EEPROM的容量就可以得到更高的使用寿命。
采用这种方法后,参数信息就不是保存在EEPROM的固定地址区域,我们需要一种方法来找到EEPROM中保存参数的位置,并且每次将参数写入到不同的EEPROM地址处[2]。为了确定每次保存参数的地址,文献1中介绍了“用后还原法”、“地址指针法”,但这些方法都比较麻烦,通用性不强,使得开发困难,为此笔者研究了一种“打包标记”算法,来查找每次参数的保存位置。
本算法在保存参数时,并不是直接将参数保存到EEPROM中,而是先将参数“打包”后再存放,“打包”后要保存的数据包格式为:
这里,Flag1、Flag2是一个选择用来标记参数存放位置的特殊数据,尽量选择Flag1、Flag2的值与所保存的参数不一样,以便于识别。采用这样的数据包格式存放数据后,查找参数保存地址的算法可描述如下:
1) 设置搜索地址addr为0,查找次数search_times为0;
2) 将addr地址处内容与Flag1比较,若相等,则执行第4步;
3) 搜索地址前进1个单位,如果到了最大的EEPROM地址,折回到0地址,查找次数加1;
4) 读入N个单元的数据到缓冲区,查找次数加N;
5) 读入第N+1个单元,并将其与Flag2比较,如果相等,说明找到了数据存放的单元,将addr保存到全局变量,成功返回,否则继续执行第6步;
6) 判断查找次数是否大于EEPROM容量,若小于,回到第2步继续搜索,若大于,说明没有找到曾经保存了参数,这说明系统并没有经过初始化,返回查找失败。
写入参数的算法可描述如下:
1) 每次写入之前,需要先查找一遍EEPROM,找到以前存放数据的起始地址;
2) 向上一次存放参数的地址的Flag1标志擦除清0;
3) 向前跳过N+1个单元,跳过上一次写入参数的EEPROM地址,开始写入数据。
该算法的实现由load_parma、save_parma两个函数组成,算法中调用eeprom_read函数和eeprom_write函数分别实现数据的读取和写入,如要将该算法移植到不同的系统,只需要实现这两个底层写入函数即可。load_parma函数查找参数保存的地址并存放到param_start_address全局变量,并将参数加载到buf数组缓冲区,save_parma函数跳过当前参数在EEPROM中存放的区域,写入下一个EEPROM区域。
bool load_parma(unsigned char *buf, unsigned char len)
{
unsigned int search_times=0;
unsigned int addr;
unsigned char flag=0;
unsigned char check_sum;
unsigned char i=0;
for(search_times=0;search_times { //从当前的参数存放起始地址开始查找 flag=eeprom_read(param_start_address); //当前地址不是参数启示的标志字节 //跳出循环,从下一个字节继续搜索 if(flag!=PARAM_FLAG1) { param_start_address=next_eeprom_addr(param_start_address); continue; } //找到标志字节,加载数据 addr=param_start_address; //check_sum=PARAM_FLAG; for(i=0;i { addr=next_eeprom_addr(addr); buf[i]=eeprom_read(addr); //check_sum^=buf[i]; } addr=next_eeprom_addr(addr); if(eeprom_read(addr)==PARAM_FLAG2) { //找到参数存放位置 return true; } //校验和计算不正确,是错误的标志字节,重新搜索 param_start_address=next_eeprom_addr(param_start_address); } //全部搜索完毕,未找到已保存参数 return false; } bool save_param(unsigned char *buf, unsigned char len){ unsigned char flag=0; //unsigned char check_sum=PARAM_FLAG; unsigned char i=0; unsigned int addr; //如果参数起始地址为标志字节,说明已经保存过参数 flag=eeprom_read(param_start_address); if(flag==PARAM_FLAG1){ //擦掉上一次保存的标志字节 eeprom_write(param_start_address, 0); //切换下一个个区块来保存数据 for(i=0;i param_start_address=next_eeprom_addr(param_start_address); //跳过Flag2单元 param_start_address=next_eeprom_addr(param_start_address); } eeprom_write(param_start_address,PARAM_FLAG1); addr=next_eeprom_addr(param_start_address); for(i=0;i { eeprom_write(addr,buf[i]); //check_sum^=buf[i]; addr=next_eeprom_addr(addr); } eeprom_write(addr,PARAM_FLAG2); return true; } 为了验证算法的有效性,笔者在PC机上编写了一个测试程序,用内存模拟EEPROM,将随机数据先使用save_parma保存,在使用load_parma读取,将读取到的数据与写入前的数据进行比对,经过100亿次的循环测试,数据没有出现错误,这说明该算法是有效可行的。 本文给出一种延长EEPROM使用寿命的算法,在笔者使用该算法设计的一个数据记录仪系统上应用,系统已经长时间稳定运行,经实践检验该算法运行可靠,具有一定的参考价值。4 结语