治标治本,彻底解决AVR单片机EEPROM数据丢失问题

编译环境:WinAVR-20060421 + AVR Studio 4.12.498  Service 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  
*相关说明: 
********************************************************************/ 
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 
*相关说明: 
********************************************************************/ 
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 
*相关说明: 
********************************************************************/ 
void EepromWriteByte(unsigned char *address,unsigned char data) 

eeprom_busy_wait(); 
eeprom_write_byte(address,data); 


/******************************************************************* 
*函数名称:EepromWriteWord() 
*函数功能:写一个Word的数据进EEPROM 
*输入参数:address:地址;data:数据 
*返回参数: 
*编写作者:my_avr 
*相关说明: 
********************************************************************/ 
void EepromWriteWord(unsigned int *address,unsigned int data) 

eeprom_busy_wait(); 
eeprom_write_word(address,data); 


/******************************************************************* 
*函数名称:EepromWriteBlock() 
*函数功能:将缓冲区中的n个数据写进EEPROM 
*输入参数:address:地址;data:数据 
*返回参数: 
*编写作者:my_avr 
*相关说明: 
********************************************************************/ 
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  
*相关说明: 
********************************************************************/ 
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 ^ ((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 
*相关说明: 
********************************************************************/ 
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 ^ ((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  
*相关说明: 
********************************************************************/ 
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 
*相关说明: 
********************************************************************/ 
uint8_t DataRecover(void) 

unsigned char i; 
unsigned char temp; 
unsigned char page; 
unsigned int  invalidpage[3]; 
unsigned int  validpage; 

invalidpage[0] = 0; 
invalidpage[1] = 0; 
invalidpage[2] = 0; 
validpage      = 0; 
temp           = 0; 
page           = 0; 

if (EepromCheck((uint8_t *)EepromPage1Add,EepromPageSize) == VALID) 

validpage = EepromPage1Add; 

else 

invalidpage[page] = EepromPage1Add; 
page ++; 


if (EepromCheck((uint8_t *)EepromPage2Add,EepromPageSize) == VALID) 

validpage = EepromPage2Add; 

else 

invalidpage[page] = EepromPage2Add; 
page ++; 


if (EepromCheck((uint8_t *)EepromPage3Add,EepromPageSize) == VALID) 

validpage = EepromPage3Add; 

else 

invalidpage[page] = 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[page] + i),temp); 



if (CheckAllPage() == VALID) 

return VALID; 


return INVALID; 


使用方法(三个备份): 

1、定义一个数组:EEPROMData[EepromPageSize-2] ,数组定义为EepromPageSize-2是为了给每个备份留2个字节的校验 

2、要保存数据时,先把数据放到数组中,然后调用EepromWriteBlock()函数,把这个数组的数据写进EEPROM,三个备份要写三次。 

3、写完了之后,调用CheckWriteCRC()函数,该函数会计算出当前备份的CRC16检验数据并写到EEPROM备份的尾部,有多少个备份就要调用多少次。 

4、至此,数据的备份工作已经完成。

5、校验数据(一般在复位后运行),执行CheckAllPage()函数,若通过了,则EEPROM数据没有问题,否则要运行DataRecover()函数,对损坏的备份进行修复 

------------------修改原因:修改变量的定义形式

永不止步步 发表于11-12 11:55 浏览65535次
分享到:

已有0条评论

暂时还没有回复哟,快来抢沙发吧

添加一条新评论

只有登录用户才能评论,请先登录注册哦!

话题作者

永不止步步
金币:67410个|学分:345327个
立即注册
畅学电子网,带你进入电子开发学习世界
专业电子工程技术学习交流社区,加入畅学一起充电加油吧!

x

畅学电子网订阅号