问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

EEPROM原理与应用实践

创作时间:
作者:
@小白创作中心

EEPROM原理与应用实践

引用
CSDN
1.
https://m.blog.csdn.net/m0_58749949/article/details/144905380

EEPROM(电可擦写可编程只读存储器)是一种非易失性存储器,广泛应用于需要永久存储数据的电子设备中。本文将详细介绍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);  
        }  
    }  
}  

本文原文来自CSDN

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号