各型号基本说明
型号 容量 器件寻址字节(8位) 一次装载字节数 (页长度)
AT24C01 128×8 1010A2A1A0 R/W 8
AT24C02 256×8 1010A2A1A0 R/W 8
AT24C04 512×8 1010A2A1P0 R/W 16
AT24C08 1024×8 1010A2P1P0 R/W 16
AT24C16 2048×8 1010P2P1P0 R/W 16
AT24C32 4096*8 1010A2A1A0 R/W 32
AT24C64 8192*8 1010A2A1A0 R/W 32
AT24C128 16384*8 1010A2A1A0 R/W 64
AT24C256 16384*8 1010A2A1A0 R/W 64
1、AT24C01~AT24C16:容量分别是128,256,512,1024,2048个字节,页长度分别是8,8,16,16,16,读写都是先发设备地址,然后发一个字节的字节地址。

- 24c01、02 1K/2K EEPROM 在一条IIC总线上可以挂8个,地址由A2,A1,A0确定;
- 24C04 4k EEPROM 只有A2,A1的做地址位,这样一条IIC总线上能挂4个设备,A0是用来确定内部页地址的,A0在芯片上没有线连接的(NA);
- 24C08 8k EEPROM 使用A2来确定地址线,A1,A0位是在确定内部页地址的,一条IIC总线能扩展2片;
- 24C16 16k EEPROM,A2A1A0都是确定内部页地址的,一条总线上只能挂1个一个这样的设备。
2、AT24C32~AT24C64:,容量分别是4096,8192个字节,页长度分别是32,32字节,读写都是先发设备地址,然后发一个字节的高地址,再发一个字节的低地址,设备地址如下所示

3、 AT24C128~AT24C512:容量分别是16384,32768个字节,页长度分别是64,64,128字节,读写都是先发设备地址,然后发一个字节的高地址,再发一个字节的低地址,
A1、A0:输入脚用于多个器件级联时设置器件地址,当这些脚悬空时默认值为0,最多可以级联4个设备,只有一个设备的话,A0,A1悬空或者为0
它们的设备地址如下所示:

有关E2PROM的数据管理(来自网络)
- 操作注意事项:分页操作需要有5ms延迟等待时间才可以(以类AT24C04的产品为例),也就是模块化程序设计中,在写数据之前、写数据完毕后、度数据之前、读数据之后都需要考虑加5ms的延时时间。本来IIC的读写速率就不是很高,外加这些延迟一定会势必影响系统设计的实时性,但也不得不从读写性能的角度出发。
-
上拉电阻的选择:出于稳定性考虑,WP、SDA、SCL引脚都会设置上拉电阻,常用的电阻值为 4.7K 、10K电阻,个人比较推荐4.7K。
-
硬件IIC与软件模拟IIC的比较:对MCU资源不是很敏感的应用,都会考虑软件模拟的方式,毕竟这个移植起来真的很方便,只有第一做软件部分的时序、保护性设计作为足够好,后面拿过来修改时钟就可以直接,确实方便。
-
默认参数的写入:设置新E2PROM的时间戳标志,每次系统启动时检查这个时间戳和MCU自身存储的时间戳是否一致,不一致则初始化整个E2PROM为默认参数;当然软件程序的升级,这个时间戳表示也有必要做更改。
-
数据容错和管理:
把数据以有意义的数据块作分类管理,在数据的块的头、位加固定标识和CS/crc校验 模式,格式如
数据块开始字 数据长度 数据 校验 结束
0xA5 Length Byte0---ByteN CS/CRC 0x5A
实际用于产品中,可以挑选这个格式内容里面的部分内容使用,比如去掉结束符等。
个人之所以建立写入开始字、结束字,原因是方便最好读出来的数据做数据格式检查,确认写入、读出的数据可靠性最高。
为增强实际的可靠性,在需要写入的时候,可以在写入后,再读出来进行数据的比对,确认写入是否正确;或者在需要读出的时候,读两次、或者多次,检查每次的数据是否一致。
对于出现异常的数据,最好有容错机制,可以回到默认状态值,不至于系统此时因为某个参数改变的崩溃。
-
实际底层操作是否需要关闭主程序的中断:一般按照上述(5)操作,有多次冗余操作设计,可以不关闭主程序中断。而且,IIC为等待型操作,一般不会因为系统延迟导致时钟脉宽拉长,影响字节写入、读出。
-
E2PROM擦写次数的延长: 如果现在手上的E2PROM的擦写次数是10万次,项目要求为100万次,且E2PROM内有很多空闲字节的没有使用。
可以这样操作,将数据整理好,以数据块的方式存储,一组数据分10个块地址存储,每次写完后转移到下一块写,即10次写操作中每个物理的数据存储地址只操作了1次。
注意此时的写块数据的指针不能单独存、操作,不然这个字节的操作频率高,也就受到10万次的限制,这个关键的链子在10万次的时候掉了,其他字节也就挂了。这个表征操作哪个块的指针或者说标示符,当然也需要是移动的,至于具体怎么实现,就是见仁见智的事了。
I²C线路需要上拉电阻才能正常通信。这些电阻的值取决于线路的电容和您想要通信的频率,一般使用4.7kΩ上拉电阻。

Arduino简单程序示例
//原程序地址:https://forum.arduino.cc/index.php?topic=62822.msg751697#msg751697 /* * Use the I2C bus with small EEPROMs * 24C01, 20C02, 24C04, 24C08, 24C16 * * For a single device, connect as follows: * EEPROM 4 (GND) to GND * EEPROM 8 (Vcc) to Vcc (5 Volts) 24C04实际3.3V的电压也能使用,最低1.8V,最高5.5V * EEPROM 5 (SDA) to ESP32-S Pin 21 * EEPROM 6 (SCL) to ESP32-S Pin 22 * EEPROM 7 (WP) to GND 低电平,可读写 * EEPROM 1 (A0) to GND * EEPROM 2 (A1) to GND * EEPROM 3 (A2) to GND */ #include <Wire.h> // The seven-bit device address for EEPROMs 7 bit 地址 1010000(0x50) 开始 期中1010为厂商地址 // I'll define it here rather than hard-code it inside all of the functions. const byte DEVADDR = 0x50; void setup() { byte msg1[] = "Message 1."; // data to write byte msg2[] = "Zaphod says yo"; byte msg3[] = "Tttthat's all, folks!"; byte msgf[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; Wire.begin(); Serial.begin(9600); // // Change #if 0 to #if 1 and it will erase the // EEPROM pages that we are going to write to: // #if 0 eeprom_write_page(DEVADDR, 0x000, msgf, 16); eeprom_write_page(DEVADDR, 0x010, msgf, 16); eeprom_write_page(DEVADDR, 0x020, msgf, 16); eeprom_write_page(DEVADDR, 0x100, msgf, 16); eeprom_write_page(DEVADDR, 0x1f0, msgf, 16); Serial.println("After erasing pages starting at 0x000, 0x100, and 0x1f0:"); eeprom_dump(DEVADDR, 0, 512); #endif // // Change #if 1 to #if 0 so that it won't write over the stuff next time // #if 1 // Write some stuff to EEPROM eeprom_write_page(DEVADDR, 0x000, msg1, sizeof(msg1)); eeprom_write_page(DEVADDR, 0x100, msg2, sizeof(msg2)); eeprom_write_page(DEVADDR, 0x1f0, msg3, 16); #endif Serial.println("Memory written"); } void loop() { // // Read the first page in EEPROM memory, a byte at a time // Serial.println("eeprom_read_byte, starting at 0"); for (int i = 0; i < 16; i++) { byte b = eeprom_read_byte(DEVADDR, i); Serial.print(b, HEX); Serial.print(' '); } Serial.println(); // // Read the first page using the read_buffer function // Serial.println("eeprom_read_buffer, starting at 0"); byte buffer[16]; eeprom_read_buffer(DEVADDR, 0, buffer, sizeof(buffer)); // //First print the hex bytes on this row // for (int i = 0; i < sizeof(buffer); i++) { char outbuf[6]; sprintf(outbuf, "%02X ",buffer[i]); Serial.print(outbuf); } Serial.println(); // // Now print the char if printable ASCII // otherwise print '.' // for (int i = 0; i < sizeof(buffer); i++) { if (isprint(buffer[i])) { Serial.print(buffer[i]); } else { Serial.print('.'); } } Serial.println(); // Now dump 512 bytes Serial.println("eeprom_dump(DEVADDR, 0, 512)"); eeprom_dump(DEVADDR, 0, 512); Serial.println(); delay(20000); } void eeprom_write_byte(byte deviceaddress, int eeaddress, byte data) { // Three lsb of Device address byte are bits 8-10 of eeaddress byte devaddr = deviceaddress | ((eeaddress >> 8) & 0x07); byte addr = eeaddress; Wire.beginTransmission(devaddr); Wire.send(int(addr)); Wire.send(int(data)); Wire.endTransmission(); delay(10); } // Pages are blocks of 16 bytes, starting at 0x000. // That is, pages start at 0x000, 0x010, 0x020, ... // For a device "page write", the last byte must be // on the same page as the first byte. // // No checking is done in this routine. // // TODO: Do some checking, or, better yet (maybe) // make length an int and do repeated device // page writes if necessary. (Then maybe rename to // eeprom_write_pages or some such thing.) // void eeprom_write_page(byte deviceaddress, unsigned eeaddr, const byte * data, byte length) { // Three lsb of Device address byte are bits 8-10 of eeaddress byte devaddr = deviceaddress | ((eeaddr >> 8) & 0x07); byte addr = eeaddr; Wire.beginTransmission(devaddr); Wire.send(int(addr)); for (int i = 0; i < length; i++) { Wire.send(data[i]); } Wire.endTransmission(); delay(10); } // TODO: Change to integer data type and return -1 if can't // read. // int eeprom_read_byte(byte deviceaddress, unsigned eeaddr) { byte rdata = -1; // Three lsb of Device address byte are bits 8-10 of eeaddress byte devaddr = deviceaddress | ((eeaddr >> 8) & 0x07); byte addr = eeaddr; Wire.beginTransmission(devaddr); Wire.send(int(addr)); Wire.endTransmission(); Wire.requestFrom(int(devaddr), 1); if (Wire.available()) { rdata = Wire.receive(); } return rdata; } // // Returns number of bytes read from device // // Due to buffer size in the Wire library, don't read more than 30 bytes // at a time! No checking is done in this function. // // TODO: Change length to int and make it so that it does repeated // EEPROM reads for length greater than 30. int eeprom_read_buffer(byte deviceaddr, unsigned eeaddr, byte * buffer, byte length) { // Three lsb of Device address byte are bits 8-10 of eeaddress byte devaddr = deviceaddr | ((eeaddr >> 8) & 0x07); byte addr = eeaddr; Wire.beginTransmission(devaddr); Wire.send(int(addr)); Wire.endTransmission(); Wire.requestFrom(devaddr, length); int i; for (i = 0; i < length && Wire.available(); i++) { buffer[i] = Wire.receive(); } return i; } // // The display is like hexdump -C. It will always // begin and end on a 16-byte boundary. // void eeprom_dump(byte devaddr, unsigned addr, unsigned length) { // Start with the beginning of 16-bit page that contains the first byte unsigned startaddr = addr & (~0x0f); // stopaddr is address of next page after the last byte unsigned stopaddr = (addr + length + 0x0f) & (~0x0f); for (unsigned i = startaddr; i < stopaddr; i += 16) { byte buffer[16]; // Hold a page of EEPROM char outbuf[6]; //Room for three hex digits and ':' and ' ' and '\0' sprintf(outbuf, "%03x: ", i); Serial.print(outbuf); eeprom_read_buffer(devaddr, i, buffer, 16); for (int j = 0; j < 16; j++) { if (j == 8) { Serial.print(" "); } sprintf(outbuf, "%02x ", buffer[j]); Serial.print(outbuf); } Serial.print(" |"); for (int j = 0; j < 16; j++) { if (isprint(buffer[j])) { Serial.print(buffer[j]); } else { Serial.print('.'); } } Serial.println("|"); } }
作者:幸运派
链接:https://www.jianshu.com/p/4e7a730e9a97
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。