AT24C02芯片
目录
I2C(Inter-Integrated Circuit)总线协议的简单通信功能
一、基本参数
-
存储容量:2Kbit,即内部含有256个8位字节的存储空间。
-
工作电压:宽范围,从1.8V至5.5V。
-
通信接口:采用I2C(Inter-Integrated Circuit)串行总线接口,这是一种广泛使用的二线制串行总线标准。
-
存储速率:支持400KHz的传输速率(在特定电压下,如1.8V, 2.5V, 2.7V, 3.6V)。
-
可擦写次数:高达100万次(也有资料指出为10000次,这可能是不同批次或版本之间的差异,但通常认为其可擦写次数非常可观)。
-
存储器保持时间:数据保留时间超过100年,但也有资料指出为10年,这可能与具体的工作环境和使用条件有关。
-
工作温度范围:-40℃至+85℃
二、接口与引脚
-
引脚配置:AT24C02通常具有8个引脚,包括电源引脚(VCC和GND)、串行数据输入/输出引脚(SDA)、串行时钟输入引脚(SCL)、写保护引脚(WP)以及用于设置器件地址的引脚(A0、A1、A2)。
-
I2C总线:SDA和SCL引脚用于通过I2C总线与主控设备(如单片机)进行通信。SDA是双向数据线,SCL是时钟线。两者都需要通过上拉电阻连接到正电源,以确保在总线空闲时保持高电平状态。
三、功能特点
-
低功耗:采用先进的CMOS技术,工作电流低(1mA),待机电流极低(1uA),适合低功耗应用。
-
写保护功能:具有硬件写保护功能,通过WP引脚控制。当WP引脚接地时,允许正常的读/写操作;当WP引脚接高电平时,禁止写操作,以保护存储器内的数据不被意外更改。
-
灵活的寻址方式:通过A0、A1、A2引脚可以设置不同的器件地址,以实现多个AT24C02芯片在同一I2C总线上的级联使用。
-
高速读写:写入速度快(小于10ms),支持快速数据存取。
四、I2C时序结构
起始条件和终止条件
-
起始条件:SCl高电平期间,SDA从高电平切换到低电平
-
终止条件:SCl高电平期间,SDA从低电平切换到高电平
注意:SCL处于高电平之间的时候,SDA才可以切换电平
代码所示:
/*
I2C开始函数
*/
void I2C_Start(void)
{
SDA=1;
SCL=1;
SDA=0;
SCL=0;
}
/*
I2C停止函数
*/
void I2C_Stop(void)
{
SDA=0;
SCL=1;
SDA=1;
}
字节传输
发送一个字节:
SCL低电平期间,主机将数据位依次放到SDA线上(高位在前),然后拉高SCL,从机将在SCL高电平期间读取数据位所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次即可发送一个字节
高位在前,在SCL处于高电平之间,SDA不允许有数据变化,高电平读取,低电平写
此时单片机是机,at24c02是从机
注意:在发送完一个字节之后,要拉高SCL,再拉高SDA
代码所示:
/*
I2C发送一个字节
Byte为要发送的字节
*/
void I2C_SendByte(ucha Byte)
{
ucha i;
for(i=0;i<8;i++)
{
SDA=Byte&(0x80>>i);
SCL=1;
SCL=0;
}
}
接收一个字节:
注意:主机在接受数据之前,需要先释放SDA,即SDA=1;拉高电平
发送的时候主机控制scl读取和sda发送,接收的时候主机就只控制scl读取了,sda给从机发数据用
代码所示:
/*
I2C接受一个字节
Byte接收到的一个字节数据
*/
ucha I2C_ReceiveByte(void)
{
ucha i,Byte=0x00;
SDA=1;
for(i=0;i<8;i++)
{
SCL=1;
if(SDA)
{
Byte|=(0x80>>i);//if判断,此时从机拿着话语权,从机在SDA上发送0或1的状态,主机对从机发送的状态进行翻译 |=(0x80),从机送过来一个状态,主机翻译一个,将结果丢入Byte这个小盒子,最终返回出来
}
SCL=0;
}
return Byte;
}
发送应答和接受应答
注意,无论什么时候SDA在接受之前,都要释放SDA***
发送完一个字节之后要接受应答,接受完一个字节后要发送应答
发送完之后要接收应答
当一方(发送方)发送完数据后,它通常会期望从另一方(接收方)接收一个应答信号。这个应答信号的作用主要是:
-
确认接收:应答信号是接收方告诉发送方“我已经收到了你发送的数据”的一种方式。
-
错误检测:如果接收方在接收过程中发现数据错误(如校验错误),它可能会发送一个不应答(NACK)信号来通知发送方。
-
流控制:应答机制还可以帮助控制数据流的速度。如果接收方因为某种原因(如缓冲区满)无法立即处理更多数据,它可以通过不应答来告诉发送方暂时停止发送。
接收完之后要发送应答
当接收方成功接收到数据后,它通常需要发送一个应答信号给发送方。这个应答信号的作用主要是:
-
确认处理:应答信号是接收方告诉发送方“我已经成功处理了你发送的数据”的一种方式。
-
请求更多数据:在某些协议中,接收方的应答可能还包含请求发送方继续发送更多数据的信号。
-
维持通信:通过发送应答,接收方告诉发送方它仍然在线并准备好进行进一步的通信
发送应答:
/*
I2C发送应答
askbit 为应答位,0为应答,1为非应答
*/
void I2C_SendAck(ucha ackbit)
{
SDA=ackbit;
SCL=1;
SCL=0;
}
接受应答:
/*
I2C接受应答位
返回值为接受到的应答位,0为应答,1为非应答
*/
unsigned char I2C_ReceiveAck(void)
{
ucha AckBit,i;
SDA=1;//释放SDA
SCL=1;
AckBit=SDA;
SCL=0;
return AckBit;
}
发送一帧数据
高电位读,低电位写 前四位固定为1010,A2~A0为手动配置(i2c地址),末尾为读写标志位
接收一帧数据
此时,总线的控制权交给了从机,最后一个字节发非应答,停止
先发送再接受(复合格式)
字节写和随机读
第一个为器件地址,第二个为字地址(用于指定器件内部的具体寄存器或内存位置,以便进行读写操作)
字节写
代码所示:
/*
AT24C02写入一个字节
WordADDress 要写入字节的地址
Data 要写入的数据
*/
void AT24C02_WriteByte(unsigned char WordAddress,Data)
{
//ucha ack;
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
// ack=I2C_ReceiveAck(); //返回的结果为0,0为从机应答之后的回馈信号
// if(ack==0)
// {
// P2=0x00;
// }
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_SendByte(Data);
I2C_ReceiveAck();
I2C_Stop();
}
随机读
代码所示:
/*
AT24C02读取一个字节
WordADDress 要读出字节的地址
返回值为读出的数据
*/
ucha AT24C02_ReadByte(ucha WordAddress)
{
ucha 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);//SDA等于1为非应答,SDA等于0为应答,SCL等于1为相应应答,为0拒绝应答
I2C_Stop();
return Data;
}
五、代码实现
I2C(Inter-Integrated Circuit)总线协议的简单通信功能
-
I2C_Start(void) 这个函数用于生成I2C总线的启动条件。首先,通过拉高SDA和SCL线确保总线处于空闲状态,然后SDA在SCL为高电平时被拉低,形成启动条件。
-
I2C_Stop(void) 这个函数用于生成I2C总线的停止条件。首先,SDA在SCL为高电平时被拉低,然后SCL被拉高,最后SDA在SCL为高电平时被拉高,形成停止条件。
-
I2C_SendByte(ucha Byte) 这个函数用于通过I2C总线发送一个字节的数据。它通过循环将字节的每一位(从最高位开始)依次放到SDA线上,并在SCL的每个上升沿将位值锁存到总线上。注意,这里有一个小错误:在循环中,SDA的值应该是通过位操作来设置的,但(Byte&(0x80>>i))实际上并不正确,因为这会使得每次迭代时掩码的值递减(从0x00到0x01),而不是检查Byte的每一位。正确的应该是(Byte & (0x80 >> i)),但通常我们会写为(Byte >> i) & 0x01来确保只检查当前位。
-
I2C_ReceiveByte(void) 这个函数用于通过I2C总线接收一个字节的数据。在接收过程中,SDA线被释放为高阻态(由外部设备驱动),然后通过检测SCL上升沿时SDA的状态来接收每一位数据。接收到的位被左移并累加,最终形成一个完整的字节。
-
I2C_SendAck(ucha ackbit) 这个函数用于发送一个应答信号(ACK)或非应答信号(NACK)。它通过设置SDA线的状态(低电平为ACK,高电平为NACK),然后拉高SCL线一个时钟周期来实现。
-
I2C_ReceiveAck(void) 这个函数用于接收一个应答位。它首先释放SDA线(设置为输入模式),然后拉高SCL线一个时钟周期,读取SDA线的状态(低电平为ACK,高电平为NACK),并将状态返回。
#include <REGX52.H>
sbit SCL=P2^1;
sbit SDA=P2^0;
typedef unsigned int uint;
typedef unsigned char ucha;
/*
I2C开始函数
*/
void I2C_Start(void)
{
SDA=1;
SCL=1;
SDA=0;
SCL=0;
}
/*
I2C停止函数
*/
void I2C_Stop(void)
{
SDA=0;
SCL=1;
SDA=1;
}
/*
I2C发送一个字节
Byte为要发送的字节
*/
void I2C_SendByte(ucha Byte)
{
ucha i;
for(i=0;i<8;i++)
{
SDA=Byte&(0x80>>i);
SCL=1;
SCL=0;
}
}
/*
I2C接受一个字节
Byte接收到的一个字节数据
*/
ucha I2C_ReceiveByte(void)
{
ucha i,Byte=0x00;
SDA=1;
for(i=0;i<8;i++)
{
SCL=1;
if(SDA)
{
Byte|=(0x80>>i);//if判断,此时从机拿着话语权,从机在SDA上发送0或1的状态,主机对从机发送的状态进行翻译 |=(0x80),从机送过来一个状态,主机翻译一个,将结果丢入Byte这个小盒子,最终返回出来
}
SCL=0;
}
return Byte;
}
/*
I2C发送应答
askbit 为应答位,0为应答,1为非应答
*/
void I2C_SendAck(ucha ackbit)
{
SDA=ackbit;
SCL=1;
SCL=0;
}
/*
I2C接受应答位
返回值为接受到的应答位,0为应答,1为非应答
*/
unsigned char I2C_ReceiveAck(void)
{
ucha AckBit,i;
SDA=1;//释放SDA
SCL=1;
AckBit=SDA;
SCL=0;
return AckBit;
}
I2C接口与AT24C02 EEPROM进行通信
AT24C02_WriteByte
函数
这个函数的目的是向AT24C02 EEPROM的指定地址写入一个字节的数据。
-
参数:
WordAddress
是要写入数据的地址(一个无符号字符),Data
是要写入该地址的数据(也是一个无符号字符)。 -
过程:
-
调用
I2C_Start()
发送I2C起始条件。 -
调用
I2C_SendByte(AT24C02_ADDRESS)
发送EEPROM的设备地址(0xa0
),注意这里的地址是以写模式发送的(末尾为0)。 -
调用
I2C_ReceiveAck()
接收从设备的应答,但这里并没有处理应答的结果。在实际应用中,应该检查应答以确认从设备是否已准备好接收数据。 -
调用
I2C_SendByte(WordAddress)
发送要写入数据的地址。 -
再次调用
I2C_ReceiveAck()
接收应答,确认地址已被从设备接收。 -
调用
I2C_SendByte(Data)
发送要写入的数据。 -
调用
I2C_ReceiveAck()
接收应答,但同样没有处理结果。 -
调用
I2C_Stop()
发送I2C停止条件,结束通信。
-
注意:函数中的 I2C_ReceiveAck()
调用虽然接收了应答,但没有检查其返回值来确定通信是否成功。在实际应用中,应该根据应答结果来决定是否继续执行或进行错误处理。
AT24C02_ReadByte
函数
这个函数的目的是从AT24C02 EEPROM的指定地址读取一个字节的数据。
-
参数:
WordAddress
是要读取数据的地址(一个无符号字符)。 -
返回值:从指定地址读取的数据(一个无符号字符)。
-
过程:
-
调用
I2C_Start()
发送I2C起始条件。 -
调用
I2C_SendByte(AT24C02_ADDRESS)
发送EEPROM的设备地址(以写模式),以便设置要读取数据的地址。 -
调用
I2C_ReceiveAck()
接收应答。 -
调用
I2C_SendByte(WordAddress)
发送要读取数据的地址。 -
再次调用
I2C_ReceiveAck()
接收应答。 -
调用
I2C_Start()
再次发送I2C起始条件。 -
调用
I2C_SendByte(AT24C02_ADDRESS|0x01)
发送EEPROM的设备地址(这次以读模式发送,通过将地址的最低位设置为1来实现)。 -
调用
I2C_ReceiveAck()
接收应答。 -
调用
I2C_ReceiveByte()
接收从设备发送的数据,并将其存储在Data
变量中。 -
调用
I2C_SendAck(1)
发送非应答信号(SDA为高电平),告诉从设备不需要更多数据。 -
调用
I2C_Stop()
发送I2C停止条件,结束通信。 -
返回读取到的数据
Data
。
-
这个函数正确地实现了通过I2C接口从AT24C02 EEPROM读取数据的过程。不过,同样需要注意的是,在实际应用中应该根据 I2C_ReceiveAck()
的返回值来确认通信的每一步是否成功。
#include <REGX52.H>
#include "I2C.h"
#include "AT24C02.h"
#define AT24C02_ADDRESS 0xa0 // 1010 0000 末尾为0是写入,1为读取 低位写,高位读
typedef unsigned int uint;
typedef unsigned char ucha;
/*
AT24C02写入一个字节
WordADDress 要写入字节的地址
Data 要写入的数据
*/
void AT24C02_WriteByte(ucha WordAddress,ucha Data)
{
//ucha ack;
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
// ack=I2C_ReceiveAck(); //返回的结果为0,0为从机应答之后的回馈信号
// if(ack==0)
// {
// P2=0x00;
// }
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_SendByte(Data);
I2C_ReceiveAck();
I2C_Stop();
}
/*
AT24C02读取一个字节
WordADDress 要读出字节的地址
返回值为读出的数据
*/
ucha AT24C02_ReadByte(ucha WordAddress)
{
ucha 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);//SDA等于1为非应答,SDA等于0为应答,SCL等于1为相应应答,为0拒绝应答
I2C_Stop();
return Data;
}
六、使用注意事项
-
在使用AT24C02时,需要确保I2C总线的时序和协议得到正确遵守,以避免通信失败或数据错误。
-
在进行写操作时,需要注意写保护引脚的状态,以避免意外地禁止写操作。
-
当需要连续写入多个字节时,应注意写入周期的限制,以避免写入速度过快导致的问题。
综上所述,AT24C02是一种功能强大、易于使用的串行EEPROM芯片,适用于多种低功耗、高速存储的应用场景。