嵌入式——EEPROM(AT24C02)

目录

一、初识AT24C02

1. 介绍

2. 引脚功能

补:

二、AT24C02组成

1. 存储结构

2. AT24C02通讯地址

3. AT24C02寻址方式

(1)芯片寻址

(2)片内子地址寻址

三、IlC通信部分规则

四、AT24C02时序

1. 总线空闲

2. 启动数据传输

3. 停止数据传输

4. 数据有效

5. 确认时序

五、AT24C02读写时序

1. 写操作

(1)字节写入方式

流程:

(2)页写入方式

2. 读操作

(1)立即读方式

流程:

(2)指定地址读方式

流程:

(3)连续读方式

补充:

六、参考代码

1. at24c02.h

2. at24c02.c

3. 工程地址


一、初识AT24C02

1. 介绍

        AT24C02是 低工作电压的 2Kb 串行电可擦除 只读存储器,可存储 256个字节 数据,内部有一个 16字节页写缓冲器。AT24C02工作电压 1.8~5.5V,采用二线制 IIC数据传输协议,支持硬件写保护,能擦写 100万次,数据可保存 100年。

        通过器件地址输入端 A0、A1和 A2可以实现将 最多 8个 AT24C02器件 连接到 IIC总线上

     补:EEPROM 是一种 掉电后数据不丢失的储存器,常用来存储一些配置信息,在系统重新上电时就可以加载。 ​​​​


详细描述可以参考:  

      

    常用存储器http://t.csdnimg.cn/TyKsuicon-default.png?t=N7T8http://t.csdnimg.cn/TyKsu

2. 引脚功能

补:

    ① A2、A1、A0:器件地址输入引脚。AT24C02 在一个总线上最多可寻址 8个器件,A2、A1 和 A0 必须连接。

    ② SDA:可实现 双向串行数据传输。该引脚为 开漏输出,可与其他 多个开漏输出器件 或开集电极器件线 或连接。

    ③ SCL:在 SCL输入时钟信号的 上升沿将数据送入 EEPROM器件,并在时钟的 下降沿将数据读出。

    ④ VCC、GND:2~6V 电源输入。

二、AT24C02组成

1. 存储结构

        AT24C02的存储容量为 256 Byte,由 32页 组成且每页8 Byte

2. AT24C02通讯地址

        主器件通过 发送一个 起始信号 启动发送过程,然后 发送它所要 寻址的 从器件的地址。高 4位 固定为 1010,接下来的 3位 A2、A1、A0为器件 的地址位(由器件 1~3引脚外接电平决定),用来 寻址哪个器件 被主器件访问,8位 地址的 最低位作为读写控制位( 1表示对 从器件进行读操作,0 表示对从器件 进行写操作 )。

        在主器件 发送起始信号 和从器件地址字节后,AT24C02监视 总线并当 其地址与发送的从地址相符时 响应一个 应答信号(通过SDA线),再根据 读写控制位 R / W 的状态进行 读或写操作

(1)不可编程部分:1010。

(2)可编程部分:由硬件管脚 A0 / 1 / 2决定。

(3)数据传输方向:读数据‘1’还是写数据‘0’。写操作地址:0xA0    读操作地址:0xA1

3. AT24C02寻址方式

        AT24C02有两种寻址方式:芯片寻址、片内子地址寻址

(1)芯片寻址

        AT24C02芯片地址固定为1010,它是 IIC总线器件的 特征编码,其地址控制字的格式为 1010 A2 A1 A0 R/WA2 A1 A0 引脚接高、低电平后得到 确定的 3位编码,与1010 形成的 7位编码,即为 该器件的地址码。由于 A2 A1 A0共有 8种组合,故系统最多可外接 8片 AT24C02R / W是 对芯片的 读 / 写控制位

(2)片内子地址寻址

        确定AT24C02芯片的 7位地址码 后,片内的存储空间 可用 1字节的 地址码寻址,寻址范围为00H~FFH,可对片内的 256个单元进行 读 / 写操作。

三、IlC通信部分规则

        1. 启动信号和停止信号 只能由主机发起

        2. IIC通信中 SCL 永远由主机控制SDA 由发送方控制(发送方可以是主机也可以是从机,接收方同理)。

        3. 接收方在收到 8 bit 数据 后必须 发送一个 应答信号给 发送方。

        4. 8 bit 数据位是 高位先发,因为是 串行通信,所以 接收方也是高位先收

        5. IIC 在收发 8 bit 数据过程中:

            ① 发送方只能在 SCL的 低电平期间 改变 SDA电平 以发送数据

            ② 接收方只能在 SCL的 高电平期间 读取 SDA电平 以获取数据

四、AT24C02时序

1. 总线空闲

        数据线 和 时钟线同时为 高电平

2. 启动数据传输

        时钟(SCL)为高电平时,SDA 从高电平变为低电平表示 起始条件产生。起始条件 必须 先于所有的命令 产生

3. 停止数据传输

        时钟 (SCL)为高电平时,SDA 从低电平变为 高电平表示 停止条件产生。所有操作都必须 以停止条件结束

4. 数据有效

        数据线的状态 表明数据 何时有效。在起始条件之后,数据线在 时钟处于高电平期间 保持稳定。必须在 时钟信号为 低电平期间 改变数据线。一个数据位对应 一个时钟 脉冲。

        数据的 每次传输以起始条件开始,以 停止条件结束。在 起始条件和停止条件之间传输的数据字节数目 由主器件决定

5. 确认时序

五、AT24C02读写时序

1. 写操作

        AT24C02有两种写入方式:字节写入方式 和 页写入方式。字节写模式 就是一个地址一个数据进行写入。页写模式 就是连续写入数据。

        只需要写一个地址,连续写入数据时 地址会自增,但存在 页的限制超出一页时,超出数据覆盖原 先写入的数据。但 读会自动翻页

(1)字节写入方式

时序简单描述:        

        字节写操 作以来自于主器件的 起始位开始,位 控制码 紧随其后。接下来的 3 位是存储块寻址位(不带地址输入引脚的器件)或 止选位(带地址输入引脚的器件)。然后 主发送器将 R / W (该位为逻辑低电平)发送到总线。从器件在 第九个 时钟周期产生一个 确认位

        单片机(主器件)先发送启动信号和 1 字节的控制字,从器件 发出应答信号后,单片机再发送 1 字节 的存储单元子地址(AT24C02芯片内部 单元的地址码),单片机收到 AT24C02应答后,再发送 8 位 数据 和 1 位终止信号

流程:

    ① 主器件 发送起始命令 和 从器件地址信息(R / W 位置0),等待 从器件应答。

    ② 主器件 发送 一个地址字节( 数据要写入的 AT24C02 地址单元 ),等待 从器件应答。        

    ③ 主器件 发送待 写数据字节,等待 从器件应答。

    ④ 主器件 发送停止信号后,AT24C02 开始 内部数据的 擦写,在内部擦写过程中,AT24C02 不再应答 主器件的 任何请求。

(2)页写入方式

        单片机先发送启动信号和 1 字节 的控制字,再发送1字节的 存储器起始单元地址,上述几字节都得到 AT24C02的 应答后,就可以发送最多 1页的数据,即一次写入最多 16个字节的数据至 AT24C02。这些数据字节临时存储在片 内页缓冲器中。在主器件发送 停止条件之后,这些数据将被 写入存储器

        数据页写操作的 启动和字节 写一样,不同在于 传送了1 字节数据 后并不产生 停止信号。主器件可以发送 15个额外的字节,每发送一个字节数据后,AT24C02 产生一个 应答位并将字节 地址低 4 位 加 1( 1111 加 1 变成 0000 ),高 4 位 保持不变。同时数据 顺序存放在由 已指定的起始地址开始的 相继单元中

        如果在 发送停止信号 之前 主器件 发送超过 16个 字节数据,先前 写入的 数据将被 顺序覆盖,AT24C02 只保留 最后传输的 16个字节 数据。

        主器件发送停止信号后,AT24C02 启动内部写周期,将数据写到数据区。所有接收的数据在一个写周期内 写入,写一个字节与同时写 16个 字节的 时间相同,整个写周期大约需要 10ms。

2. 读操作

        AT24C02的读操作也有三种方式:立即读方式、指定地址读方式和指定地址连续读方式

(1)立即读方式

        AT24C02的 地址计数器内容为 最后操作字节 的地址加 1。也就是说,如果上次 读 / 写 的操作地址为 N,则立 即读的地址 从地址 N+1 开始。如果 N=255,则地址计数器 将翻转到 0 且继续输出数据。

流程:

    ① 主器件 发送起始命令 和 从器件地址信息( R / W 位置 1 )。

    ② AT24C02 发送 应答信号,应答 后发送一个 8位 字节数据

    ③ 主器件 发送 停止信号,结束 读操作。

(2)指定地址读方式

        单片机 发送启动信号后,先 发送含有芯片 地址的 写操作控制字,AT24C02 应答后,单片机再发送 1 字节 的指定单元 的地址,AT24C02 应答后再发送 1个 含有芯片 地址的 读操作 控制字,此时如果 AT24C02 做出应答,被访问 单元的数据就 会按 SCL信号同步 出现在SDA线上,供单片机 读取。

流程:

    ① 主器件 发送起始命令 和 从器件地址信息( R / W 位置 0 ),等待 从器件应答。

    ② 主器件 发送一个 地址字节( 要读的数据 地址单元 ),等待 从器件应答。

    ③主器件 重发起始 信号,执行一次 立即 地址读操作。

(3)连续读方式

        连续读操作可通过 立即读 或 指定地址读操作启动,在 AT24C02 发送完一个 8 位 字节数据后,主器件 产生一个 应答信号来 响应,告知 AT24C02 主器件要求 更多的数据,只有 AT24C02检测到应答信号后,其 内部的 地址寄存器 就自动加 1 指向 下一个单元,并顺序 将指向 单元的数据送到 SDA 线上。对应 每个主机产生的 应答信号,AT24C02 将发送 一个数据字节,当 主器件不发送应答信号而 发送停止位 时结束此操作。

        从 AT24C02 输出的数据按顺序由 N 到 N+1 输出。读操作时,地址计数器在 AT24C02 整个地址内 增加,这样 整个寄存器区域 可在一个读操作内全部读出当前读取的 字节地址为255时,地址计数器 将翻转到 0 并继续 输出 数据字节。

补充:

        在 IIC总线 上每次传送的 数据字节数不限,但 每一个字节必须为 8 位,而且 每个传送的字节 后面必须 跟一个 应答信号每次 都是先传 最高位,通常 从器件在 接收到 每个字节后都会 做出响应,即释放 SCL线 返回高电平,准备 接收下一个 数据字节,主器件 可继续 传送。

        如果 从器件 正在处理一个 实时事件而 不能接收数据 时,可以 使时钟线 SCL 保持 低电平,从器件 必须使 SDA 保持高电平,此时 主器件产生 1 个 结束信号,使传 送异常结束,迫使主器件 处于 等待状态。当 从器件处理 完毕时将 释放 SCL 线,主器件 继续 完成传送。

六、参考代码

    注:代码可能有些许问题,望大佬们指点一二。

1. at24c02.h

/*
*********************************************************************************************************
*
*	模块名称 : at24c02驱动文件
*	文件名称 : at24c02.h
*
*********************************************************************************************************
*/
#ifndef __AT24C02_H
#define __AT24C02_H

#include "main.h"


#define AT24CXX_MAX_SIZE   256
#define AT24CXX_PAGE_SIZE  8
#define AT24CXX_PAGE_TOTAL (AT24CXX_MAX_SIZE/AT24CXX_PAGE_SIZE)


#define AT24C02_SlaveAddress  0xa0      // 从机地址

#define IIC_READ_SCL HAL_GPIO_ReadPin(AT24C02_SCL_GPIO_Port, AT24C02_SCL_Pin)
#define IIC_READ_SDA HAL_GPIO_ReadPin(AT24C02_SDA_GPIO_Port, AT24C02_SDA_Pin)
#define AT24C02_SCL(x) ((x==1) ? (AT24C02_SCL_GPIO_Port->BSRR=AT24C02_SCL_Pin) : (AT24C02_SCL_GPIO_Port->BRR=AT24C02_SCL_Pin))
#define AT24C02_SDA(x) ((x==1) ? (AT24C02_SDA_GPIO_Port->BSRR=AT24C02_SDA_Pin) : (AT24C02_SDA_GPIO_Port->BRR=AT24C02_SDA_Pin))


HAL_StatusTypeDef AT24C02_IsConnected(void);
void AT24C02_IIC_SendByte(uint8_t byte);
uint8_t AT24C02_IIC_RecByte(void);
void AT24C02_Write_Data(uint8_t address, uint8_t byte);
uint8_t AT24C02_Read_Data(uint8_t address);


int AT24C02_write(uint8_t addr, uint8_t* dataPtr, uint16_t dataSize);
int AT24C02_read(uint8_t addr, uint8_t* dataPtr, uint16_t dataSize);

#endif

2. at24c02.c

/*
*********************************************************************************************************
*
*	模块名称 : at24c02驱动文件
*	文件名称 : at24c02.c
*
*********************************************************************************************************
*/
#include "at24c02.h"
#include "i2c.h"


void AT24C02_IIC_Delay(uint16_t us)
{    
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000000);
    HAL_Delay(us - 1);
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
}

/**
 * @brief	IIC 总线连接检查
 *
 * @return  HAL_StatusTypeDef
 */
HAL_StatusTypeDef AT24C02_IsConnected(void)
{
    return HAL_I2C_IsDeviceReady(&hi2c1, AT24C02_SlaveAddress, 1, 50);
}

/**
 * @brief	IIC 总线启动信号
 *
 * @return  void
 */
void AT24C02_IIC_Start(void)
{
    AT24C02_SDA(1);
    AT24C02_SCL(1);
    AT24C02_IIC_Delay(20);
    
    AT24C02_SDA(0);
    AT24C02_IIC_Delay(5);
    AT24C02_SCL(0);
}    

/**
 * @brief	IIC 总线停止信号
 *
 * @return  void
 */
void AT24C02_IIC_Stop(void)
{
    AT24C02_SDA(0);
    AT24C02_SCL(0);
    AT24C02_IIC_Delay(5);
    
    AT24C02_SCL(1);
    AT24C02_IIC_Delay(5);
    AT24C02_SDA(1);
}

/**
 * @brief	发送应答信号或非应答信号
 *
 * @param   ack   0:应答 / 1:非应答
 *
 * @return  void
 */
void AT24C02_IIC_SendAck(uint16_t ack)
{
    AT24C02_SCL(0);
    AT24C02_SDA(ack);
    AT24C02_IIC_Delay(5);
    AT24C02_SCL(1);
    AT24C02_IIC_Delay(5);
    
    AT24C02_SCL(0);
    AT24C02_SDA(1);        // 接收器释放SDA
    AT24C02_IIC_Delay(5);
}

/**
 * @brief	接收应答信号或非应答信号
 *
 * @return  uint16_t  0:应答 / 1:非应答
 */
uint16_t AT24C02_IIC_WaitAck(void)
{
    uint16_t ack;
    AT24C02_SDA(1);        // 发送器释放SDA
    AT24C02_SCL(1);
    AT24C02_IIC_Delay(5);
    
    ack = IIC_READ_SDA;
    AT24C02_SCL(0);
    AT24C02_IIC_Delay(5);
    return ack;
}


/**
 * @brief	发送一个字节
 *
 * @param   byte   1 字节数据
 *
 * @return  void
 */
void AT24C02_IIC_SendByte(uint8_t byte)
{
    AT24C02_SCL(0);
    
    for(int i = 0; i < 8; i++)
    {
        AT24C02_IIC_Delay(2);
        AT24C02_SDA( ((byte>>(7-i)) & 0x01) );     // 获取最高位
        AT24C02_IIC_Delay(3);
        
        AT24C02_SCL(1);
        AT24C02_IIC_Delay(5);
        AT24C02_SCL(0);
    }
    
    AT24C02_IIC_WaitAck();      // 等待应答信号
}


/**
 * @brief	接收一个字节
 *
 * @return  uint8_t
 */
uint8_t AT24C02_IIC_RecByte(void)
{
    uint8_t data;
    
    AT24C02_SDA(1);
    
    for(int i = 0; i < 8; i++)
    {
        AT24C02_SCL(1);
        AT24C02_IIC_Delay(5);
        data <<= 1;
        if(IIC_READ_SDA)
            data |= 1;
        
        AT24C02_SCL(0);
        AT24C02_IIC_Delay(5);
    }
    
    AT24C02_IIC_SendAck(0);     // 发送应答信号
    return data;
}


/**
 * @brief	向 AT24C02的地址address中 写入一个字节数据
 *
 * @param   byte   1 字节数据
 *
 * @return  void
 */
void AT24C02_Write_Data(uint8_t address, uint8_t byte)
{
    AT24C02_IIC_Start();
    AT24C02_IIC_SendByte(AT24C02_SlaveAddress);
    AT24C02_IIC_WaitAck();      // 等待应答信号
    AT24C02_IIC_SendByte(address);
    AT24C02_IIC_WaitAck();
    
    AT24C02_IIC_SendByte(byte);
    AT24C02_IIC_WaitAck();
    AT24C02_IIC_Stop();
}


/**
 * @brief	向 AT24C02的地址address中 读出一个字节数据
 *
 * @param   byte   1 字节数据
 *
 * @return  uint8_t
 */
uint8_t AT24C02_Read_Data(uint8_t address)
{
    uint8_t data;
    
    AT24C02_IIC_Start();
    AT24C02_IIC_SendByte(AT24C02_SlaveAddress);
    AT24C02_IIC_WaitAck();      // 等待应答信号
    AT24C02_IIC_SendByte(address);
    AT24C02_IIC_WaitAck();
    
    AT24C02_IIC_Start();
    AT24C02_IIC_SendByte(AT24C02_SlaveAddress + 1);
    AT24C02_IIC_WaitAck();
    data = AT24C02_IIC_RecByte();
    AT24C02_IIC_WaitAck();
    AT24C02_IIC_Stop();
    
    return data;
}



/**
 * @brief	向 AT24C02的地址address中 存储数据
 *
 * @param   addr        存储器地址
 * @param   dataPtr     待存储数据
 * @param   dataSize    存储数据长度
 *
 * @return  uint8_t
 */
int AT24C02_write(uint8_t addr, uint8_t* dataPtr, uint16_t dataSize)
{
    if (0 == dataSize) { return -1; }
    
    int res = HAL_OK;
    
    int selectPage_idx  = addr % AT24CXX_PAGE_SIZE;
    int selectPage_rest = AT24CXX_PAGE_SIZE - selectPage_idx;
    
    if (dataSize <= selectPage_rest) {
        res = HAL_I2C_Mem_Write(&hi2c1, 
                                 AT24C02_SlaveAddress, 
                                 addr,
                                 I2C_MEMADD_SIZE_8BIT, 
                                 dataPtr,
                                 dataSize,
                                 0xFF);
        
        if (HAL_OK != res) { return -1; }
        
        HAL_Delay(10);
        
    } else {
    
        /*! 1 write selectPage rest*/
        res = HAL_I2C_Mem_Write(&hi2c1, 
                                 AT24C02_SlaveAddress, 
                                 addr,
                                 I2C_MEMADD_SIZE_8BIT, 
                                 dataPtr,
                                 selectPage_rest,
                                 0xFF);
        
        if (HAL_OK != res) { return -1; }
        
        addr     += selectPage_rest;
        dataSize -= selectPage_rest;
        dataPtr  += selectPage_rest;
        
        AT24C02_IIC_Delay(5);
        
        /*! 2 write nextPage full */
        int fullPage = dataSize/AT24CXX_PAGE_SIZE;
        for (int iPage = 0; iPage < fullPage; ++iPage) {
            res = HAL_I2C_Mem_Write(&hi2c1, 
                                     AT24C02_SlaveAddress, 
                                     addr,
                                     I2C_MEMADD_SIZE_8BIT, 
                                     dataPtr,
                                     AT24CXX_PAGE_SIZE,
                                     0xFF);
            
            if (HAL_OK != res) { return -1; }
            
            HAL_Delay(5);
            
            addr     += AT24CXX_PAGE_SIZE;
            dataSize -= AT24CXX_PAGE_SIZE;
            dataPtr  += AT24CXX_PAGE_SIZE;
        }
        
        /*! 3 write rest */
        if (0 != dataSize) {
            res = HAL_I2C_Mem_Write(&hi2c1, 
                                     AT24C02_SlaveAddress + 1, 
                                     addr,
                                     I2C_MEMADD_SIZE_8BIT, 
                                     dataPtr,
                                     dataSize,
                                     0xFF);
        
            if (HAL_OK != res) { return -1; }
            
            HAL_Delay(5);
        }
    }
    
    return 0;
}


/**
 * @brief	向 AT24C02的地址address中 读出数据
 *
 * @param   addr        存储器地址
 * @param   dataPtr     待接收数据
 * @param   dataSize    存储数据长度
 *
 * @return  uint8_t
 */
int AT24C02_read(uint8_t addr, uint8_t* dataPtr, uint16_t dataSize)
{
    int res = HAL_I2C_Mem_Read(&hi2c1,
                                AT24C02_SlaveAddress + 1,
                                addr,
                                I2C_MEMADD_SIZE_8BIT,
                                dataPtr,
                                dataSize,
                                0xFF);
    
    if (HAL_OK != res) { return -1; }
    
    return 0;
}

3. 工程地址

3_EEPROM_AT24C02 · CXDNW/STM32F103C8T6练习项目 - 码云 - 开源中国 (gitee.com)


IIC学习可参考这篇文章:

    嵌入式—— IIC

http://t.csdnimg.cn/npQnHicon-default.png?t=N7T8http://t.csdnimg.cn/npQnH

  • 37
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
EEPROM存储器AT24C02驱动是一种用于控制和操作AT24C02 EEPROM芯片的软件程序。AT24C02是一种2K位串行电路编程只读存储器(EEPROM),能够以字节为单位进行读写操作。下面是一个简单的EEPROM存储器AT24C02驱动的实现示例: 首先,需要确定与AT24C02芯片通信的硬件接口。一般来说,AT24C02芯片使用I2C(TWI)总线进行通信。因此,需要确保控制器上的I2C总线控制器(如I2C模块)已正确配置和初始化。 其次,需要实现与AT24C02芯片通信的相关函数,如写入数据、读取数据等。这些函数可以通过在控制器上通过I2C总线传输控制字节和数据字节来执行相关的EEPROM操作。 例如,实现写入数据的函数,可以按照以下步骤进行: 1. 通过发出启动条件向AT24C02芯片发送设备地址。设备地址应包括芯片的固定地址和A0、A1和A2引脚的状态,用来识别芯片的物理位置。 2. 发送一个字节的内存地址,确定要写入数据的EEPROM存储器地址。 3. 发送要写入的数据字节。 4. 等待写操作完成,可以通过轮询芯片或等待I2C总线中断来检查。 5. 发送停止位,结束写操作。 同样,读取数据的函数也可以按照类似的步骤进行实现,只不过在发送内存地址之后需要切换到读操作模式,并在读取数据后保存数据字节。 在驱动程序的应用中,可以使用这些函数来存储和检索数据。通过提供适当的地址,可以将数据写入或读取到AT24C02芯片的特定地址中。此外,除了基本的读写功能之外,还可以实现其他操作,如块写入、页写入等。 总之,EEPROM存储器AT24C02驱动程序通过I2C总线控制器与AT24C02芯片进行通信,实现了对EEPROM芯片的读写操作。驱动程序应提供适当的函数来进行数据存储和检索,并可以根据应用需求扩展其他功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值