资源描述
详解 AVR 单片机防 eeprom 掉数据的办法应用AVR芯片内部EEPROM写入(或写入后读出)出错问题,下面有AVR的芯片手册有相关介绍(来自 mega8 中文翻译文档):防止EEPROM数据丢失若电源电压过低,CPU和EEPROM有可能工作不正常,造成EEPROM数据的毁坏(丢失)。这种情况在 使用独立的EEPROM器件时也会遇到。因而需要使用相同的保护方案。由于电压过低造成EEPROM数据损坏有两种可能:一是电压低于EEPROM写操作所需要的最低电压; 二是 CPU 本身已经无法正常工作。EEPROM 数据损坏的问题可以通过以下方法解决:当电压过低时保持AVR RESET信号为低。这可以通过使能芯片的掉电检测电路BOD来实现。如果BOD电 平无法满足要求则可以使用外部复位电路。若写操作过程当中发生了复位,只要电压足够高,写操作仍将 正常结束。以上官方文档介绍主要提示的是电压过低发生的异常而造成读写错误,并未涉及到其它问题。 个人总结,在实际应用过程中有以下问题造成数据读写错误:1. 程序受到干扰(或程序存在BUG)造成写入EEPROM的数据本身就是错误的;2. EEPROM 写入次数过多(这个问题在频繁写入时会遇到),造成无法写入的;3. 再提电压问题:由于电压过低,造成写入的数据实际未写入或写入错误;EEPROM 写入错误问题是不可避免的,因此就应有相关的归避措施和恢复措施:从硬件方面来说:加入BOD措施是必要的,同时芯片的电源滤波也有较高要求,芯片的复位电路、晶振(及芯片晶振设置位,指单片机的工作频率,这对EEPROM读写有影响)也应仔细处理,以提高抗干扰; 当然,一个设计优良的线路板对抗干扰有很大帮助;从软件方面来说:可以有以下方式进行控制:1. 在写入EEPROM前,需对写入的EEPROM数据进行验证措施,若不正常则不写入;2. EEPROM写入后再读出(即较验),写前数据比较,应一致,否则可能为EEPROM无法再写入,这时 可能要更换存储区地址;3楼主的解决方案有比较好的效果,但是我14楼提出的问题:太占EERPOM存储空间了,可以精简一 下会更好;因为AVR内部的EEROM区有限,若存在大量存储数据情况下,则有可能选用高阶的芯片而造 成成本上升;4. 数据读出时有验证,并存在恢复措施,以使数据错误降到最低。以下是另一种办法.附源码基本思路:每份写到EEPRM的数据,都做三个备份,每个备份的数据都做CRC16校验,只要系统运行中 出错,错误地修改了 EEPROM 数据,那么根据校验字节就知道哪个备份的数据被修改了,然后用正确的备份覆盖出错的备份,达到数据恢 复的目的。EEPROMSave.h 文件:/* EEPROM 管理定义 */#define EepromPage0Addr #define EepromPage1Addr #define EepromPage2Addr #define EepromPage3Addr #define EepromPage4Addr #define EepromPage5Addr #define EepromPage6Addr #define EepromPage7Addr0x0000/各个页的其始地址定义(EepromPage0Addr + EepromPageSize) (EepromPage1Addr + EepromPageSize) (EepromPage2Addr + EepromPageSize) (EepromPage3Addr + EepromPageSize) (EepromPage4Addr + EepromPageSize) (EepromPage5Addr + EepromPageSize) (EepromPage6Addr + EepromPageSize)/*最后两个字节为 CRC16 校验码,其余为数据| 61 | 62 | 63 |.DataCRCH CRCL| 0 | 1 | 2 | Data Data*/ #define VALID 0x01#define INVALID 0x00/*/EEPROMSave.c 文件:* 函数名称:EepromReadByte()*函数功能:写一个Byte的数据进EEPROM*输入参数:address :地址*返回参数:从指定地址读出来的数据* 编写作者: my_avr*编写时间: 2007 年8 月13日*相关说明: unsigned char EepromReadByte(unsigned char *address)unsigned char data;data = 0;eeprom_busy_wait();data = eeprom_read_byte(address);return data;* 函数名称:EepromReadWord();*函数功能:写一个Word的数据进EEPROM*输入参数:address :地址*返回参数:从指定地址读出来的数据* 编写作者: my_avr*编写时间: 2007 年8 月13日*相关说明: uint16_t EepromReadWord(uint16_t 函数名称: EepromWriteWord()*函数功能:写一个Word的数据进EEPROM*输入参数:address:地址;data:数据*返回参数:* 编写作者: my_avr*编写时间: 2007 年8 月13日address)uint16_t data;data = 0;eeprom_busy_wait();data = eeprom_read_word(address);return data;*函数名称: EepromWriteByte()*函数功能:写一个Byte的数据进EEPROM*输入参数:address:地址;data:数据*返回参数:无* 编写作者: my_avr*编写时间: 2007 年8 月13日*相关说明: void EepromWriteByte(unsigned char *address,unsigned char data) eeprom_busy_wait(); eeprom_write_byte(address,data);*相关说明:void EepromWriteWord(unsigned int *address,unsigned int data) eeprom_busy_wait(); eeprom_write_word(address,data);* 函数名称:EepromWriteBlock()*函数功能:将缓冲区中的n个数据写进EEPROM*输入参数:address:地址;data:数据*返回参数:* 编写作者: my_avr*编写时间: 2007 年8 月13日*相关说明: void EepromWriteBlock(unsigned char *buff,unsigned char *address,unsigned char n)unsigned char i;for (i = 0; i n; i+)EepromWriteByte(unsigned char *)(address + i),*buff);buff+;*函数名称: unsigned char EepromCheck(unsigned char *pdata,unsigned char packsize) *函数功能:检查EEPROM的数据是否有效,采用CRC16校验技术。一次校验默认最后两个字节为校验码, 需要注意,packsize包括数据长度和校验码字节*输入参数:pdata:数组指针;packsize:数据长度*返回参数:数据是否有效,有效:VALID,无效:INVALID* 编写作者: my_avr*编写时间: 2007 年8 月21 日*相关说明: unsigned char EepromCheck(unsigned char *pdata,unsigned char packsize) unsigned char i,j;unsigned int crc,ref_crc;crc = 0; ref_crc = 0;for (i = 0; i (packsize - 2); i +)crc = crc A (uintl6_t) EepromReadByte(pdata) 8);for (j = 0; j 8; j+)if (crc & 0x8000)crc = (crc l) A 0xl02l;elsecrc = crc l;pdata +;ref_crc = (uintl6_t) EepromReadByte(pdata);ref_crc = ref_crc8;pdata +;ref_crc |= (uintl6_t) EepromReadByte(pdata);if (crc = ref_crc)return VALID;elsereturn INVALID;*编写时间:2007 年 8 月 21 日*相关说明:unsigned char CheckWriteCRC(unsigned char *pdata,unsigned char packsize) unsigned char i,j;unsigned int crc;crc = 0;for (i = 0; i (packsize - 2); i +)crc = crc A (uintl6_t) EepromReadByte(pdata) 8);for (j = 0; j 8; j+)if (crc & 0x8000)crc = (crc l) A 0xl02l;elsecrc = crc 8);pdata +;EepromWriteByte(pdata,(uint8_t) crc);pdata +;if (EepromCheck(pdata - packsize),packsize)return VALID;elsereturn INVALID;* 函数名称:unsigned char CheckAllPage(void)*函数功能:检查 EEPROM 数据是否有效,检查三个备份 *输入参数:无*返回参数:操作成功否?,成功:VALID,失败:INVALID* 编写作者: my_avr*编写时间: 2007 年8 月21日*相关说明: uint8_t CheckAllPage(void)if (EepromCheck(unsigned char *)EepromPage1Add,EepromPageSize) = VALID) &(EepromCheck(unsigned char *)EepromPage2Add,EepromPageSize) = VALID) &(EepromCheck(unsigned char *)EepromPage3Add,EepromPageSize) = VALID)return VALID;return INVALID;*函数名称: unsigned char DataRecover(void)*函数功能:检查 EEPROM 数据是否被破坏,如果被破坏了,作数据恢复*输入参数:无*返回参数:操作成功否?,成功:VALID,失败:INVALID* 编写作者: my_avr*编写时间: 2007 年8 月21日*相关说明: uint8_t DataRecover(void)unsigned char i;unsigned char temp;unsigned char page;unsigned int invalidpage3; unsigned int validpage;invalidpage0 = 0; invalidpage1 = 0; invalidpage2 = 0; validpage = 0;temp = 0;if (EepromCheck(uint8_t *)EepromPage1Add,EepromPageSize) = VALID) validpage = EepromPage1Add;elseinvalidpagepage = EepromPage1Add; page +;if (EepromCheck(uint8_t *)EepromPage2Add,EepromPageSize) = VALID) validpage = EepromPage2Add;elseinvalidpagepage = EepromPage2Add; page +;if (EepromCheck(uint8_t *)EepromPage3Add,EepromPageSize) = VALID) validpage = EepromPage3Add;elseinvalidpagepage = EepromPage3Add; page +;if (page = 3)return INVALID;/三个备份都被破坏了/ 数据完全无效了while (page-) 0)/数据恢复for (i = 0; i EepromPageSize; i +)temp = EepromReadByte(uint8_t *) (validpage + i); EepromWriteByte(uint8_t *) (invalidpagepage + i),temp); if (CheckAllPage() = VALID) return VALID;return INVALID;使用方法(三个备份):1、定义一个数组:EEPROMDataEepromPageSize-2,数组定义为EepromPageSize-2是为了给每个备份留2 个字节的校验2、 要保存数据时,先把数据放到数组中,然后调用EepromWriteBlock()函数,把这个数组的数据写进 EEPROM,三个备份要写三次。3、写完了之后,调用CheckWriteCRC()函数,该函数会计算出当前备份的CRC16检验数据并写到EEPROM 备份的尾部,有多少个备份就要调用多少次。4、至此,数据的备份工作已经完成。5、校验数据(一般在复位后运行),执行CheckAllPage()函数,若通过了,则EEPROM数据没有问题,否 则要运行DataRecover()函数,对损坏的备份进行修复*函数名称:unsigned char CheckWriteCRC(unsigned char *pdata,unsigned char packsize) *函数功能:为 EEPROM 数据写 CRC 校验码*输入参数:pdata:数组指针;packsize:数据长度*返回参数:操作成功否?,成功:VALID,失败:INVALID* 编写作者: my_avr
展开阅读全文