山东电子小菜鸟的个人空间 https://bbs.21ic.com/?1197168 [收藏] [复制] [RSS]

日志

基于单片机存储e2数据丢失的问题的探讨

已有 3295 次阅读2017-3-17 17:17 |个人分类:随笔|系统分类:单片机| 单片机

                  
     最近在用某品牌的单片机做一个数字电源的项目,有些参数需要保存,但是偶尔会出现存储数据丢失的问题,硬件方面的处理不过多阐述,现从程序方面,跟大家探讨一下.
基本思路:每份写到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:地址 
*返回参数:从指定地址读出来的数据 
*相关说明: 
********************************************************************/ 
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:地址 
*返回参数:从指定地址读出来的数据 
*相关说明: 
********************************************************************/ 
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:数据 
*返回参数:无 
*相关说明: 
********************************************************************/ 
void EepromWriteByte(unsigned char *address,unsigned char data) 
eeprom_busy_wait(); 
eeprom_write_byte(address,data); 

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

/******************************************************************* 
*函数名称:EepromWriteBlock() 
*函数功能:将缓冲区中的n个数据写进EEPROM 
*输入参数:address:地址;data:数据 
*返回参数: 
*相关说明: 
********************************************************************/ 
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 
*相关说明: 
********************************************************************/ 
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 
*相关说明: 
********************************************************************/ 
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 
*相关说明: 
********************************************************************/ 
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 
*相关说明: 
********************************************************************/ 
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()函数,对损坏的备份进行修复

路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)