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左右。你想想看,如果只改一个参数,用字节写最合适。
操作流程是这样的:
- 主机发送起始信号
- 发送器件地址(写操作)
- 等待从机应答
- 发送要写入的存储地址
- 等待从机应答
- 发送要写入的数据
- 等待从机应答
- 主机发送停止信号
代码实现起来也很简单。我习惯用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个字节,效率比字节写高得多。适合批量存储数据,比如一次存一组传感器数据或者一段配置信息。
操作流程和字节写类似,只是数据部分变成了连续发送多个字节:
- 主机发送起始信号
- 发送器件地址(写操作)
- 等待从机应答
- 发送起始地址
- 等待从机应答
- 连续发送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的核心内容就这些。下一章我们会实际动手,用代码把读写操作跑起来。到时候你会发现,理论懂了,写代码其实很快。