关于I2C 学习的时候介绍得最多的就是24C02 这里存储EEPROM了,但学的时候基本只是讲讲简单的I2C 的总线数据传输而已,即使先gooogle上搜索也绝大部分这这样的文章,很少有说到如何在实际情况中如何使用的程序。
24Cxx系列数据块存储时也是比较讲究的,
图为 几类不同容量的芯片的存储空间结构,24C16以下空间的大于8位后的寻址高位地址在片选地址中选择,详细看芯片手册。另外要注意的就是字节页,一次连续写入的数据量不能超过一页的数据量。有些老款的芯片甚至不支持跨页写入。为了适用也参照不跨页写入的方法写这个程序。而读取数据没有这个限制,只要单片机开辟的缓存够大,可以一直连续读下去。
/*****24Cxx Seriel EEPROM*************************/ #define EEPROM 8 /******** 01 -> 24C01; 02 -> 24C02; 04 -> 24C04; 08 -> 24C08; 16 -> 24C16; 32 -> 24C32; 64 -> 24C64; 128 -> 24C128; 256-> 24C256; 512 -> 24C512; *****/ #if EEPROM==1 #define PAGE_SIZE 8 #define EE_SIZE 0x007F #elif EEPROM==2 #define PAGE_SIZE 16 #define EE_SIZE 0x00FF #elif EEPROM==4 #define PAGE_SIZE 16 #define EE_SIZE 0x01FF #elif EEPROM==8 #define PAGE_SIZE 16 #define EE_SIZE 0x03FF #elif EEPROM==16 #define PAGE_SIZE 16 #define EE_SIZE 0x07FF #elif EEPROM==32 #define PAGE_SIZE 32 #define EE_SIZE 0x0FFF #elif EEPROM==64 #define PAGE_SIZE 32 #define EE_SIZE 0x1FFF #elif EEPROM==128 #define PAGE_SIZE 64 #define EE_SIZE 0x3FFF #elif EEPROM==256 #define PAGE_SIZE 64 #define EE_SIZE 0x7FFF #elif EEPROM==512 #define PAGE_SIZE 128 #define EE_SIZE 0xFFFF #endif
头文件可以写成预编译模式,方便移植后修改,PAGE_SIZE为一页的存储量,EE_SIZE为芯片的存储量,而后一些程序的判断也根据选择的存储芯片来判断。
顶层用于外部程序调用的函数只有两个,读和写两个情况而已
顶层用于外部程序调用的函数只有两个,读和写两个情况而已
unsigned char EEPROM_Write(unsigned char* pBuffer,unsigned int WriteAddress,unsigned char NumbyteToWrite); unsigned char EEPROM_Read(unsigned char* pBuffer,unsigned int ReadAddress,unsigned char NumbyteToWrite);
传递函数有三个:
pBuffer:要存储或读出来的数据所在的变量存储区字符串头指针;
WriteAddress/ReadAddress:要存入EEPROM所在的存储空间的第一个存储空间地址;
NumbyteToWrite:数据写入或读出的字节个数;
这样的应用比较简单 例如在头文件中分配了两类数据的存储空间起始地址DATA1、DATA2
存储数据所在缓存 EE_Buffer[20],应用程序如下写法:
这样EE_Buffer内的数据便被写入EEPROM中 0x10~0x30 的数据存储空间中了。
合理的分配陪EEPROM 的存储空间对数据管理非常重要。甚至于可以作为一个小型黑匣子一样。
pBuffer:要存储或读出来的数据所在的变量存储区字符串头指针;
WriteAddress/ReadAddress:要存入EEPROM所在的存储空间的第一个存储空间地址;
NumbyteToWrite:数据写入或读出的字节个数;
这样的应用比较简单 例如在头文件中分配了两类数据的存储空间起始地址DATA1、DATA2
#define DATA1 0x0010 #define DATA2 0x0050
EEPROM_Write(EE_Buffer,DATA1,20);
合理的分配陪EEPROM 的存储空间对数据管理非常重要。甚至于可以作为一个小型黑匣子一样。
unsigned char EEPROM_Write(unsigned char* pBuffer,unsigned int WriteAddress,unsigned char NumbyteToWrite) { unsigned char TempBuffer,Temp2Buffer; if((WriteAddress+NumbyteToWrite) > EE_SIZE) //判断是否超出存储空间 { return 0; } else {// 连续写入两次避免因EMC等因素造成的写入失败情况 IIC_WriteBuffer(pBuffer,WriteAddress,NumbyteToWrite); IIC_WriteBuffer(pBuffer,WriteAddress,NumbyteToWrite); //读取eeprom 的数据与缓存中的数据对比 相同为确认写入成功 IIC_ReadBuffer(&TempBuffer,WriteAddress+NumbyteToWrite-1,1); Temp2Buffer=*(pBuffer+NumbyteToWrite-1); if(TempBuffer==Temp2Buffer) return 1; else return 0; } } unsigned char EEPROM_Read(unsigned char* pBuffer,unsigned int ReadAddress,unsigned char NumbyteToWrite) { if((ReadAddress+NumbyteToWrite) > EE_SIZE) { return 0; } else { IIC_ReadBuffer(pBuffer,ReadAddress,NumbyteToWrite); IIC_ReadBuffer(pBuffer,ReadAddress,NumbyteToWrite); return 1; } }
接下来的是是IIC_ReadBuffer、IIC_WriteBuffer,两个函数主要是对存入数据的处理,如页面内写入,超过页面数量的数据处理等,我这里把函数定义为static 函数只能对内部应用,外部只能调用上面的两个函数,累似于API函数一样,这也可以避免不必要的程序调用书写。IIC_ReadBuffer函数相对简单,因为读取没有对页面的限制,可以无限制的读下去。读取缓存的函数只对地址做一下判断即可。写入函数较为复杂,需判断数据起始存储地址 和页等关系
static void IIC_ReadBuffer(unsigned char* pBuffer,unsigned int ReadAddress,unsigned char NumbyteToWrite); static void IIC_WriteBuffer(unsigned char* pBuffer,unsigned int WriteAddress,unsigned char NumByteToWrite); void IIC_ReadBuffer(unsigned char* pBuffer,unsigned int ReadAddress,unsigned char NumbyteToWrite) { u8 PageAddress=0; /******pageAddress is over 8bit***********/ /******判断存储地址是否超过8位************/ #if EEPROM < 32 PageAddress=(u8)(ReadAddress>>7)&0x0E|ReadAddress_EEPROM; #else PageAddress=WriteAddress_EEPROM; #endif IIC_ReadPage(pBuffer,PageAddress,ReadAddress,NumbyteToWrite); } void IIC_WriteBuffer(unsigned char* pBuffer,unsigned int WriteAddress,unsigned char NumByteToWrite) { u8 NumOfPage=0,NumOfSingle=0; // u16 Part=0;// u8 PageAddress=0; /******pageAddress is over 8bit***********/ /******判断存储地址是否超过8位************/ #if EEPROM < 32 PageAddress=(u8)(WriteAddress>>7)&0x0E|WriteAddress_EEPROM; #else PageAddress=WriteAddress_EEPROM; #endif /*********判断起始地址与跨页地址的字节个数********/ Part=WriteAddress/PAGE_SIZE; if(Part!=0) { Part=PAGE_SIZE*(Part+1)-WriteAddress; } else { Part=PAGE_SIZE-WriteAddress; } /******/ if(Part >= NumByteToWrite) { /***写入的数据个数小于跨页剩余的个数可直接写入 ***/ IIC_WritePage(pBuffer,PageAddress,WriteAddress,NumByteToWrite); } else { NumOfPage = (NumByteToWrite-Part)/PAGE_SIZE; NumOfSingle = (NumByteToWrite-Part)%PAGE_SIZE; pBuffer = IIC_WritePage(pBuffer,PageAddress,WriteAddress,Part); /***1.写入的数据个数大于跨页剩余的个数先把剩余的跨页个数填充满 ***/ NumByteToWrite -= Part; WriteAddress += Part; while(NumOfPage--) { pBuffer = IIC_WritePage(pBuffer,PageAddress,WriteAddress,PAGE_SIZE); /***2.按计算的数据量占页面数,连续写入页面*******/ WriteAddress += PAGE_SIZE; } if(NumOfSingle!=0) { IIC_WritePage(pBuffer,PageAddress,WriteAddress,NumOfSingle); /***3.补充页面写完后超出的不足一页数据量的数据***/ } } }
剩下的是IIC_WritePage()、IIC_ReadPage()两个函数是芯片的IIC通讯底层函数,根据单片机的IIC通讯模式写入即可。以上的程序可通用到任何24Cxx 系列所应用的程序。
在实际应用中要注意的便是存储空间的分配,已经空间长度的输入,这样EEPROM的使用便能得心应手。
参考来源:虚V界的个人空间的博客->24Cxx 系列EEPROM通用程序及应用,http://home.eeworld.com.cn/my/space-uid-93649-blogid-77560.html