3、AT24C02芯片介绍:引脚功能、内部存储结构、页写与字节写模式

好,咱们今天来聊聊AT24C02这颗芯片。说实话,在嵌入式系统里,EEPROM用得最多的就是它了。我最早接触它是在做一个智能手环的项目,当时需要存储用户的运动数据,掉电不能丢,容量又不用太大。嗯,AT24C02正好合适。

3.1 引脚功能:别看脚少,个个有用

AT24C02是8个引脚的封装,常见的是DIP-8或者SOP-8。你想想看,8个脚能干啥?其实功能划分得很清楚。

引脚编号 名称 功能描述
1 A0 地址输入引脚0,用于设置器件地址
2 A1 地址输入引脚1,用于设置器件地址
3 A2 地址输入引脚2,用于设置器件地址
4 GND 电源地
5 SDA 串行数据线,双向传输
6 SCL 串行时钟线,由主机控制
7 WP 写保护引脚,高电平时禁止写入
8 VCC 电源正极,典型值5V或3.3V

这里我要特别说一下A0、A1、A2这三个引脚。它们决定了芯片的I2C地址。AT24C02的固定地址部分是1010,后面跟着A2、A1、A0的电平状态,最后一位是读写位。所以同一根I2C总线上最多可以挂8片AT24C02。我在项目中遇到过需要扩展存储容量的情况,直接并联了4片,地址分别设置成不同的组合,用起来很方便。

器件地址计算: 如果A2、A1、A0都接地,那么器件地址就是0xA0(写操作)或0xA1(读操作)。

WP引脚是个好东西。我曾经在产品调试时,不小心写了个死循环,一直往EEPROM里写数据。还好当时WP引脚接了高电平,数据没被破坏。不然的话,之前存的关键参数就全没了。

3.2 内部存储结构:2Kbit到底能存啥?

AT24C02的容量是2Kbit,也就是256字节。你可能会觉得,256字节能干啥?其实对于很多小数据量的应用来说,完全够用了。比如存储设备配置参数、用户设置、校准数据等等。

它的内部结构是这样的:

  • 总容量: 2Kbit = 256字节
  • 页大小: 8字节/页,共32页
  • 地址范围: 0x00 ~ 0xFF

说白了,这256个字节被分成了32个页,每页8个字节。为什么要分页?因为写操作是以页为单位的。你一次最多可以连续写8个字节,这就是页写模式。如果超过8个字节,数据就会回卷到当前页的开头,覆盖之前的数据。嗯,这里要注意,很多人第一次用的时候会踩这个坑。

注意: 页写时如果跨页,数据会回卷到当前页的起始地址,而不是自动进入下一页。我曾经有个同事,想一次写16个字节,结果发现前8个字节被后8个字节覆盖了,查了半天才找到原因。

3.3 字节写模式:一个一个来,稳得很

字节写模式是最基本的操作。每次只写一个字节,写完需要等待芯片内部完成编程。这个等待时间大概是5ms左右。你想想看,如果只改一个参数,用字节写最合适。

操作流程是这样的:

  1. 主机发送起始信号
  2. 发送器件地址(写操作)
  3. 等待从机应答
  4. 发送要写入的存储地址
  5. 等待从机应答
  6. 发送要写入的数据
  7. 等待从机应答
  8. 主机发送停止信号

代码实现起来也很简单。我习惯用C语言写底层驱动,下面是一个典型的字节写函数:

// AT24C02 字节写函数
// addr: 目标地址 (0x00 ~ 0xFF)
// data: 要写入的数据
void AT24C02_WriteByte(uint8_t addr, uint8_t data)
{
    I2C_Start();                    // 起始信号
    I2C_SendByte(0xA0);             // 器件地址 + 写
    I2C_WaitAck();                  // 等待应答
    I2C_SendByte(addr);             // 存储地址
    I2C_WaitAck();                  // 等待应答
    I2C_SendByte(data);             // 写入数据
    I2C_WaitAck();                  // 等待应答
    I2C_Stop();                     // 停止信号
    delay_ms(5);                    // 等待内部编程完成
}

这里有个小细节:写完数据后一定要加5ms的延时。为什么?因为芯片内部需要时间把数据写入存储单元。如果你紧接着就发下一个命令,芯片可能还没准备好,会导致操作失败。我刚开始做的时候也犯过这个错,后来老老实实加了延时,再也没出过问题。

3.4 页写模式:批量操作,效率翻倍

页写模式就厉害了。一次可以写8个字节,效率比字节写高得多。适合批量存储数据,比如一次存一组传感器数据或者一段配置信息。

操作流程和字节写类似,只是数据部分变成了连续发送多个字节:

  1. 主机发送起始信号
  2. 发送器件地址(写操作)
  3. 等待从机应答
  4. 发送起始地址
  5. 等待从机应答
  6. 连续发送8个字节的数据
  7. 每次发送后等待从机应答
  8. 主机发送停止信号

小技巧: 页写时,起始地址最好选在页的起始位置,比如0x00、0x08、0x10等。这样不容易搞混。如果从中间地址开始写,写到页尾时会回卷,容易出问题。

页写的代码实现:

// AT24C02 页写函数
// startAddr: 起始地址 (必须是页对齐的)
// pData: 数据缓冲区指针
// len: 数据长度 (不超过8)
void AT24C02_WritePage(uint8_t startAddr, uint8_t *pData, uint8_t len)
{
    uint8_t i;
    
    I2C_Start();                    // 起始信号
    I2C_SendByte(0xA0);             // 器件地址 + 写
    I2C_WaitAck();                  // 等待应答
    I2C_SendByte(startAddr);        // 起始地址
    I2C_WaitAck();                  // 等待应答
    
    for(i = 0; i < len; i++)
    {
        I2C_SendByte(pData[i]);     // 连续发送数据
        I2C_WaitAck();              // 等待应答
    }
    
    I2C_Stop();                     // 停止信号
    delay_ms(5);                    // 等待内部编程完成
}

你可能会问,页写和字节写哪个更好?我的建议是:

  • 单个数据修改: 用字节写,简单直接
  • 批量数据存储: 用页写,效率高
  • 不确定场景: 用字节写,稳字当头

我记得有一次做产品升级,需要一次性写入128字节的配置数据。如果用字节写,要写128次,每次5ms,总共640ms。换成页写,16次就搞定了,只要80ms。这个差距在实际产品中是很明显的。

3.5 字节写 vs 页写:怎么选?

说白了,选择哪种模式取决于你的应用场景。我整理了一个对比表,方便你参考:

对比项 字节写模式 页写模式
单次写入量 1字节 最多8字节
写入时间 约5ms/次 约5ms/次
适用场景 单个参数修改 批量数据存储
地址限制 无限制 建议页对齐
代码复杂度 简单 稍复杂

嗯,这里要提醒一下:不管是字节写还是页写,写完数据后都要检查一下是否真的写进去了。我习惯的做法是写完后再读出来对比一下。虽然AT24C02的可靠性很高,但硬件电路偶尔也会出问题,多一层检查总是好的。

核心要点: AT24C02的256字节存储空间,32页,每页8字节。字节写适合单点修改,页写适合批量操作。无论哪种模式,写完都要等5ms。记住这些,你的EEPROM操作就不会出大问题。

好了,AT24C02的核心内容就这些。下一章我们会实际动手,用代码把读写操作跑起来。到时候你会发现,理论懂了,写代码其实很快。