介绍

EEPROM作为掉电不丢失的储存器,在使用上还是比较广泛的,

其可重复擦写,电擦除,使用方便

这里主要是以AT24C02为例介绍

硬件设计




esp01s烧录失败_数据


esp01s烧录失败_嵌入式硬件_02


Pin Configurations


该电路图设置下,A0—A2都是接地,所以EEPROM的地址为0x1010 000.

SDA和SCL分别与I2C相连,二者数据交换。

WP是写保护端口,这里直接接地,就没有写保护的功能,当然这不是说写保护不重要,只是在此处教学没有太大的用途,要是想控制WP,那就使WP与GPIO相连。

协议

写入


esp01s烧录失败_stm32_03


又协议图可知,I2C协议就是这个,所以我们可以把I2C协议拉出来使用。

向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率。

字节写入的话,每写完一次就需要判断是否写入完成,这一很麻烦。

于是我们就采用突发写入,也就是连续写入,这样就能直接写入多个字节。

/*
*********************************************************************************************************
*    函 数 名: ee_ReadBytes
*    功能说明: 从串行EEPROM指定地址处开始读取若干数据
*    形    参:_usAddress : 起始地址
*             _usSize : 数据长度,单位为字节
*             _pReadBuf : 存放读到的数据的缓冲区指针
*    返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)
{
    uint16_t i;
    
    /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
    
    /* 第1步:发起I2C总线启动信号 */
    i2c_Start();
    
    /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR);    /* 此处是写指令 */
     
    /* 第3步:等待ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }

    /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
    i2c_SendByte((uint8_t)_usAddress);
    
    /* 第5步:等待ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }
    
    /* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
    i2c_Start();
    
    /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_RD);    /* 此处是读指令 */
    
    /* 第8步:发送ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }    
    
    /* 第9步:循环读取数据 */
    for (i = 0; i < _usSize; i++)
    {
        _pReadBuf[i] = i2c_ReadByte();    /* 读1个字节 */
        
        /* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
        if (i != _usSize - 1)
        {
            i2c_Ack();    /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
        }
        else
        {
            i2c_NAck();    /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
        }
    }
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return 1;    /* 执行成功 */

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return 0;
}
/*
*********************************************************************************************************
*    函 数 名: ee_ReadBytes
*    功能说明: 从串行EEPROM指定地址处开始读取若干数据
*    形    参:_usAddress : 起始地址
*             _usSize : 数据长度,单位为字节
*             _pReadBuf : 存放读到的数据的缓冲区指针
*    返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)
{
    uint16_t i;
    
    /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
    
    /* 第1步:发起I2C总线启动信号 */
    i2c_Start();
    
    /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR);    /* 此处是写指令 */
     
    /* 第3步:等待ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }

    /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
    i2c_SendByte((uint8_t)_usAddress);
    
    /* 第5步:等待ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }
    
    /* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
    i2c_Start();
    
    /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_RD);    /* 此处是读指令 */
    
    /* 第8步:发送ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }    
    
    /* 第9步:循环读取数据 */
    for (i = 0; i < _usSize; i++)
    {
        _pReadBuf[i] = i2c_ReadByte();    /* 读1个字节 */
        
        /* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
        if (i != _usSize - 1)
        {
            i2c_Ack();    /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
        }
        else
        {
            i2c_NAck();    /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
        }
    }
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return 1;    /* 执行成功 */

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return 0;
}

读取


esp01s烧录失败_嵌入式硬件_04


该读取方式为:读取当前地址数据,就是上次操作的地址数据。这样的读取方式很少。


esp01s烧录失败_stm32_05


读取数据的操作只需要多一步,那就使提前说好要读取的地址,之后就是想要读取几个就几个,但是不能超出储存器范围,否则会重复读取,也就是读取之前的数据。

/*
*********************************************************************************************************
*    函 数 名: ee_ReadBytes
*    功能说明: 从串行EEPROM指定地址处开始读取若干数据
*    形    参:_usAddress : 起始地址
*             _usSize : 数据长度,单位为字节
*             _pReadBuf : 存放读到的数据的缓冲区指针
*    返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)
{
    uint16_t i;
    
    /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
    
    /* 第1步:发起I2C总线启动信号 */
    i2c_Start();
    
    /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR);    /* 此处是写指令 */
     
    /* 第3步:等待ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }

    /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
    i2c_SendByte((uint8_t)_usAddress);
    
    /* 第5步:等待ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }
    
    /* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
    i2c_Start();
    
    /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_RD);    /* 此处是读指令 */
    
    /* 第8步:发送ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }    
    
    /* 第9步:循环读取数据 */
    for (i = 0; i < _usSize; i++)
    {
        _pReadBuf[i] = i2c_ReadByte();    /* 读1个字节 */
        
        /* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
        if (i != _usSize - 1)
        {
            i2c_Ack();    /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
        }
        else
        {
            i2c_NAck();    /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
        }
    }
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return 1;    /* 执行成功 */

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return 0;
}

/*
*********************************************************************************************************
*    函 数 名: ee_ReadBytes
*    功能说明: 从串行EEPROM指定地址处开始读取若干数据
*    形    参:_usAddress : 起始地址
*             _usSize : 数据长度,单位为字节
*             _pReadBuf : 存放读到的数据的缓冲区指针
*    返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)
{
    uint16_t i;
    
    /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
    
    /* 第1步:发起I2C总线启动信号 */
    i2c_Start();
    
    /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR);    /* 此处是写指令 */
     
    /* 第3步:等待ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }

    /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
    i2c_SendByte((uint8_t)_usAddress);
    
    /* 第5步:等待ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }
    
    /* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
    i2c_Start();
    
    /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
    i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_RD);    /* 此处是读指令 */
    
    /* 第8步:发送ACK */
    if (i2c_WaitAck() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }    
    
    /* 第9步:循环读取数据 */
    for (i = 0; i < _usSize; i++)
    {
        _pReadBuf[i] = i2c_ReadByte();    /* 读1个字节 */
        
        /* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
        if (i != _usSize - 1)
        {
            i2c_Ack();    /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
        }
        else
        {
            i2c_NAck();    /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
        }
    }
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return 1;    /* 执行成功 */

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
    /* 发送I2C总线停止信号 */
    i2c_Stop();
    return 0;
}


想要更加深入的学习,拿下offer