引言
AT24C02是一款由Atmel(现为Microchip Technology公司的一部分)生产的2Kb(256字节)I²C接口EEPROM存储器芯片。它常用于需要非易失性存储的小型嵌入式系统中,比如存储配置信息或校准数据。可用于保存单片机运行时想要永久保存的数据信息。存储介质是E2PROM。
什么是EEPROM?
EEPROM(Electrically Erasable Programmable Read-Only Memory,电可擦除可编程只读存储器)是一种非易失性存储器,可以通过电信号擦除和重新编程。
EEPROM的特性
数据在断电后依然保存,这使得EEPROM非常适合存储需要长期保存的配置信息、校准数据和其他重要信息。EEPROM可以通过电信号进行擦除和写入,无需物理移除芯片。EEPROM支持字节级别的读写操作,这意味着可以对单个字节进行修改,而无需擦除整个块。EEPROM的读写速度相对较慢,特别是与RAM相比,但足以满足大多数非实时数据存储需求。
芯片概述
AT24C02是EEPROM的一种具体实现,它采用了IIC接口进行数据传输。具体而言,AT24C02是EEPROM大家族中的一个成员,具备了EEPROM的基本特性,同时结合了I²C总线接口,方便与微控制器进行通信。它具有以下特点:
- 存储容量:最大容量2Kb(256字节)
- 接口类型:IIC通信协议(Inter-Integrated Circuit)
- 工作电压:1.8V至5.5V
- 工作温度范围:-40°C至+85°C
- 封装类型:常见封装包括8引脚的PDIP、SOIC、TSSOP等
注意:有关IIC通信协议,可查看此博客内容讲解:IIC总线原理特性解析及通信要点
芯片及引脚说明
AT24C02通常采用8引脚的封装形式,如PDIP、SOIC、TSSOP等。每个引脚都有其特定的功能,具体引脚配置如下:
各引脚功能详解
- 地址引脚0(A0, 引脚1):用于设置I²C设备地址的一部分。通过连接到Vcc或GND,可以设置不同的设备地址。A0引脚的电平值决定了地址的最低位(A0位)。
- 地址引脚1(A1, 引脚2):用于设置I²C设备地址的一部分。通过连接到Vcc或GND,可以设置不同的设备地址。A1引脚的电平值决定了地址的次低位(A1位)。
- 地址引脚2(A2, 引脚3):用于设置I²C设备地址的一部分。通过连接到Vcc或GND,可以设置不同的设备地址。A2引脚的电平值决定了地址的次高位(A2位)。
- GND(引脚4):电源地连接,提供芯片的基准电压,是整个电路的参考点。
- SDA(引脚5):串行数据线,用于IIC总线上的双向数据传输。通过这条线,主设备和从设备之间可以进行数据交换。
- SCL(引脚6):串行时钟线,提供IIC总线上的时钟信号,用于同步数据传输。主设备生成时钟信号,控制通信的节奏。
- WP(引脚7):写保护引脚,控制EEPROM的写保护状态。当WP引脚接高电平时,EEPROM进入写保护模式,禁止写操作;当WP引脚接低电平时,允许写操作。
- 0 Vcc(引脚8):电源引脚,连接到电源正极,提供工作电压给芯片,工作电压范围为1.8V至5.5V。
高四位是固定的,为1010
,低四位是可编程的,最后一位用于设置数据传输方向。在前面提到的IIC总线原理特性解析及通信要点有详细讲解。
电路图
下图是普中A7开发板EEPROM模块电路图,采用STC89C51RC/RD+
系列,SCL连接P2_1引脚,SDA连接P2_0引脚。(从图中可以看出,这两条线上带有上拉电阻),操作方式可以采用IIC时序,IIC总线原理特性解析及通信要点有详细讲解。这里不过多说明。
起始停止信号
根据IIC总线通信时序,进行编写代码,下图则是对应时序图:
//起始信号
void iic_start()
{
IIC_SCL = 1;
IIC_SDA = 1;
DelayXms(1);
IIC_SDA = 0;
DelayXms(1);
IIC_SCL = 0;//开始占用总线
}
//停止信号
void iic_stop()
{
IIC_SCL = 1;
IIC_SDA = 0;
DelayXms(1);
IIC_SDA = 1;
}
应答与非应答
时钟线SCL为低电平期间,SDA数据允许发生更改。SDA低电平表示应答信号,高电平表示非应答信号。下方为对应时序图:
//应答信号
void iic_ack()
{
IIC_SCL = 0;
IIC_SDA = 0;
DelayXms(1);
IIC_SCL = 1;
DelayXms(1);
IIC_SCL = 0;
}
//非应答信号
void iic_nack()
{
IIC_SCL = 0;
IIC_SDA = 1;
DelayXms(1);
IIC_SCL = 1;
DelayXms(1);
IIC_SCL = 0;
}
代码演示
系统运行时,数码管右3位显示0,按K1键将数据写入到EEPROM内保存,按K2键读取EEPROM内保存的数据,按K3键显示数据加1,按K4键显示数据清零。
#include <REGX52.H>
#include <INTRINS.H>
#define EEPROM_ADDRESS 0
sbit KEY1 = P3^0;
sbit KEY2 = P3^1;
sbit KEY3 = P3^2;
sbit KEY4 = P3^3;
sbit IIC_SCL = P0^0;
sbit IIC_SDA = P0^1;
sbit LED1 = P2^0;
sbit LED2 = P2^1;
//共阴极数码管显示 0~F 的段码数据
unsigned char gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
void DelayXms(unsigned int xms) //@12.000MHz
{
unsigned char data i, j;
while(xms)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
xms--;
}
}
unsigned char Scan_keys()
{
if(KEY1 == 0 || KEY2 == 0 || KEY3 == 0 || KEY4 == 0)
{
DelayXms(100);//消抖处理
if(KEY1 == 0)
return 1;
else if(KEY2 == 0)
return 2;
else if(KEY3 == 0)
return 3;
else if(KEY4 == 0)
return 4;
}
return 0;
}
void Init_Port_Smg(unsigned char Location,unsigned char dat[])
{
unsigned char i = 0;
unsigned char local = Location - 1;
for(i = Location;i < 8;i++)
{
switch(i+1)
{
case 1:
P2_2 = 0;P2_3 = 0;P2_4 = 0;
break;
case 2:
P2_2 = 1;P2_3 = 0;P2_4 = 0;
break;
case 3:
P2_2 = 0;P2_3 = 1;P2_4 = 0;
break;
case 4:
P2_2 = 1;P2_3 = 1;P2_4 = 0;
break;
case 5:
P2_2 = 0;P2_3 = 0;P2_4 = 1;
break;
case 6:
P2_2 = 1;P2_3 = 0;P2_4 = 1;
break;
case 7:
P2_2 = 0;P2_3 = 1;P2_4 = 1;
break;
case 8:
P2_2 = 1;P2_3 = 1;P2_4 = 1;
break;
}
P1 = gsmg_code[dat[i-Location]];
DelayXms(1);
P1 = 0x00;//消影
}
}
//起始信号
void iic_start()
{
IIC_SCL = 1;
IIC_SDA = 1;
_nop_();
IIC_SDA = 0;
_nop_();
IIC_SCL = 0;//开始占用总线
}
//停止信号
void iic_stop()
{
IIC_SCL = 1;
IIC_SDA = 0;
_nop_();
IIC_SDA = 1;
}
//应答信号
void iic_ack()
{
IIC_SCL = 0;
IIC_SDA = 0;
_nop_();
IIC_SCL = 1;
_nop_();
IIC_SCL = 0;
}
//非应答信号
void iic_nack()
{
IIC_SCL = 0;
IIC_SDA = 1;
_nop_();
IIC_SCL = 1;
_nop_();
IIC_SCL = 0;
}
unsigned char iic_wait_ack()
{
unsigned char time = 0;
IIC_SCL = 1;
_nop_();
while(IIC_SDA)
{
time++;
if(time>100)
{
iic_stop();
return 1;
}
}
IIC_SCL = 0;
return 0;
}
void iic_write_byte(unsigned char dat)
{
unsigned char i = 0;
IIC_SCL = 0;//数据可以发生变化
for(i = 0;i< 8;i++)
{
if((dat&0X80)>0)//先发送高位
{
IIC_SDA = 1;
}
else
{
IIC_SDA = 0;
}
dat<<=1;
IIC_SCL = 1;//数据发送
_nop_();
IIC_SCL = 0;
_nop_();
}
}
unsigned char iic_read_byte(unsigned char ack)
{
unsigned char i = 0;
unsigned char res = 0;
for(i = 0;i < 8;i++)
{
IIC_SCL = 0;
_nop_();
IIC_SCL = 1;//数据发送
_nop_();
res<<=1;
if(IIC_SDA)
{
res++;
}
}
if(!ack)//nack
iic_nack();
else
iic_ack();
return res;
}
void at24c02_write_byte(unsigned char dat,unsigned char addr)
{
iic_start();//发送起始信号 A0写 A1读
iic_write_byte(0xa0);
iic_wait_ack();//等待从机的ack
iic_write_byte(addr);//发送要写的设备地址
iic_wait_ack();//等待从机的ack
iic_write_byte(dat);//发送数据
iic_wait_ack();//等待从机的ack
iic_stop();//停止
_nop_();
}
unsigned char at24c02_read_byte(unsigned char addr)
{
unsigned char temp = 0;
iic_start();//发送起始信号 A0写 A1读
iic_write_byte(0xa0);
iic_wait_ack();//等待从机的ack
iic_write_byte(addr);//发送要写的设备地址
iic_wait_ack();//等待从机的ack
iic_start();//发送起始信号 A0写 A1读
iic_write_byte(0xa1);//发送数据
iic_wait_ack();//等待从机的ack
temp = iic_read_byte(0);
iic_stop();//停止
return temp;
}
void main()
{
unsigned char keynum = 0;
unsigned char i = 0;
unsigned char sava_value = 0;
unsigned char save_buf[3];
while(1)
{
keynum = Scan_keys();
if(keynum == 1)
{
at24c02_write_byte(sava_value,EEPROM_ADDRESS);
}
else if(keynum == 2)
{
sava_value = at24c02_read_byte(EEPROM_ADDRESS);
}
else if(keynum == 3)
{
sava_value++;
if(sava_value >= 255)//最大值
{
sava_value = 255;
}
}
else if(keynum == 4)
{
sava_value = 0;
}
save_buf[0] = sava_value/100;//百位
save_buf[1] = sava_value%100/10;//十位
save_buf[2] = sava_value%100%10;//个位
Init_Port_Smg(5,save_buf);
}
}