AT24CXX系列程序

xiaoxiao2021-02-28  71

存储空间分配 AT24C02A,2K 串行 EEPROM:共 32 页,每页 8 字节,寻址字需 8 位。 AT24C04A,4K 串行 EEPROM:共 32 页,每页 16 字节,寻址字需 9 位。 AT24C08A,8K 串行 EEPROM:共 64 页,每页 16 字节,寻址字需 10 位。 AT24C16A,16K 串行 EEPROM:共 128 页,每页 16 字节,寻址字需 11 位。

图为 几类不同容量的芯片的存储空间结构,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 #define DATA1  0x0010 #define DATA2  0x0050 存储数据所在缓存 EE_Buffer[20],应用程序如下写法: EEPROM_Write(EE_Buffer,DATA1,20); 这样EE_Buffer内的数据便被写入EEPROM中 0x10~0x30 的数据存储空间中了。 合理的分配陪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的使用便能得心应手

void I2C_EE_Init() {  /* depending on the EEPROM Address selected in the i2c_ee.h file */ #ifdef EEPROM_Block0_ADDRESS  /* Select the EEPROM Block0 to write on */  EEPROM_ADDRESS = EEPROM_Block0_ADDRESS; #endif #ifdef EEPROM_Block1_ADDRESS  /* Select the EEPROM Block1 to write on */  EEPROM_ADDRESS = EEPROM_Block1_ADDRESS; #endif #ifdef EEPROM_Block2_ADDRESS  /* Select the EEPROM Block2 to write on */  EEPROM_ADDRESS = EEPROM_Block2_ADDRESS; #endif #ifdef EEPROM_Block3_ADDRESS  /* Select the EEPROM Block3 to write on */  EEPROM_ADDRESS = EEPROM_Block3_ADDRESS; #endif } void I2C_ByteWrite(u8* pBuffer, u8 WriteAddr)//дһ¸ö×Ö½Ú {        I2C_GenerateSTART(I2C1, ENABLE); //ÆðʼÐźŠ    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//Çå³ýEV5       I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//·¢ËÍ´ÓÆ÷¼þµØÖ·     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//Çå³ýEV6     I2C_SendData(I2C1, WriteAddr);//дµÄµØÖ·00~FF     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//EV8-1     I2C_SendData(I2C1, *pBuffer); //·¢ËÍÊý¾Ý     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Çå³ýEv8     I2C_GenerateSTOP(I2C1, ENABLE);//²úÉúÍ£Ö¹ÐźŠ} void I2C_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite)//Á¬ÐøÐ´¶à¸ö×Ö½Ú {     while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); // ÅжÏ×ÜÏßÊÇ·ñ·±Ã¦     I2C_GenerateSTART(I2C1, ENABLE);     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//EV5      I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//·¢ËÍ´ÓÆ÷¼þµØÖ·     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//Çå³ýEV6       I2C_SendData(I2C1, WriteAddr);//дµÄµØÖ·00~FF       while(! I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//EV8-1     while(NumByteToWrite--)       {         I2C_SendData(I2C1, *pBuffer);          pBuffer++;          while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Çå³ýEv8     }      I2C_GenerateSTOP(I2C1, ENABLE);//²úÉúÍ£Ö¹ÐźŠ} void I2C_WaitEepromStandbyState(void)//µÈ´ý²Ù×÷Íê³É       {   vu16 SR1_Tmp = 0;   do   {     I2C_GenerateSTART(I2C1, ENABLE);//ÆðʼÐźŠ    SR1_Tmp = I2C_ReadRegister(I2C1, I2C_Register_SR1);//¶ÁÈ¡¼Ä´æÆ÷±ê־λ     I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);   }   while(!(I2C_ReadRegister(I2C1, I2C_Register_SR1) & 0x0002));   I2C_ClearFlag(I2C1, I2C_FLAG_AF);//Çå³ý   I2C_GenerateSTOP(I2C1, ENABLE);  } void I2C_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite)//ºÍ25X16Ò»Ñù {     u8 NumOfPage = 0; u8 NumOfSingle = 0;  u8 Addr = 0;  u8 count = 0;     Addr = WriteAddr %  I2C_PageSize;//¿´¿´²»ÂúÒ»Ò³µÄÓÃÁ˶àÉÙ     count =I2C_PageSize-Addr;//²»ÂúһҳûÓõIJ¿·Ö     NumOfPage =  NumByteToWrite / I2C_PageSize;//Ò»¸ö¶àÉÙÒ³     NumOfSingle = NumByteToWrite % I2C_PageSize;//²»ÂúÒ»Ò³×Ö½ÚÊý     if(Addr == 0)//ҳͷ¿ªÊ¼Ð´     {             if(NumOfPage == 0)//²»ÂúÒ»Ò³         {             I2C_PageWrite(pBuffer, WriteAddr, NumOfSingle);             I2C_WaitEepromStandbyState();         }              else//³¬¹ýÒ»Ò³           {        while(NumOfPage--)//Õûҳд        {  I2C_PageWrite(pBuffer, WriteAddr,I2C_PageSize);        I2C_WaitEepromStandbyState();            WriteAddr +=  I2C_PageSize;            pBuffer += I2C_PageSize;        }        if(NumOfSingle!=0)//²»ÂúÕûÒ³µÄ        {            I2C_PageWrite(pBuffer, WriteAddr, NumOfSingle);            I2C_WaitEepromStandbyState();        }         }     }       else//ÆðʼµØÖ·²»ÊÇҳͷ     {         if(NumOfPage== 0)          {             I2C_PageWrite(pBuffer, WriteAddr, NumOfSingle);             I2C_WaitEepromStandbyState();         }         else         {             NumByteToWrite -= count;             NumOfPage =  NumByteToWrite / I2C_PageSize;             NumOfSingle = NumByteToWrite % I2C_PageSize;             if(count != 0)             {          I2C_PageWrite(pBuffer, WriteAddr, count);        I2C_WaitEepromStandbyState( );        WriteAddr += count;        pBuffer += count;             }              while(NumOfPage--)             {                 I2C_PageWrite(pBuffer, WriteAddr, I2C_PageSize);                 I2C_WaitEepromStandbyState();                 WriteAddr +=  I2C_PageSize;                 pBuffer += I2C_PageSize;               }             if(NumOfSingle != 0)             {                 I2C_PageWrite(pBuffer, WriteAddr, NumOfSingle);                  I2C_WaitEepromStandbyState();             }         }     }   }

void I2C1_GPIO_Config() { GPIO_InitTypeDef  GPIO_InitStructure;  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;     GPIO_Init(GPIOB, &GPIO_InitStructure); } void I2C1_Config() { I2C_InitTypeDef  I2C_InitStructure;      I2C_DeInit(I2C1); I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 =0XA0; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;     I2C_InitStructure.I2C_ClockSpeed = 400000; I2C_Init(I2C1, &I2C_InitStructure);// I2C_Cmd(I2C1, ENABLE);//´ò¿ªI2C1 I2C_AcknowledgeConfig(I2C1, ENABLE);//ÔÊÐíÓ¦´ð }

void I2C_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)//¶ÁÒ»´®Êý {         while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));// ÅжÏ×ÜÏßÊÇ·ñ·±Ã¦     I2C_GenerateSTART(I2C1, ENABLE); //ÆðʼÐźŠ    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//EV5      I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//·¢ËÍ´ÓÆ÷¼þµØÖ·     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//Çå³ýEV6     I2C_Cmd(I2C1, ENABLE);     I2C_SendData(I2C1, ReadAddr);//Òª¶ÁÊý¾ÝµÄµØÖ·       while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//EV8-1     I2C_GenerateSTART(I2C1, ENABLE);//ÆðʼÐźŠ    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//Çå³ýEV5     I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Receiver);//¸æË߯÷¼þ  ÎÒÒª¶ÁÊý¾Ý     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));//EV6     while(NumByteToRead)       {         if(NumByteToRead == 1)//Ê£×îºóÒ»¸öÊý¾Ýʱ         {             I2C_AcknowledgeConfig(I2C1, DISABLE);//¹Ø±ÕÓ¦´ð             I2C_GenerateSTOP(I2C1, ENABLE);// ʹÄÜÍ£Ö¹         }         if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))//EV7  ÓнÓÊÕʱ           {                   *pBuffer = I2C_ReceiveData(I2C1);//µôÓÿ⺯Êý              pBuffer++; //              NumByteToRead--;//                 }         }     I2C_AcknowledgeConfig(I2C1, ENABLE);//´ò¿ªÓ¦´ð   ΪÏÂÒ»´Î×¼±¸ }

1. 注意24C16的管脚7是写保护,需要把它拉低

2.如果没有级联。最好A2 A1 A0 最好都接地

2.  24C16只能挂1个

3.  24C16的A2,A1,A0都是扇区地址

     I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);

     EEPROM_ADDRESS就是八个扇区代表 0XA0 0XA2 0XA4 0XA6 0XA8 0XAA 0XAC 0XAE

     Send7bit意思是前7位自己决定最后一位方向为由I2C_Direction_Transmitter决定,看源码就知道了

     I2C_SendData(I2C1, WriteAddr1);决定每个扇区内256个字节的具体地址

4   24C16有8个扇区(block) 128个页   2048字节  16k位

     1扇区有16页       1页有16字节

4.  24C16的IIC函数第二个地址是从0开始,每256byte进行加一次地址,也就是说每次读写最大就是256byte,超过了的,要重新写地址,第二次地址= 0+256开始,继续最大写256byte  ,第三次地址是0+256+256这样循环下去写   好像24C16有自动地址溢出的功能

转载请注明原文地址: https://www.6miu.com/read-49907.html

最新回复(0)