在仪器仪表及其他一些嵌入式系统中,通常需要存储一些设置参数、现场数据,这些参数和数据要求系统掉电时不丢失,在下次加电工作时系统能够自动恢复原先设定的数据。为了实现上述功能,通常需要给系统配置一定容量的EEPROM。本文以笔者设计某型号的智能流量计时,在应用EEPROM的过程中遇到的问题及解决方法为例,来说明EEPROM的应用方法。具体包括:系统中EEPROM的配置方法、EEPROM驱动程序的设计方法、延长EEPROM寿命的方法。
1EEPROM的配置
当系统需要EEPROM功能时,如何进行配置是在系统设计初期选择微控制器时就要考虑的问题。尽管越来越多的微控制器配备了内部的EEPROM,但是还有大量的微控制器(比如传统的MCS51单片机、PIC系列大部分型号的产品、MC68HC05/08系列大部分型号的产品)没有配置内部的EEPROM。因此,在选择微控制器的时候就需要综合考虑各种因素,以确定EEPROM的配置方案是采用内部EEPROM还是采用外扩的方式,这直接影响系统的可扩展性、安全性及综合成本。
采用外扩EEPROM时,以串行通信方式居多,常见的串行通信方式有I2C、SPI、Microwire等几种形式。采用外扩EEPROM时,可以根据系统的实际需求灵活选择容量合适的EEPROM芯片,比如Atmel公司的AT24C01/02/04等不同容量的芯片,其中AT24C1024B的容量为1 Mbit,选择空间十分大。尽管采用外扩EEPROM具有廉价、灵活的特点,但其缺点是占用微控制器有限的引脚资源,电路结构复杂,同时保密性差。
笔者在设计流量计时综合考虑各种因素,采用微控制器内置的EEPROM,选用的单片机是Microchip公司的PIC16F876A,在其内部作为一个片内外设模块配置了容量为256×8位的EEPROM,通过特殊功能寄存器对其进行读写。与外扩EEPROM的方式相比,此种方案具有电路结构简单、开发方便、数据保密性好的优点。
2EEPROM驱动程序的设计
EEPROM驱动程序的设计方法与EEPROM的配置方案、选用的微控制器及开发工具有关。笔者在设计流量计时采用的是微控制器内部自带的EEPROM,选用的单片机是PIC16F876A,集成开发环境是Microchip公司的MAPLAB 8.3及HITECH公司的C编译器PICC 9.5。下面详细介绍一下EEPROM驱动程序的设计方法。
为了对单片机内部的EEPROM进行读写,单片机额外增加了4个特殊功能寄存器:地址寄存器EEADR、数据寄存器EEDATA、读写控制第一寄存器EECON1、写控制第二寄存器EECON2。按照规定的操作步骤对这几个寄存器进行操作,就可以完成对EEPROM一个字节的读写操作。但实际进行驱动程序设计时,可以直接利用PICC提供的EEPROM读写函数,函数原型为:
unsigned char eeprom_read(unsigned char addr);
EEPROM读函数,addr为欲读的单元的地址,返回值为指定单元的数据。
void eeprom_write(unsigned char addr, unsigned char value);
EEPROM写函数,addr为欲写入的单元的地址,value为欲写入的数据。
上述两个函数只能够实现单个字节的读写,而在系统运行过程中通常要求写入EEPROM中的数据为多字节。比如,流量计在运行过程中通常需要将累积量写入EEPROM中,为了保证足够的计数范围和累积精度,在编程时将累积量定义为double型的变量,PICC默认的double型变量为24位,但可以通过改变编译选项改为32位,笔者采用的是32位。那么如何将4字节的累积量写入EEPROM和从EEPROM读出呢?浮点数存储时是存放在连续的字节中的,只要设法找到存储位置就可以找到4个字节中每个字节的值,从而分别对其读写。编程实现时可以定义一指针变量,通过指针进行操作。
以下两个函数是笔者自行编制的用于EEPROM读写的函数:
void ee_double_write(unsigned char addr, double data) {
unsigned char *p,i;
p = (unsigned char *)&data;
for(i=0; i<=3;i++) {
eeprom_write(addr+i,*p);
p++;
}
}
向EEPROM中写入double型数据,addr为需要写入的数据在EEPROM中的首地址,data为需要写入的数据。
double ee_double_read(unsigned char addr) {
unsigned char array[4],i;
double *p;
for(i=0; i<=3; i++)
array[i]=eeprom_read(addr+i);
p = (double *)array;
return(*p);
}
从EEPROM中读出double型数据,addr为读出数据在EEPROM中的首字节地址,函数返回值为double型数据。其他类型的数据在EEPROM中的读写方法与此类似,不再赘述。
3延长EEPROM寿命的方法
向EEPROM中写入数据实际是一个烧写的过程,对EEPROM具有一定的破坏性,因此一个EEPROM单元的擦写次数是受限的。具体擦写次数视不同厂家、不同系列的芯片而定。Microchip公司的PIC16F876A内部EEPROM的擦写次数据为100万次(数据文档记录),对于一些不需要频繁改变的参数(比如流量计中上限流量、下限流量等设定参数)而言完全可以满足要求。但是当用固定的4字节存放流量累积量时,是不能满足要求的。因为,在综合考虑各种因素后,要求流量计在运行时每10 s存放1次累积量,则EEPROM能安全运行的天数为:1×106/(360×24)≈117天,不能满足要求。必须采用一定的方法延长EEPROM的读写寿命。
笔者采用的方法是在EEPROM中开辟一段空间滚动存放累积量,具体实现办法是:将EEPROM中地址为1~160的单元用于滚动存放累积量,每4字节为一组,共40组,每组连续存放50次(根据需要可以改为其他数值),然后在下一组中存放。当在第40组(起始首字节地址为157)连续存放50次后,再回滚到第1组(起始首字节地址为1)开始存放。由全局变量SaveTime记录在某一组连续存放的次数。EEPROM中地址为0的单元用于存放当前累积量有效值所在组的首字节地址(记为SaveAddr),当某一组连续存放50次,滚入下一组时更新SaveAddr的值。具体流程如图1所示。
实现上述功能的函数为:
void ee_Cumulation_write(double data) {
SaveTime++;//同一组位置连续存的次数加1
if(SaveTime > 50) {
SaveTime = 0;
SaveAddr += 4;
//转到下一组位置
if(SaveAddr > 157)
//到达上边界,回转
SaveAddr = 1;
eeprom_write(0,SaveAddr);
//EEPROM位置0存放当前累积量的首址
}
ee_double_write(SaveAddr,data);//存放累积量
}
图1写EEPROM流程
采用此种方法后,EEPROM的寿命可以延长40倍,为117天×40=4 680天≈12.8年,可以充分满足要求。当采用此种方法写累积量后,若需要读出累积量则需要首先读取EEPROM地址0的内容,得到有效累积量的存放首地址,然后再调用函数ee_double_read()进行读取。
结语
本文以笔者设计的某型号智能流量计为背景,总结了EEPROM在仪表及嵌入式系统中应用时需要注意的问题,以及相应解决方法。希望本文能够对读者有所启发和帮助,起到抛砖引玉的作用。