第4章 EEPROM器件选型:AT24C系列深度解析

做I2C驱动开发,选对器件是第一步。AT24C系列,说白了就是EEPROM界的“常青树”。我最早接触它是在一个工业控制项目里,当时需要存储设备校准参数,翻来覆去还是选了它——不是因为最先进,而是因为最可靠。

这一章,我们就把AT24C系列掰开揉碎了讲。从型号命名到地址配置,从页写和字节写的差异到实际选型建议,都是我这些年踩坑换来的经验。

4.1 AT24C系列型号与容量

AT24C系列是Microchip(原Atmel)的经典产品线。型号命名其实有规律可循:

型号 容量(Kbit) 容量(字节) 页大小(字节) 地址位宽
AT24C01 1K 128 8 7位
AT24C02 2K 256 8 7位
AT24C04 4K 512 16 7位
AT24C08 8K 1024 16 7位
AT24C16 16K 2048 16 7位
AT24C32 32K 4096 32 7位
AT24C64 64K 8192 32 7位
AT24C128 128K 16384 64 7位
AT24C256 256K 32768 64 7位
AT24C512 512K 65536 128 7位

注意看,型号数字除以8就是字节数。比如AT24C02,2Kbit = 256字节。这个换算关系,我建议你记在心里——调试时经常要用到。

个人习惯:我一般优先选AT24C02或AT24C04做原型验证。容量适中,页大小8字节,调试起来不费劲。等方案定型了再根据实际需求换大容量型号。

4.2 地址引脚配置——I2C从机地址怎么来的?

AT24C系列的I2C从机地址,由两部分组成:

  • 固定部分:高4位,固定为 1010
  • 可配置部分:由A2、A1、A0引脚电平决定
  • 读写位:最低位,0表示写,1表示读

举个例子,AT24C02的7位从机地址格式:

Bit 6  Bit 5  Bit 4  Bit 3  Bit 2  Bit 1  Bit 0
  1      0      1      0      A2     A1     A0

如果A2、A1、A0都接地(低电平),那么7位地址就是 1010000,即0x50。写操作时发送0xA0,读操作时发送0xA1。

这里有个关键点:不同容量的器件,可用的地址引脚数量不一样。

型号 可用地址引脚 最大级联数量 说明
AT24C01/02 A2, A1, A0 8个 3个引脚全可用
AT24C04 A2, A1 4个 A0内部用于页选择
AT24C08 A2 2个 A1、A0内部用于页选择
AT24C16 无 1个 所有地址引脚内部用于页选择
我曾经踩过的坑:有一次设计板子,用了AT24C16,想当然地把A0、A1、A2都拉到了不同电平,以为能挂8个。结果发现它们全都被内部用于页地址选择了,根本没法级联。最后只能改板子,换成AT24C02才解决问题。

为什么会这样?因为容量越大,需要的内部地址位越多。AT24C16有2K字节,需要11位地址。I2C协议里器件地址只给了3位可配置空间,不够用怎么办?就把A0、A1、A2“征用”为内部页地址了。

4.3 页写与字节写模式差异

这是驱动开发中最容易出问题的地方。我见过不少新手,写一个字节没问题,一写多个字节就翻车——十有八九是没搞懂页写和字节写的区别。

4.3.1 字节写模式

字节写,一次只写一个字节。时序流程:

  1. 主机发送起始信号
  2. 主机发送器件地址(写方向)
  3. EEPROM应答
  4. 主机发送存储地址(高字节)
  5. EEPROM应答
  6. 主机发送存储地址(低字节)
  7. EEPROM应答
  8. 主机发送数据字节
  9. EEPROM应答
  10. 主机发送停止信号

写完后,EEPROM会进入内部编程周期,大约5ms。这期间不会响应任何I2C命令。

4.3.2 页写模式

页写,一次可以写多个字节,但不能跨页。什么意思?

比如AT24C02,页大小8字节。如果你从地址0x00开始写,可以连续写8个字节(0x00~0x07)。但如果从地址0x05开始写,写第4个字节时就会“卷回”到页首——0x05、0x06、0x07、0x00、0x01……

这个特性,我称之为“页内回绕”。很多驱动bug都源于此。

// 页写示例:向AT24C02写入多个字节
// 注意:不能跨页,需要手动处理页边界

uint8_t i2c_eeprom_page_write(uint8_t dev_addr, uint16_t mem_addr, 
                               uint8_t *data, uint16_t len)
{
    // 检查是否跨页
    uint16_t page_size = 8;  // AT24C02页大小
    uint16_t page_start = (mem_addr / page_size) * page_size;
    uint16_t page_end = page_start + page_size - 1;
    uint16_t write_len = (mem_addr + len - 1 > page_end) ? 
                         (page_end - mem_addr + 1) : len;
    
    // 构造I2C消息
    uint8_t buffer[write_len + 2];
    buffer[0] = (mem_addr >> 8) & 0xFF;  // 地址高字节
    buffer[1] = mem_addr & 0xFF;          // 地址低字节
    memcpy(&buffer[2], data, write_len);
    
    // 发送
    i2c_master_write(dev_addr, buffer, write_len + 2);
    
    // 等待内部编程完成
    i2c_eeprom_wait_ready(dev_addr);
    
    return write_len;  // 返回实际写入的字节数
}
核心差异对比:
特性 字节写 页写
每次写入字节数 1字节 1~N字节(N=页大小)
跨页支持 支持(逐字节写) 不支持(页内回绕)
编程时间 每次5ms 每次5ms(整页)
总线占用 高(频繁启停) 低(批量传输)
适用场景 零星数据更新 批量数据写入

4.4 选型建议与避坑指南

根据我多年的项目经验,给你几条实在的建议:

  • 小数据量(<256字节):AT24C02就够了,8字节页大小,地址引脚全可用,级联方便
  • 中等数据量(256~2K字节):AT24C16,注意它不能级联
  • 大数据量(>2K字节):AT24C256或AT24C512,页大小64/128字节,写入效率高
  • 需要频繁写入:注意EEPROM的擦写寿命,一般是100万次。AT24C系列在这方面表现不错
我曾经踩过的另一个坑:在一个产品里用了AT24C256,驱动写好了,测试也没问题。结果量产时发现有些板子写入数据会丢失。查了两天才发现——AT24C256的页大小是64字节,但我的驱动里写死了32字节。换了批次后,EEPROM的页边界处理有细微差异,导致跨页写入时数据被覆盖。从那以后,我写驱动都会从器件手册里动态读取页大小参数。

4.5 知识体系结构图

下面这张图,把AT24C系列的核心知识点串起来了:

AT24C系列知识体系结构图 AT24C系列EEPROM知识体系 AT24C系列 型号与容量 地址引脚配置 读写模式 AT24C01~512 容量换算 固定部分1010 A2/A1/A0配置 字节写模式 页写模式 Kbit ÷ 8 = 字节数 页大小随容量递增 大容量器件地址引脚减少 AT24C16无法级联 页写不能跨页 编程时间约5ms 选型核心:容量需求 + 地址配置 + 写入效率 图4-1 AT24C系列知识体系结构图 💡 选型时先看容量需求,再看是否需要级联,最后考虑写入效率