AT24C02介绍
开发板上使用的是 AT24C02(EEPROM) 芯片,此芯片具有 I2C 通信接口,芯片内保存的数据在掉电情况下都不丢失, 所以通常用于存放一些比较重要的数据等。AT24C02 芯片管脚及外观图如下图所示:


AT24C02 器件地址为 7 位,高 4 位固定为 1010,低 3 位由 A0/A1/A2 信 号线的电平决定。 因为传输地址或数据是以字节为单位传送的,当传送地址时,器件地址占 7 位,还有最后一位(最低位 R/W)用来选择读写方向,它与地址 无关。其格式如下:

开发板已经将芯片的 A0/A1/A2 连接到 GND,所以器件地址为 1010000,即 0x50(未计算最低位)。
如果要对芯片进行写操作时,R/W 即为 0,写器件地址为 0XA0。
如果要对芯片进行读操作时,R/W 即为 1,读器件地址为 0XA1。
开发板上也将 WP 引脚直接接在 GND 上,此时芯片允许数据正常读写。
硬件电路

从图中可以看出,芯片的 SCL 和 SDA 管脚是连接在单片机的 P2.1 和 P2.0 上,为了让 IIC 总线默认为高电平,通常会在 IIC 总线上接上拉电阻。
在图中并没有看到 SCL 和 SDA 管脚有上拉电阻,这是因为开发板单片机 IO 都外接了 10K 上拉电阻,当单片机 IO 口连接到芯片的 SCL 和 SDA 脚时即相当于它们外接上拉电阻,所以此处可以省去。
示例代码
I2C.h
#ifndef _I2C_H_
#define _I2C_H_
#include <regx52.h>
// 定义I2C引脚连接 - 根据实际硬件调整
sbit I2C_SCL = P2^1; // I2C时钟线
sbit I2C_SDA = P2^0; // I2C数据线
// 函数声明
void I2C_Start(void);
void I2C_Stop(void);
void I2C_SendByte(unsigned char Byte);
unsigned char I2C_ReceiveByte(void);
void I2C_SendAck(unsigned char AckBit);
unsigned char I2C_ReceiveAck(void);
#endif
I2C.c
#include "I2C.h"
/**
* @brief 产生I2C起始信号
*/
void I2C_Start(void) {
I2C_SDA = 1;
I2C_SCL = 1;
I2C_SDA = 0;
I2C_SCL = 0;
}
/**
* @brief 产生I2C停止信号
*/
void I2C_Stop(void) {
I2C_SDA = 0;
I2C_SCL = 1;
I2C_SDA = 1;
}
/**
* @brief 向I2C总线发送一个字节
* @param Byte: 要发送的字节
*/
void I2C_SendByte(unsigned char Byte) {
unsigned char i;
for (i = 0; i < 8; i++) {
I2C_SDA = Byte & (0x80 >> i);
I2C_SCL = 1;
I2C_SCL = 0;
}
}
/**
* @brief 从I2C总线接收一个字节
* @retval 接收到的字节
*/
unsigned char I2C_ReceiveByte(void) {
unsigned char i, Byte = 0x00;
I2C_SDA = 1; // 释放数据线,设置为输入
for (i = 0; i < 8; i++) {
I2C_SCL = 1;
if (I2C_SDA) { Byte |= (0x80 >> i); }
I2C_SCL = 0;
}
return Byte;
}
/**
* @brief 发送应答信号
* @param AckBit: 0-应答,1-非应答
*/
void I2C_SendAck(unsigned char AckBit) {
I2C_SDA = AckBit;
I2C_SCL = 1;
I2C_SCL = 0;
}
/**
* @brief 接收应答信号
* @retval 0-应答,1-非应答
*/
unsigned char I2C_ReceiveAck(void) {
unsigned char AckBit;
I2C_SDA = 1; // 释放数据线,设置为输入
I2C_SCL = 1;
AckBit = I2C_SDA;
I2C_SCL = 0;
return AckBit;
}
AT24C02.h
#ifndef _AT24C02_H_
#define _AT24C02_H_
// AT24C02设备地址[citation:3]
#define AT24C02_ADDRESS 0xA0
// 函数声明
void AT24C02_WriteByte(unsigned char WordAddress, unsigned char Data);
unsigned char AT24C02_ReadByte(unsigned char WordAddress);
void AT24C02_DelayMs(unsigned int ms);
#endif
AT24C02.c
#include "AT24C02.h"
#include "I2C.h"
/**
* @brief 向AT24C02指定地址写入一个字节[citation:3]
* @param WordAddress: 写入目标地址 (0x00~0xFF)
* @param Data: 要写入的数据
*/
void AT24C02_WriteByte(unsigned char WordAddress, unsigned char Data) {
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS); // 发送设备地址+写命令
I2C_ReceiveAck();
I2C_SendByte(WordAddress); // 发送要写入的存储单元地址
I2C_ReceiveAck();
I2C_SendByte(Data); // 发送要写入的数据
I2C_ReceiveAck();
I2C_Stop();
// 写入操作后,AT24C02需要时间完成内部写周期[citation:6]
AT24C02_DelayMs(10); // 延时10ms等待写入完成
}
/**
* @brief 从AT24C02指定地址读取一个字节[citation:3]
* @param WordAddress: 读取源地址 (0x00~0xFF)
* @retval 读取到的数据
*/
unsigned char AT24C02_ReadByte(unsigned char WordAddress) {
unsigned char Data;
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS); // 发送设备地址+写命令
I2C_ReceiveAck();
I2C_SendByte(WordAddress); // 发送要读取的存储单元地址
I2C_ReceiveAck();
I2C_Start(); // 发送重复起始信号
I2C_SendByte(AT24C02_ADDRESS | 0x01); // 发送设备地址+读命令
I2C_ReceiveAck();
Data = I2C_ReceiveByte(); // 读取数据
I2C_SendAck(1); // 发送非应答信号,表示读取结束
I2C_Stop();
return Data;
}
/**
* @brief 简单延时函数
* @param ms: 延时的毫秒数
*/
void AT24C02_DelayMs(unsigned int ms) {
unsigned int i, j;
for (i = 0; i < ms; i++)
for (j = 0; j < 112; j++); // 针对12MHz晶振的近似延时
}
main.c
#include <regx52.h>
#include "AT24C02.h"
// 定义LED指示灯 - 用于演示状态
sbit LED_READ = P2^5; // 读取指示
sbit LED_WRITE = P2^6; // 写入指示
sbit LED_SUCCESS = P2^7;// 成功指示
/**
* @brief 主函数 - 完整的AT24C02读写测试示例
*/
void main()
{
unsigned char write_data = 0x5A; // 要写入的测试数据
unsigned char read_data; // 存储读取的数据
unsigned char test_address = 0x12; // 测试地址(0x00-0xFF)
// 初始化LED状态
LED_READ = 1;
LED_WRITE = 1;
LED_SUCCESS = 1;
while (1)
{
// 写入数据
LED_WRITE = 0;
AT24C02_WriteByte(test_address, write_data);
AT24C02_DelayMs(200); //延时看灯
LED_WRITE = 1;
// 短暂延时
AT24C02_DelayMs(2000);
// 读取刚写入的数据
LED_READ = 0;
read_data = AT24C02_ReadByte(test_address);
AT24C02_DelayMs(200); //延时看灯
LED_READ = 1;
// 验证数据是否正确
if (read_data == write_data)
{
LED_SUCCESS = 0; // 读写一致,点亮成功指示灯
}
else
{
LED_SUCCESS = 1; // 读写失败
}
}
}
完结,撒花~~~