[原创]治标治本,彻底解决AVR单片机EEPROM数据丢失问题
在项目中复制出来的程序,使用时可能有些地方需要修改。编译环境:WinAVR-20060421 + AVR Studio 4.12.498Service Pack 4
基本思路:每份写到EEPRM的数据,都做三个备份,每个备份的数据都做CRC16校验,只要系统运行中出错,错误地修改了EEPROM数据,
那么根据校验字节就知道哪个备份的数据被修改了,然后用正确的备份覆盖出错的备份,达到数据恢复的目的。
EEPROMSave.h 文件:
/* EEPROM管理定义 */
#define EepromPageSize 64 //页容量定义
#define EepromPage0Addr 0x0000 //各个页的其始地址定义
#define EepromPage1Addr (EepromPage0Addr + EepromPageSize)
#define EepromPage2Addr (EepromPage1Addr + EepromPageSize)
#define EepromPage3Addr (EepromPage2Addr + EepromPageSize)
#define EepromPage4Addr (EepromPage3Addr + EepromPageSize)
#define EepromPage5Addr (EepromPage4Addr + EepromPageSize)
#define EepromPage6Addr (EepromPage5Addr + EepromPageSize)
#define EepromPage7Addr (EepromPage6Addr + EepromPageSize)
/*
最后两个字节为CRC16校验码,其余为数据
| 0 | 1 | 2 | |.......................| 61 | 62 | 63 |
Data Data...................Data.....CRCH CRCL
*/
#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 *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);
}
/*******************************************************************
*函数名称:EepromWriteWord()
*函数功能:写一个Word的数据进EEPROM
*输入参数:address:地址;data:数据
*返回参数:
*编写作者:my_avr
*编写时间:2007年8月13日
*相关说明:
********************************************************************/
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 intcrc,ref_crc;
crc = 0;
ref_crc = 0;
for (i = 0; i < (packsize - 2); i ++)
{
crc = crc ^ ((uint16_t) EepromReadByte(pdata) << 8);
for (j = 0; j < 8; j++)
{
if (crc & 0x8000)
{
crc = (crc << 1) ^ 0x1021;
}
else
{
crc = crc << 1;
}
}
pdata ++;
}
ref_crc= (uint16_t) EepromReadByte(pdata);
ref_crc= ref_crc<<8;
pdata ++;
ref_crc |= (uint16_t) EepromReadByte(pdata);
if (crc == ref_crc)
{
return VALID;
}
else
{
return INVALID;
}
}
/*******************************************************************
*函数名称:unsigned char CheckWriteCRC(unsigned char *pdata,unsigned char packsize)
*函数功能:为EEPROM数据写CRC校验码
*输入参数:pdata:数组指针;packsize:数据长度
*返回参数:操作成功否?,成功:VALID,失败:INVALID
*编写作者:my_avr
*编写时间:2007年8月21日
*相关说明:
********************************************************************/
unsigned char CheckWriteCRC(unsigned char *pdata,unsigned char packsize)
{
unsigned char i,j;
unsigned intcrc;
crc = 0;
for (i = 0; i < (packsize - 2); i ++)
{
crc = crc ^ ((uint16_t) EepromReadByte(pdata) << 8);
for (j = 0; j < 8; j++)
{
if (crc & 0x8000)
{
crc = (crc << 1) ^ 0x1021;
}
else
{
crc = crc << 1;
}
}
pdata ++;
}
EepromWriteByte(pdata,(uint8_t) (crc>>8));
pdata ++;
EepromWriteByte(pdata,(uint8_t) crc);
pdata ++;
if (EepromCheck((pdata - packsize),packsize))
{
return VALID;
}
else
{
return 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 intinvalidpage;
unsigned intvalidpage;
invalidpage = 0;
invalidpage = 0;
invalidpage = 0;
validpage = 0;
temp = 0;
page = 0;
if (EepromCheck((uint8_t *)EepromPage1Add,EepromPageSize) == VALID)
{
validpage = EepromPage1Add;
}
else
{
invalidpage= EepromPage1Add;
page ++;
}
if (EepromCheck((uint8_t *)EepromPage2Add,EepromPageSize) == VALID)
{
validpage = EepromPage2Add;
}
else
{
invalidpage= EepromPage2Add;
page ++;
}
if (EepromCheck((uint8_t *)EepromPage3Add,EepromPageSize) == VALID)
{
validpage = EepromPage3Add;
}
else
{
invalidpage= 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 *) (invalidpage+ i),temp);
}
}
if (CheckAllPage() == VALID)
{
return VALID;
}
return INVALID;
}
使用方法(三个备份):
1、定义一个数组:EEPROMData ,数组定义为EepromPageSize-2是为了给每个备份留2个字节的校验
2、要保存数据时,先把数据放到数组中,然后调用EepromWriteBlock()函数,把这个数组的数据写进EEPROM,三个备份要写三次。
3、写完了之后,调用CheckWriteCRC()函数,该函数会计算出当前备份的CRC16检验数据并写到EEPROM备份的尾部,有多少个备份就要调用多少次。
4、至此,数据的备份工作已经完成。
5、校验数据(一般在复位后运行),执行CheckAllPage()函数,若通过了,则EEPROM数据没有问题,否则要运行DataRecover()函数,对损坏的备份进行修复
------------------修改原因:修改变量的定义形式 顶一下 Cool ! 楼主有想法,
但是对于我这样只利用eeprom保存十几个字节的应用(每个字节或字 代表不同的运行参数), 好像太过了
其实我想知道为什么eeprom里的内容会莫名其妙的变掉 哈哈,AM真快
一场秋雨一层凉,总算有条裤子穿了 有点过了,谈不是治本;)。多看看自已的程序,多看看自已的电路吧,我暂时没了现EEPROM丢的现象。 用AVR几年了,从来没发现会丢EEPROM数据。
听同事说曾经遇到过退回的一个产品数据丢了,据说是所有的数据都变成0了,个人认为有两方面的原因:
1,程序有漏洞;
2,遇到了很强的干扰,直接把EEPROM的数据改写了?????(可能性不大,因为我曾经在EEPROM中写入一批数据,程序只读不写,然后用10KV左右的高压对电路板的地线放电十天,读出来的数据依然是正确的) 好,谢谢LZ。 【4楼】 my_avr
一场秋雨一层凉,总算有条裤子穿了
深圳还是这么热。 楼主的方法可以参考偶啊一下
谢谢~~ 在freescale上用过类似的方法 dd 真不错来着 cool 对可靠性要求高的场合,这个方法可以用用,也有不错的效果。
但是对这个方法充其量,也只能说治标而已。我实在看不出哪里有治本的语句或方法存在?做3个备份,也就是等于把数据存储缩小到原来空间的4倍,这个方法对于少量数据来说可以试试,但是对于大量数据的保存是绝对不适合的。究其原因是没有治本,而只是治标而已。
真正的治本应该是硬件为主,软件为辅,软硬结合。 支持楼主,好想法 应急可行 mark 不错 个人觉得意义不大,我之前也做过相关实验,因为一旦EEPROM出问题,根本就不是因为被错误改写,因为错误的改写必须有正确的时序,也就是说EEPROM出问题的时候一般都是硬件,主要是电压波动导致的,出问题的时候数据被毁好大片,你这种办法可以有所改善,但是不可能是根本的,其实把BOD打开EEPROM很少会出问题,当然了,一旦出问题,是没有任何办法可以解决的,LZ的彻底解决我不知道什么依据...个人感觉比较武断,我只是谈谈个人想法,仅仅是技术交流,LZ别搞我的人啊,小声说一句:偶也是武汉的: 顺便说一句,做 CRC 还不如做 RS 好 严重同意19楼!
我曾经做过类似的试验:
RAM数据备份,每个数据存三个不连续的地方,当RAM的数据被强干扰改变时(一般是不容易改变的),按三中取二判断,结果要么是三个数据都不同,要么三个数据都一样但不是正确的数据,后来再多备份几个也一样。
EEPROM数据被改写,有以下几种原因:
1,BOD没打开时,由于电源电压不稳或比较低时,程序乱跑,运行了写EEPROM的程序,这时即使是备份10份,校验也是正确的,因为是你自己写的;
2,MCU受到干扰,导致程序跑飞,运行了写EEPROM的程序,结果同1;
3,MCU受到干扰或电源不稳,直接导致EEPROM数据被改写?(没碰到过,也无法验证);
……
所以我认为如果程序完全没有问题,楼主这样做一点意义都没有,最多只是因为心理作用会自我感觉程序比较健壮;如果程序有BUG,按楼主这样做有可能会减少出错的机会甚至避免出错,但这是治标不治本。 【19楼】 gaoqiru
【20楼】 zrl700424
其实我的程序是经过考验的。去年在做一个汽车上的项目,M16控制8个继电器来控制4个电机正反转,因为系统要存储四个电机的参数,只好把数据存到EEPROM里,不过客户也可以自己设定这些数据。硬件方面该考虑的都考虑,自认为系统在抗干扰方面还不是很差。一开始,没有做什么数据备份,只是打开了BOD,可是系统的参数老出错。最后,只能采用了备份校验的方法,从此后,系统没有出现过掉数据的问题。
因为经常看到有人在问“我的EEPROM数据怎么老改写呀?”之类的问题,我在这里只是介绍一下我个人的解决方法而已,欢迎讨论!我相信会有更好的解决方法的。 AVR单片机的EEPROM并不是鸡肋,用好了可能变成鸡腿,^_^ 我觉得EEPROM数据出问题应该来说和软件关系不是太大,大部分出问题是直接造成了EEPROM存储单元的物理性质发生了变化,并不是通过读写时序来控制的,也不是因为程序跑飞来改变的.你可以用其他外部EEPROM做实验,即使你把写使能关掉也是一样的,一旦出问题你没有办法,之前我的使用24C01不管做再大的努力也没有用,只是情况有所好转,(因为我们的产品是大概100多个设备连在一起的,如果是单个好象永远都不会出问题),之后换成了MEGA48,开始用的时候没有打开BOD,数据经常丢失,最后我把BOD打开,再也没有出现过类似的问题,LZ的这个办法确实是可行的,但是离彻底解决距离遥远,因为彻底解决根本就不是我们这些人能够解决的了的,除非出现新的材料,新的工艺,这才是治本的办法啊.... eeprom出错的原因有:
1、程序问题(不在讨论范围);
2、程序飞跑引发的(可能是随机的,也可能是完整的,视程序运行位置及程序的合理性);
3、EEPROM相关寄存器因外部干扰出错所产生的写入动作(随机,但出错时基本上不会出现成片或多处的,一般只会有一处)。
所以楼主的方法还是很有效的,本人也有些产品在用,但还可以再加一点料:
1、在主循环里边置EEPROM地址为0(EEPROM零地址放弃不用);
2、增加程序运行识别码,也即定义一个或多个识别寄存器,根据程序的运行情况给予赋值,在写EEPROM时先判断识别寄存器的值是否正确,再决定是否写入,这样可以在很大程度上避免单个或成片的错误。 记下,学习,谢谢。 呵呵 我用的是24楼的方法1,掉电的时候写,没开bod,使用的是4.7u的一个电容,保存2个字节的数据,关机的时候存储,写完后就直接强制复位-延迟。
还没发现问题,不过我觉得这样处理还不完善,再想想 如果存储CRC数据的位置被修改怎么办。 个人认为,如果楼主是在测试时候发现有eeprom被改写,但分析不出原因,而加上这样的代码。这样做不但是完全错误的,而且是不负责任的。这样做即使改善了出错的概率,但也使错误隐藏得更深,你也许可以瞒过测试人员,但你瞒不过用户。假设你不加这段程序一天出一次问题,加上后1000天才出一次问题,你认为问题就解决了吗?你要是卖出去1000套,一天就会被发现。
如果在测试时没有发现eeprom被改写,而是因为eepom还很空,从而加上这种冗余设计,还是值得鼓励的,但不能在debug之前加,而应该在debug快完成时加,因为在debug之前加上,可能会造成一些本应该被发现的错误被隐藏起来。 只是治标不治本啊
如果你原来eeprom容易出错的话,还是得从硬件和程序上去查啊。 顶一个 回:
【29楼】 dack
【30楼】 maoxia007
其实,目前的这个处理方法只是让你的系统更加健壮而已,我并没有刻意地用这个程序来堵系统的其他漏洞。
说老实话,AVR的EEPROM是挺好用的,不过要注意一些细节问题:
1、写EEPROM的时候最好把全局中断关闭
2、读写EEPROM的时候注意不要让看门狗复位
3、一定要打开BOD 不是通过硬件解决或解决硬件问题,不能叫治本. 我一直在想,可以采用硬盘Raid 5的方法 好贴,留名。 记号 mark 应用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.数据读出时有验证,并存在恢复措施,以使数据错误降到最低。
我现在正在学习AVR单片机,一直应用PIC单片机,以上措施在PIC单片机使用有4年了,很少出现(机率为千分之一以下)数据错误问题;但仍然不能保证100%无问题,有时,芯片本身缺陷也会造成故障。
相关样例程序只有PIC的,这分论坛暂时不发,有空我会去PIC分论坛进行发布的。 mark 好文,彻底解决AVR EEPROM丢数据 不知道什么叫彻底?是不是完全一点改写的机率都没有,还是只指软件上或者硬件不会改写,或软硬都不可能导致改写。
看了上面的讨论,只觉得想彻底很困难。 mark 不错 不错的办法,不过我感觉如果要彻底解决,还要在电路抗扰性,上下电,防止程序跑飞后误写eeprom上下些功夫。 这帖怎么又顶上来了?
冒昧直言,似乎都没讨论到点子上。不分析出令人信服的数据丢失原因,靠瞎猜瞎碰如何去解决,又何谈治标治本? mark 踩一下 过来看一下,借鉴,取经 好贴,标记一下 学习标记 mark 确实是大家头疼的事情 不是吧,我用ATMEL好几年了从没发现过EEPROM的数据丢失现象。。。。。。好好看看硬件和软件设计是否合理吧? 非常实用,谢谢 mark mark mark mark MARK 参考了,用过EEPROM 实践证明,没有好的办法。 俺是分成了16个区。不同的是用汇编做的。
不做机制的时候,确实丢过数据,有机制后,基本没有问题。
毕竟大片数据丢失的几率太小了。 崩溃,都用到crc了,在实时性要求高的场合,没法这么做吧,太耗时间了。存一个数据恐怕要耽误很多事情。
同一个数据并行写入就可以了,读取的时候判断一下。 丢数据应该主要是上电掉电过程吧。强干扰丢数据还没有遇到过。
掉电上电的速度和状态,有时候是单设备本身不好控制的。硬件设计固然重要,但还是不得不防 mark MARK mark 非常实用,mark . 留名顶帖! 我也是上电掉电丢数据啊,搞死人,我解决的方法也是存三分,每次比较,不过我觉得的还是上电要有个长延时,这段时间千万不能操作e2c, mark 和6楼一样,从未遇到过。软硬兼施吧 留名顶帖 我公司有个老工程师做过一次,用EEPROM来记录地址,结果没过多久就挂了,还不知道怎么回事,而且程序里面怎么更改都没法解决,最后就干脆不用EEPROM。
但是现在我在用,却没有出现之前出现的问题。不知道是怎么回事。 好贴,大家畅所欲言,把自己遇到的问题和解决方法都贴上来,让AVR运行更稳定。本人做的工控板,在有干扰情况下,EEP中的头几个数据也会给改写,通过给电源滤噪声,EEP中开头部分弃用,后来就没出问题过, mark mark 又是一个好方法! mark mark 解决AVR单片机EEPROM数据丢失问题 mark mark 以前做430时经常发生上电时会把EEPROM保存的数据冲掉,后在程序改成EEPROM在每次写过之后就改为只读,就再也没有发生这种情况,也是一种方法. mark mark 慢慢学习中 学习了 好贴值得推广 个人觉得意义不大,我之前也做过相关实验,因为一旦EEPROM出问题,根本就不是因为被错误改写,因为错误的改写必须有正确的时序,也就是说EEPROM出问题的时候一般都是硬件,主要是电压波动导致的,出问题的时候数据被毁好大片,你这种办法可以有所改善,但是不可能是根本的,其实把BOD打开EEPROM很少会出问题,当然了,一旦出问题,是没有任何办法可以解决的,LZ的彻底解决我不知道什么依据...个人感觉比较武断,我只是谈谈个人想法,仅仅是技术交流,LZ别搞我的人啊,小声说一句:偶也是武汉的: 顺便说一句,做 CRC 还不如做 RS 好 留个记号慢慢看,谢谢! 记号 学习 程序中这样做是非常必要的,假如你的工作环境比较恶劣 mark。 mark mark dddd dddd 学习,领教了~
页:
[1]
2