Bootstrap

AT24C02存储芯片

AT24C02芯片

目录

AT24C02芯片

一、基本参数

二、接口与引脚

三、功能特点

四、I2C时序结构

起始条件和终止条件

字节传输

发送一个字节:

接受一个字节

发送应答和接受应答

发送完之后要接收应答

接收完之后要发送应答

发送应答

接受应答

发送一帧数据

接收一帧数据

先发送再接受(复合格式)

字节写和随机读

字节写

随机读

五、代码实现

I2C(Inter-Integrated Circuit)总线协议的简单通信功能

I2C接口与AT24C02 EEPROM进行通信

AT24C02_WriteByte 函数

AT24C02_ReadByte 函数

六、使用注意事项


一、基本参数

  • 存储容量: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***

发送完一个字节之后要接受应答,接受完一个字节后要发送应答

发送完之后要接收应答

当一方(发送方)发送完数据后,它通常会期望从另一方(接收方)接收一个应答信号。这个应答信号的作用主要是:

  1. 确认接收:应答信号是接收方告诉发送方“我已经收到了你发送的数据”的一种方式。

  2. 错误检测:如果接收方在接收过程中发现数据错误(如校验错误),它可能会发送一个不应答(NACK)信号来通知发送方。

  3. 流控制:应答机制还可以帮助控制数据流的速度。如果接收方因为某种原因(如缓冲区满)无法立即处理更多数据,它可以通过不应答来告诉发送方暂时停止发送。

接收完之后要发送应答

当接收方成功接收到数据后,它通常需要发送一个应答信号给发送方。这个应答信号的作用主要是:

  1. 确认处理:应答信号是接收方告诉发送方“我已经成功处理了你发送的数据”的一种方式。

  2. 请求更多数据:在某些协议中,接收方的应答可能还包含请求发送方继续发送更多数据的信号。

  3. 维持通信:通过发送应答,接收方告诉发送方它仍然在线并准备好进行进一步的通信

发送应答:
/*
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)总线协议的简单通信功能
  1. I2C_Start(void) 这个函数用于生成I2C总线的启动条件。首先,通过拉高SDA和SCL线确保总线处于空闲状态,然后SDA在SCL为高电平时被拉低,形成启动条件。

  2. I2C_Stop(void) 这个函数用于生成I2C总线的停止条件。首先,SDA在SCL为高电平时被拉低,然后SCL被拉高,最后SDA在SCL为高电平时被拉高,形成停止条件。

  3. I2C_SendByte(ucha Byte) 这个函数用于通过I2C总线发送一个字节的数据。它通过循环将字节的每一位(从最高位开始)依次放到SDA线上,并在SCL的每个上升沿将位值锁存到总线上。注意,这里有一个小错误:在循环中,SDA的值应该是通过位操作来设置的,但(Byte&(0x80>>i))实际上并不正确,因为这会使得每次迭代时掩码的值递减(从0x00到0x01),而不是检查Byte的每一位。正确的应该是(Byte & (0x80 >> i)),但通常我们会写为(Byte >> i) & 0x01来确保只检查当前位。

  4. I2C_ReceiveByte(void) 这个函数用于通过I2C总线接收一个字节的数据。在接收过程中,SDA线被释放为高阻态(由外部设备驱动),然后通过检测SCL上升沿时SDA的状态来接收每一位数据。接收到的位被左移并累加,最终形成一个完整的字节。

  5. I2C_SendAck(ucha ackbit) 这个函数用于发送一个应答信号(ACK)或非应答信号(NACK)。它通过设置SDA线的状态(低电平为ACK,高电平为NACK),然后拉高SCL线一个时钟周期来实现。

  6. 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 是要写入该地址的数据(也是一个无符号字符)。

  • 过程

    1. 调用 I2C_Start() 发送I2C起始条件。

    2. 调用 I2C_SendByte(AT24C02_ADDRESS) 发送EEPROM的设备地址(0xa0),注意这里的地址是以写模式发送的(末尾为0)。

    3. 调用 I2C_ReceiveAck() 接收从设备的应答,但这里并没有处理应答的结果。在实际应用中,应该检查应答以确认从设备是否已准备好接收数据。

    4. 调用 I2C_SendByte(WordAddress) 发送要写入数据的地址。

    5. 再次调用 I2C_ReceiveAck() 接收应答,确认地址已被从设备接收。

    6. 调用 I2C_SendByte(Data) 发送要写入的数据。

    7. 调用 I2C_ReceiveAck() 接收应答,但同样没有处理结果。

    8. 调用 I2C_Stop() 发送I2C停止条件,结束通信。

注意:函数中的 I2C_ReceiveAck() 调用虽然接收了应答,但没有检查其返回值来确定通信是否成功。在实际应用中,应该根据应答结果来决定是否继续执行或进行错误处理。

AT24C02_ReadByte 函数

这个函数的目的是从AT24C02 EEPROM的指定地址读取一个字节的数据。

  • 参数WordAddress 是要读取数据的地址(一个无符号字符)。

  • 返回值:从指定地址读取的数据(一个无符号字符)。

  • 过程

    1. 调用 I2C_Start() 发送I2C起始条件。

    2. 调用 I2C_SendByte(AT24C02_ADDRESS) 发送EEPROM的设备地址(以写模式),以便设置要读取数据的地址。

    3. 调用 I2C_ReceiveAck() 接收应答。

    4. 调用 I2C_SendByte(WordAddress) 发送要读取数据的地址。

    5. 再次调用 I2C_ReceiveAck() 接收应答。

    6. 调用 I2C_Start() 再次发送I2C起始条件。

    7. 调用 I2C_SendByte(AT24C02_ADDRESS|0x01) 发送EEPROM的设备地址(这次以读模式发送,通过将地址的最低位设置为1来实现)。

    8. 调用 I2C_ReceiveAck() 接收应答。

    9. 调用 I2C_ReceiveByte() 接收从设备发送的数据,并将其存储在 Data 变量中。

    10. 调用 I2C_SendAck(1) 发送非应答信号(SDA为高电平),告诉从设备不需要更多数据。

    11. 调用 I2C_Stop() 发送I2C停止条件,结束通信。

    12. 返回读取到的数据 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芯片,适用于多种低功耗、高速存储的应用场景。

;