EEPROM

EEPROM介绍

EEPROM(Electrically Erasable Programmable Read-Only Memory,电可擦写可编程只读存储器)是一种非易失性存储器(断电后仍能保留数据),可以多次写入和擦除数据。EEPROM广泛应用于需要永久存储数据的电子设备中, 常用于存储设备工作模式、用户偏好设置等信息。

虽然叫做只读存储器(ROM),但EEPROM是即可读又可写的,这种看似矛盾的命名源于其历史发展。最早期的ROM是在制造过程中被一次性编程,出厂之后便只能读取,不能再修改,后期的各种可编程(可写)的ROM,都是基于最早期的ROM发展而来的,所以ROM这个名称就被一直沿用下来了。现在的ROM基本指非易失性存储器。

这里使用的EEPROM型号是AT24C02CN,其存储容量为2K(2048位,256字节),采用I2C协议进行读写。

引脚功能

在这里插入图片描述

内存结构

在这里插入图片描述

写操作

  • Byte Write
    Byte Write用于写入一个字节,其时序图如下。

在这里插入图片描述

注意
接收到停止信号后,EEPROM 就会开启内部的写周期,将刚刚接收到的一个字节的数据写入存储器的物理介质,在此期间,所有外部输入均会被拒绝,直到当前字节写入完成。这个写入周期的时间可从手册查询,通常用 t 表示。因此如果使用 Byte write 连续写入多个字节,在每个字节写入完成后,至少需要等待 t 之后才能写入下一字节。

  • Page Write
    Page Write用于连续写入一页数据,其时序图如下。

在这里插入图片描述

注意

  1. Page Write一次最多写入1页(16个字节)的数据
  2. Page Write会从主设备发送的字地址开始,逐字节顺序写入
  3. 当 Page write 写到页边界时,下一个字节不会进入下一页,而是会回到当前页的起始位置
  4. 当EEPROM在接收到最后的结束信号后,才会开启内部的写周期,将刚刚收到的一页数据统一写入存储 器的物理介质,在此期间,所有外部输入均会被拒绝,直接当前页写入完成。内部写周期的时长仍为 t

读操作

  • Current Address Read
    EEPROM内部有一个Address Register(地址寄存器),用于记录当前操作(读/写)的字节地址,每当完成一个字节的操作后(读/写),该地址会自动指向下一个字节。
    Current Address Read读取的就是Address Register中的地址所指向的这一个字节,具体读时序如下。

在这里插入图片描述

  • Random Read
    Random Read用于读取任意指定地址的一个字节,时序图如下。

在这里插入图片描述

  • Sequential Read
    Sequential Read用于读取连续的多个字节。其起始位置可以是Address Register记录的当前地址,也可以是用户指定的任意位置,指定起始位置的方式仍然是在Sequential Read前增加一个Dummy Write操作。下图是从指定位置开始的连续读操作的时序图。

在这里插入图片描述

注意
连续读时,读到页边界后,会自动进入下一页,若读到最后一页的页边界,则会进入第一页
页写时,当 Page write 写到页边界时,下一个字节不会进入下一页,而是会回到当前页的起始位置

原理图

在这里插入图片描述

代码实现

Int_EEPROM.h

#ifndef __INT_EEPROM_H__
#define __INT_EEPROM_H__
#include <STC89C5xRC.H>
#define DEV_ADDR  0xA0
#define PAGE_SIZE 8
#include "Dri_IIC.h"
#include "Com_Util.h"

/**
 * @brief 向EEPROM指定位置写入多个字节
 *
 * @param addr 起始地址
 * @param bytes 要写入的字节
 * @param len 要写入的字节个数
 * @return bit 0:写入成功 1:写入失败
 */
bit Int_EEPROM_WriteBytes(u8 addr, u8 *bytes, u8 len);

/**
 * @brief  从EEPROM指定位置读取多个字节
 *
 * @param addr 起始地址
 * @param bytes 存储读取的字节用的数组指针
 * @param len 要读取的字节个数
 * @return bit 0:读取成功 1:读取失败
 */
bit Int_EEPROM_ReadBytes(u8 addr, u8 *bytes, u8 len);

#endif /* __INT_EEPROM_H__ */

Int_EEPROM.c

#include "Int_EEPROM.h"

bit Int_EEPROM_WritePage(u8 addr, u8* bytes, u8 len) {
    u8 i;
    bit ack = 0;
    // 发送起始信号
    Dri_IIC_Start();

    // 发送设备地址
    Dri_IIC_SendByte(DEV_ADDR);
    ack |= Dri_IIC_ReceiveAck();

    // 发送字地址
    Dri_IIC_SendByte(addr);
    ack |= Dri_IIC_ReceiveAck();
    
    // 发送数据
    for (i = 0; i < len; i++) {
        Dri_IIC_SendByte(bytes[i]);
        ack |= Dri_IIC_ReceiveAck();
    }

    // 发送结束信号
    Dri_IIC_Stop();
    // 等待EEPROM内部写入完成
    Com_Util_Delay1ms(5);
    return ack;
}

bit Int_EEPROM_WriteBytes(u8 addr, u8* bytes, u8 len) {
    // 当前页剩余空间
    u8 page_remain;
    bit ack = 0;
    while (len > 0) {
        page_remain = PAGE_SIZE - (addr % PAGE_SIZE);
        if (len > page_remain) {
            // 当前页空间不足,先写满当前页
            ack |= Int_EEPROM_WritePage(addr, bytes, page_remain);
            // 剩余内容写到下一页
            len -= page_remain;
            bytes += page_remain;
            addr += page_remain;
        } else {
            // 当前页空间足够,直接写入剩余内容
            ack |= Int_EEPROM_WritePage(addr, bytes, len);
            len = 0;
        }
    }
    return ack;
}

bit Int_EEPROM_ReadBytes(u8 addr, u8* bytes, u8 len) {
    bit ack = 0;
    u8 i;
    // 发送起始信号
    Dri_IIC_Start();
    // 发送设备地址
    Dri_IIC_SendByte(DEV_ADDR);
    ack |= Dri_IIC_ReceiveAck();
    // 发送字地址
    Dri_IIC_SendByte(addr);
    ack |= Dri_IIC_ReceiveAck();
    // 再次发送起始信号
    Dri_IIC_Start();
    // 发送设备地址
    Dri_IIC_SendByte(DEV_ADDR + 1);
    ack |= Dri_IIC_ReceiveAck();
    // 读取数据
    for (i = 0; i < len; i++) {
        bytes[i] = Dri_IIC_ReceiveByte();
        Dri_IIC_SendAck(i == len - 1 ? 1 : 0);
    }
    // 发送结束信号
    Dri_IIC_Stop();

    return ack;
}

main.c

#include "Int_MatrixLED.h"
#include "Dri_Timer0.h"
#include "Int_EEPROM.h"


u8 pic[26] = {
    0xF8, 0x0A, 0xEC, 0xAF, 0xEC, 0x8A, 0xF8, 0x00,             // 尚
    0x10, 0xF9, 0x97, 0xF1, 0x88, 0xAA, 0xFF, 0xAA, 0x88, 0x00, // 硅
    0x14, 0x0A, 0xF5, 0x92, 0x92, 0xF5, 0x0A, 0x14,             // 谷
};


u8 tem[26];

void main() {

    u8 i = 0;
    Dri_Timer0_Init();
    Int_MatrixLED_Init();

    Int_EEPROM_WriteBytes(0, pic, 26);
    Int_EEPROM_ReadBytes(0, tem, 26);

    while (1) {
        for (i = 0; i < 26; i++) {
            Int_MatrixLED_ShiftPic(tem[i]);
            Com_Util_Delay1ms(200);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值