0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

STM32速成笔记(11)—EEPROM(AT24C02)

冬至子 来源:二土电子 作者:二土电子 2023-10-24 15:12 次阅读

一、AT24C02简介

AT24C01/02/04/08/16...是一个1K/2K/4K/8K/16K位电可擦除PROM,内部含有128/256/512/1024/2048个8位字节,AT24C01有一个8字节页写缓冲器,AT24C02/04/08/16有一个16字节页写缓冲器。

电压可允许低至1.8V,待机电流和工作电流分别为1uA和1mA。该器件通过I2C总线接口进行操作,这里就不再对IIC做详细介绍了,具体可见外设系列OLED篇。

二、AT24C02引脚

图片

AT24C02引脚定义

三、AT24C02寻址

使能芯片读写操作后,EEPROM都要求有8位的器件地址信息

图片

AT24C02地址信息

器件地址信息由"1"、"0"序列组成,前4位对于所有串行EEPROM都是一样的。对于24C02/32/64,随后3位A2、A1和A0为器件地址位,必须与硬件输入引脚保持一致。

四、AT24C02读/写操作

4.1 AT24C02写操作

写操作要求主设备发送器件地址,收到应答信号后,先接收8位的字地址。接收到这个地址后EEPROM应答"0"(ACK),然后再是一个8位数据。在接收8位数据后,EEPROM应答"0"(ACK),接着必须由主器件发送停止条件来终止写序列。时序图如下

图片

写操作时序图

24C02器件按8字节/页执行页写,24C04/08/16器件按16字节/页执行页写,24C32/64器件按32字节/页执行页写。页写初始化与字节写相同,只是主器件不会在第一个数据后发送停止条件,而是在EEPROMEEPROM收到每个数据后都应答“0”。最后仍需由主器件发送停止条件,终止写序列。

接收到每个数据后,字地址的低3位 (24C02) 或4位(24C04/08/16) 或5位(24C32/64)内部自动加1,高位地址位不变,维持在当前页内。当内部产生的字地址达到该页边界地址时,随后的数据将写入该页的页首。如果超过8个 (24C02) 或16个 (24C04/08/16) 或32个(24C32/64) 数据传送给了EEPROM,字地址将回转到该页的首字节,先前的字节将会被覆盖。

4.2 AT24C02读操作

AT24C02的读操作有三种,分别是当前地址读,随机读和顺序读。

  • 当前地址读 内部地址计数器保存着上次访问时最后一个地址加1的值。只要芯片有电,该地址就一直保存当读到最后页的最后字节,地址会回转到0。当写到某页尾的最后一个字节,地址会回转到该页的首字节。接收器件地址(读/写选择位为"1") 且EEPROM应答ACK后,当前地址的数据就随时钟送出。主器件无需应答"0",但需发送停止条件。当前地址读操作时序图如下

图片

读当前地址时序图

  • 随机读 随机读需先写一个目标字地址,一旦EEPROM接收器件地址和字地址并应答了ACK,主器件就产生一个重复的起始条件。然后,主器件发送器件地址(读/写选择位为"1") ,EEPROM应答ACK,并随时钟送出数据。主器件无需应答"0",但需发送停止条件。这里的随机读就是读取任意一个字地址的数据,并不是随即返回一个数据的意思。随机读时序图如下

图片

随机读时序图

  • 顺序读 顺序读可以通过“当前地址读”或“随机读”启动。主器件接收到一个数据后,应答ACK。只要EEPROM接收到ACK,将自动增加字地址并继续随时钟发送后面的数据。若达到存储器地址末尾,地址自动回转到0,仍可继续顺序读取数据。主器件不应答"0",而发送停止条件,即可结束顺序读操作。顺序读时序图如下

图片

顺序读时序图

五、AT24C02程序

这里给出一个AT24C02的程序,仅供参考

/*******************************************************************************
* 函 数 名         : IIC_Init
* 函数功能     : IIC初始化
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void IIC_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(IIC_SCL_PORT_RCC|IIC_SDA_PORT_RCC,ENABLE);
    
    GPIO_InitStructure.GPIO_Pin=IIC_SCL_PIN;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
    GPIO_Init(IIC_SCL_PORT,&GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN;
    GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);
    
    IIC_SCL=1;
    IIC_SDA=1; 
}
/*******************************************************************************
* 函 数 名         : SDA_OUT
* 函数功能     : SDA输出配置    
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void SDA_OUT(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
    GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);
}

/*******************************************************************************
* 函 数 名         : SDA_IN
* 函数功能     : SDA输入配置    
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void SDA_IN(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
    GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);
}

/*******************************************************************************
* 函 数 名         : IIC_Start
* 函数功能     : 产生IIC起始信号   
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void IIC_Start(void)
{
    SDA_OUT();     //sda线输出
    IIC_SDA=1;      
    IIC_SCL=1;
    delay_us(5);
     IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
    delay_us(6);
    IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
} 

/*******************************************************************************
* 函 数 名         : IIC_Stop
* 函数功能     : 产生IIC停止信号   
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void IIC_Stop(void)
{
    SDA_OUT();//sda线输出
    IIC_SCL=0;
    IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
     IIC_SCL=1; 
    delay_us(6); 
    IIC_SDA=1;//发送I2C总线结束信号
    delay_us(6);           
}

/*******************************************************************************
* 函 数 名         : IIC_Wait_Ack
* 函数功能     : 等待应答信号到来   
* 输    入         : 无
* 输    出         : 1,接收应答失败
                     0,接收应答成功
*******************************************************************************/
u8 IIC_Wait_Ack(void)
{
    u8 tempTime=0;
    SDA_IN();      //SDA设置为输入  
    IIC_SDA=1;
    delay_us(1);    
    IIC_SCL=1;
    delay_us(1);  
    while(READ_SDA)
    {
        tempTime++;
        if(tempTime >250)
        {
            IIC_Stop();
            return 1;
        }
    }
    IIC_SCL=0;//时钟输出0     
    return 0;  
} 

/*******************************************************************************
* 函 数 名         : IIC_Ack
* 函数功能     : 产生ACK应答  
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void IIC_Ack(void)
{
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=0;
    delay_us(2);
    IIC_SCL=1;
    delay_us(5);
    IIC_SCL=0;
}

/*******************************************************************************
* 函 数 名         : IIC_NAck
* 函数功能     : 产生NACK非应答  
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/      
void IIC_NAck(void)
{
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=1;
    delay_us(2);
    IIC_SCL=1;
    delay_us(5);
    IIC_SCL=0;
} 

/*******************************************************************************
* 函 数 名         : IIC_Send_Byte
* 函数功能     : IIC发送一个字节 
* 输    入         : txd:发送一个字节
* 输    出         : 无
*******************************************************************************/    
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
    SDA_OUT();      
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t< 8;t++)
    {              
        if((txd&0x80) >0) //0x80  1000 0000
            IIC_SDA=1;
        else
            IIC_SDA=0;
        txd< <=1;    
        delay_us(2);   //对TEA5767这三个延时都是必须的
        IIC_SCL=1;
        delay_us(2); 
        IIC_SCL=0; 
        delay_us(2);
    }  
} 

/*******************************************************************************
* 函 数 名         : IIC_Read_Byte
* 函数功能     : IIC读一个字节 
* 输    入         : ack=1时,发送ACK,ack=0,发送nACK 
* 输    出         : 应答或非应答
*******************************************************************************/  
u8 IIC_Read_Byte(u8 ack)
{
    u8 i,receive=0;
    SDA_IN();//SDA设置为输入
    for(i=0;i< 8;i++ )
    {
        IIC_SCL=0; 
        delay_us(2);
        IIC_SCL=1;
        receive< <=1;
        if(READ_SDA)receive++;   
        delay_us(1); 
    }      
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}
/*******************************************************************************
* 函 数 名         : AT24CXX_Init
* 函数功能     : AT24CXX初始化
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void AT24CXX_Init(void)
{
    IIC_Init();//IIC初始化
}

/*******************************************************************************
* 函 数 名         : AT24CXX_ReadOneByte
* 函数功能     : 在AT24CXX指定地址读出一个数据
* 输    入         : ReadAddr:开始读数的地址 
* 输    出         : 读到的数据
*******************************************************************************/
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{      
    u8 temp=0;                          
    IIC_Start();  
    if(EE_TYPE >AT24C16)
    {
        IIC_Send_Byte(0XA0);    //发送写命令
        IIC_Wait_Ack();
        IIC_Send_Byte(ReadAddr >>8);//发送高地址     
    }
    else 
    {
        IIC_Send_Byte(0XA0+((ReadAddr/256)< < 1));   //发送器件地址0XA0,写数据
    }     
    IIC_Wait_Ack(); 
    IIC_Send_Byte(ReadAddr%256);   //发送低地址
    IIC_Wait_Ack();     
    IIC_Start();        
    IIC_Send_Byte(0XA1);           //进入接收模式      
    IIC_Wait_Ack();  
    temp=IIC_Read_Byte(0);     
    IIC_Stop();//产生一个停止条件     
    return temp;
}

/*******************************************************************************
* 函 数 名         : AT24CXX_WriteOneByte
* 函数功能     : 在AT24CXX指定地址写入一个数据
* 输    入         : WriteAddr  :写入数据的目的地址 
                     DataToWrite:要写入的数据
* 输    出         : 无
*******************************************************************************/
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{                                
    IIC_Start();  
    if(EE_TYPE >AT24C16)
    {
        IIC_Send_Byte(0XA0);     //发送写命令
        IIC_Wait_Ack();
        IIC_Send_Byte(WriteAddr >>8);//发送高地址   
    }
    else 
    {
        IIC_Send_Byte(0XA0+((WriteAddr/256)< < 1));   //发送器件地址0XA0,写数据
    }   
    IIC_Wait_Ack();    
    IIC_Send_Byte(WriteAddr%256);   //发送低地址
    IIC_Wait_Ack();                    
    IIC_Send_Byte(DataToWrite);     //发送字节          
    IIC_Wait_Ack();            
    IIC_Stop();//产生一个停止条件 
    delay_ms(10);  
}

/*******************************************************************************
* 函 数 名         : AT24CXX_WriteLenByte
* 函数功能     : 在AT24CXX里面的指定地址开始写入长度为Len的数据
                     用于写入16bit或者32bit的数据
* 输    入         : WriteAddr  :写入数据的目的地址 
                     DataToWrite:要写入的数据
                     Len        :要写入数据的长度2,4
* 输    出         : 无
*******************************************************************************/
void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
{   
    u8 t;
    for(t=0;t< Len;t++)
    {
        AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite > >(8*t))&0xff);
    }                
}

/*******************************************************************************
* 函 数 名         : AT24CXX_ReadLenByte
* 函数功能     : 在AT24CXX里面的指定地址开始读出长度为Len的数据
                     用于读出16bit或者32bit的数据
* 输    入         : ReadAddr   :开始读出的地址 
                     Len        :要读出数据的长度2,4
* 输    出         : 读取的数据
*******************************************************************************/
u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len)
{   
    u8 t;
    u32 temp=0;
    for(t=0;t< Len;t++)
    {
        temp< <=8;
        temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);          
    }
    return temp;

}

/*******************************************************************************

  • 函 数 名 : AT24CXX_Check
  • 函数功能 : 检查AT24CXX是否正常
  • 输 入 : 无
  • 输 出 : 1:检测失败,0:检测成功
    *******************************************************************************/
    u8 AT24CXX_Check(void)
    {
    u8 temp;
    temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX
    if(temp==0x36)return 0;
    else//排除第一次初始化的情况
    {
    AT24CXX_WriteOneByte(255,0X36);
    temp=AT24CXX_ReadOneByte(255);
    if(temp==0X36)return 0;
    }
    return 1;
    }

/*******************************************************************************

  • 函 数 名 : AT24CXX_Read
  • 函数功能 : 在AT24CXX里面的指定地址开始读出指定个数的数据
  • 输 入 : ReadAddr :开始读出的地址 对24c02为0~255
    pBuffer :数据数组首地址
    NumToRead:要读出数据的个数
  • 输 出 : 无
    *******************************************************************************/
    void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
    {
    while(NumToRead)
    {
    *pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
    NumToRead--;
    }
    }

/*******************************************************************************

  • 函 数 名 : AT24CXX_Write
  • 函数功能 : 在AT24CXX里面的指定地址开始写入指定个数的数据
  • 输 入 : WriteAddr :开始写入的地址 对24c02为0~255
    pBuffer :数据数组首地址
    NumToRead:要读出数据的个数
  • 输 出 : 无
    *******************************************************************************/
    void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
    {
    while(NumToWrite--)
    {
    AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
    WriteAddr++;
    pBuffer++;
    }
    }

.h文件如下

// 核心板使用的是24c02,所以定义EE_TYPE为AT24C02
// 可修改成AT24CXX系列中的任意一个
#define EE_TYPE AT24C02

// IIC函数
void IIC_Init(void); // 初始化IIC的IO口
void IIC_Start(void); // 发送IIC开始信号
void IIC_Stop(void); // 发送IIC停止信号
void IIC_Send_Byte(u8 txd); // IIC发送一个字节
u8 IIC_Read_Byte(u8 ack); // IIC读取一个字节
u8 IIC_Wait_Ack(void); // IIC等待ACK信号
void IIC_Ack(void); // IIC发送ACK信号
void IIC_NAck(void); // IIC不发送ACK信号

u8 AT24CXX_ReadOneByte(u16 ReadAddr); //指定地址读取一个字节
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite); // 指定地址写入一个字节
void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len); // 指定地址开始写入指定长度的数据
u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len); // 指定地址开始读取指定长度数据
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite); // 从指定地址开始写入指定长度的数据
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead); // 从指定地址开始读出指定长度的数据

u8 AT24CXX_Check(void); // 检查器件
void AT24CXX_Init(void); // 初始化IIC

六、应用实例

给AT24C02写入一个数据,读取一次确认写入正常。注释掉写入程序,拔掉电源。一段时间后,插上电源再次读取之前写入时的地址的值,串口打印结果。AT24C02的初始化程序如下

AT24CXX_Init();   // AT24C02初始化
while(AT24CXX_Check())  //检测AT24C02是否正常
{
    printf("AT24C02检测不正常!rn");
    delay_ms(500);
}
printf("AT24C02检测正常!rn");
main函数如下

u8 gWData = 0xaa; // 准备要写入的数据
u8 gRData = 0xaa; // 存储读出的数据

int main(void)
{
Med_Mcu_Iint(); // 系统初始化

AT24CXX_WriteOneByte(0,gWData);
printf("写入的数据是:%drn",gWData);

gRData = AT24CXX_ReadOneByte(0);
printf("读取的数据是:%drn",gRData);

while(1)

{
}
}

串口打印结果如下

图片

串口打印结果

七、拓展应用

AT24C02这种掉电数据不丢失的特性,使得它可以存储一些重要数据。比如将一些校准数据写入AT24C02中,再次上电之后就不会丢失。或者用AT24C02记录开机次数等。这些原理与应用实例中的例子原理相同,这里就不再赘述了。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 缓冲器
    +关注

    关注

    6

    文章

    1566

    浏览量

    44878
  • EEPROM
    +关注

    关注

    9

    文章

    927

    浏览量

    80314
  • ACK
    ACK
    +关注

    关注

    0

    文章

    27

    浏览量

    11078
  • I2C总线
    +关注

    关注

    8

    文章

    357

    浏览量

    60408
  • AT24C02
    +关注

    关注

    0

    文章

    79

    浏览量

    24098
收藏 人收藏

    评论

    相关推荐

    STM32 IIC读写AT24C02(二)

    上次已经完成了IIC读写AT24C02的协议层,现在开始编写读写AT24C02的驱动函数。先从单字节的读写开始。
    发表于 07-22 10:31 761次阅读
    <b class='flag-5'>STM32</b> IIC读写<b class='flag-5'>AT24C02</b>(二)

    STM32基础知识:IIC总线操作EEPROM存储模块AT24C02

    本文是IIC总线的实际应用,将带领读者一步一步阅读AT24C02数据手册,看时序图了解如何使用IIC接口EEPROM存储模块AT24C02,并编写代码使用STM32驱动这个模块。
    的头像 发表于 10-26 14:25 1072次阅读
    <b class='flag-5'>STM32</b>基础知识:IIC总线操作<b class='flag-5'>EEPROM</b>存储模块<b class='flag-5'>AT24C02</b>

    【OneNET麒麟座试用体验】6.EEPROM存储器AT24C02的使用

    AT24C02是一款EEPROM存储芯片,存储大小只有256字节,没有听错,就是字节,相比我们512K的Flash,那么这么小的空间能干什么呢? 因为Flash只能整页擦出再重新写,而且Flash
    发表于 04-01 10:36

    AT24C02的使用

    在I2C总线这块,使用AT24C02作为存储器,作用就是存储密码,但是不是很理解其中说的器件地址是啥意思,目前就是一个AT24C02与单片机连接,求大神给我说说在存储密码这块,AT24C02
    发表于 05-14 17:36

    AT24C02芯片工作原理是什么

    AT24C02芯片工作原理在IIC通信时,一般有写和读两种操作,我们用AT24C02来具体讲解。AT24C02芯片工作原理AT24C02芯片是以IIC接口的
    发表于 07-29 09:04

    配置STM32的IIC硬件时序读写AT24C02和AT24C08

    时序的,本文采用的是模拟时序,下篇文章就介绍配置STM32的IIC硬件时序读写AT24C02和AT24C08。模拟时序更加方便移植到其他单片机,通用性更高,不分MCU;硬件时序效率更高,单每个MCU配置方法不同,依赖硬件本身支持
    发表于 12-08 06:27

    AT24C02的相关资料推荐

    AT24C02(I2C总线)_AT24C02数据存储&秒表(定时器扫描按键数码管)小白极客的51单片机笔记(自用)第一部分—存储器介绍第二部分—AT
    发表于 12-10 06:15

    EEPROM原理相关电路AT24C02器件

    蓝桥杯单片机比赛系列7EEPROMEEPROM原理相关电路AT24C02器件地址EEPROM自写代码EEPROM原理相关电路有了系列6的基础,上手eeprom就简单多了,板子上对应的器
    发表于 01-11 07:14

    AT24C02资料手册

    at24c02资料手册 中文资料很实用的at24c02资料
    发表于 03-22 17:38 79次下载

    AT24C02

    AT24C02
    发表于 01-24 16:00 11次下载

    AT24C02资料

    AT24C02资料
    发表于 12-06 17:14 29次下载
    <b class='flag-5'>AT24C02</b>资料

    EEPROM存储芯片AT24C02芯片手册

    EEPROM存储芯片AT24C02芯片手册(嵌入式开发培训需要学多久)-EEPROM存储芯片AT24C02芯片手册
    发表于 07-30 13:20 178次下载
    <b class='flag-5'>EEPROM</b>存储芯片<b class='flag-5'>AT24C02</b>芯片手册

    STM32Cube_13】使用硬件I2C读写EEPROMAT24C02

    本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的硬件I2C外设读取EEPROM数据(以AT24C02为例)。
    发表于 12-08 10:36 16次下载
    【<b class='flag-5'>STM32</b>Cube_13】使用硬件I2C读写<b class='flag-5'>EEPROM</b>(<b class='flag-5'>AT24C02</b>)

    STM32f103系列i2c读写文件at24c02

    STM32f103系列i2c读写文件at24c02免费下载。
    发表于 03-04 09:43 22次下载

    Linux驱动开发-编写(EEPROM)AT24C02驱动

    AT24C02是IIC接口的EEPROM存储芯片,这颗芯片非常经典,百度搜索可以找到非常多的资料,大多都是51、STM32单片机的示例代码,大多采用模拟时序、裸机系统运行。当前文章介绍在Linux
    的头像 发表于 09-17 15:29 3022次阅读
    Linux驱动开发-编写(<b class='flag-5'>EEPROM</b>)<b class='flag-5'>AT24C02</b>驱动