EEPROM 之 AT24C16 - 备忘录

article/2025/10/9 10:44:48

因为论坛里看到STM的I2C有点小bug,所以这里采用的是模拟I2C时序

【注】m0.6us表示的是这一段时间最小不能小于为0.6us,M0.6us表示的是这一段时间最大为0.6us

 对AT24C16的操作有读和写,读又分为CURRENT ADDRESS READ、RANDOM READ、SEQUENTIAL READ
,写又分为BYTE WRITE、PAGE WRITE。

WRITE

先研究写操作

BYTE WRITE

先研究写操作中的 Byte Write,它的时序如下

下面分步实现 BYTE WRITE。

首先是 START 和 STOP,具体时序如下

其中的时间要求如下(下图是START为例)

程序实现:

/******************************************
函数名:START
描 述 :产生起始条件,在SCL高电平期间,SDA从高到低表示通信开始
*******************************************/
static void START(void)
{SCL_H;SDA_H;	        /* 为产生 START 做铺垫 */I2C_delay(1);   /* tSU.STA=m0.6us */SDA_L;          /* SDA线从高到低,START */I2C_delay(1);   /* tHD.STA=m0.6us */SCL_L;
}/******************************************
函数名:STOP
描 述 :产生结束命令
*******************************************/
static void STOP(void)
{SCL_L;SDA_L;          /* 为产生 STOP 做铺垫 */I2C_delay(1);SCL_H;I2C_delay(1);   SDA_H;          /* SDA线从低到高,STOP */I2C_delay(1);SCL_H;
}

接下来就是发送 DEVICE ADDRESS,因为DEVICE ADDRESS、WORD ADDRESS以及DATA的发送都是一样的操作,所以就写了一个统一的函数——SendByte,其操作具体的一个位时序如下

程序实现如下

/******************************************
函数名:SendByte
描 述 :发送一字节数据,数据位从高位到低位顺序发送
输 入 :-sendbyte:要发送的字节
*@nate:
在进入SendByte函数的时候,会先把SCL拉低
在退出SendByte函数的时候,也会把SCL拉低
*******************************************/
static ErrorStatus SendByte(u8 sendbyte) 
{u8 i,data;data=sendbyte;for(i=0;i<8;i++){SCL_L;I2C_delay(1); //tHD.DAT=m0usif((data&0x80)==0x80){//高位优先发送SDA_H;            //1}else{SDA_L;}I2C_delay(1);//tSU.DAT=m100nsSCL_H;I2C_delay(1);data=data<<1;}/** 第9个周期,读取ACK **/SCL_L;SDA_H;//先把内部的输出置高,后读取I2C_delay(1); SCL_H;//SCL=1期间是DATA STABLE,也就是数据有效期I2C_delay(1);if(READ_SDA==1){SCL_L;//出去前先把SCL拉低总没错return ERROR;}else {//ACK=0SCL_L;//出去前先把SCL拉低总没错return SUCCESS;}
}

所以 BYTE WRITE的程序就简单了

/******************************************
函数名:EE_ByteWrite
描 述 :写入一个字节到存储器里
输 入 :deviceAddr:器件地址,3位,0-7wordAddr  :数据字地址data      :要写入的数据
输 出 :无
返回值:是否成功写入
*@nate:tWR=M5ms,所以两次写操作之间,要间隔5ms以上
*******************************************/
ErrorStatus EE_ByteWrite(u8 deviceAddr,u8 wordAddr,u8 data)
{u8 deviceaddr;deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_WRITE;START();             //STARTSendByte(deviceaddr);//DEVICE ADDRESSSendByte(wordAddr);  //WORD ADDRESSSendByte(data);      //DATASTOP();              //STOPI2C_delay(5000);     //tWR=M5ms return SUCCESS;
}

PAGE WRITE

PAGE WRITE 时序如下

程序:

/******************************************
函数名:EE_PageWrite
描 述 :写入一页的数据(一页16个字节)
输 入 :deviceAddr:器件地址,3位,0-7wordAddr  :数据字地址*data     :要写入的数据首地址cnt       :要写入的数据字节数
输 出 :无
返回值:无
*@nate:The address “roll over” during write is from the lastbyte of the current page to the first byte ofthe same page.
*******************************************/
ErrorStatus EE_PageWrite(u8 deviceAddr,u8 wordAddr,u8 *data,u8 cnt)
{u8 deviceaddr,i=0;deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_WRITE;START();SendByte(deviceaddr);SendByte(wordAddr);for(i=0;i<cnt;i++){SendByte(data[i]);}STOP();I2C_delay(5000);     //tWR=M5ms return SUCCESS;
}

是不是有点奇怪BYTE WRITE 和 PAGE WRITE 后的“tWR=M5ms”?

Note: 1. The write cycle time tWR is the time from a valid stop condition of a write sequence to the end of the internal clear/write cycle.

READ

再研究读操作

/******************************************
函数名:EE_CurrentAddrRead
描 述 :产生起始条件,在SCL高电平期间,SDA从高到低表示通信开始
输 入 :deviceAddr:器件地址,3位,0-7wordAddr  :数据字地址
输 出 :无
返回值:无
*******************************************/
u8 EE_CurrentAddrRead(u8 deviceAddr)
{u8 deviceaddr,data;deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_READ;START();SendByte(deviceaddr);data=ReadByte(DISABLE);STOP();return data;
}

/******************************************
函数名:EE_RandomRead
描 述 :产生起始条件,在SCL高电平期间,SDA从高到低表示通信开始
输 入 :deviceAddr:器件地址,3位,0-7输 出 :无
返回值:无
*******************************************/
u8 EE_RandomRead(u8 deviceAddr,u8 wordAddr)
{u8 deviceaddr,data;deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_WRITE;START();SendByte(deviceaddr);SendByte(wordAddr);deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_READ;START();SendByte(deviceaddr);data=ReadByte(DISABLE);STOP();return data;
}

/******************************************
函数名:EE_SequentialRead
描 述 :顺序读取(一页16个字节)
输 入 :deviceAddr:器件地址,3位,0-7输 出 :无
返回值:无
*@nate:The address “roll over” during read is from the last byte of the last memory page to the first byte of the first page
*******************************************/
u8 EE_SequentialRead(u8 deviceAddr,u8 wordAddr,u8* dataAddr,u8 cnt)
{u8 deviceaddr,i;deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_WRITE;START();SendByte(deviceaddr);SendByte(wordAddr);deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_READ;START();SendByte(deviceaddr);for(i=0;i<cnt-1;i++){dataAddr[i]=ReadByte(ENABLE);}dataAddr[i]=ReadByte(DISABLE);STOP();return 0;
}

 

 

 

 

源代码:http://www.openedv.com/forum.php?mod=viewthread&tid=288334&page=1&extra=#pid931498

Q:STM32的引脚怎么才能既输入又输出呢,另外从SDA引脚读取数据时,需不需要先输出高电平再读取数据

A:STM32的引脚怎么才能即输入又输出呢

https://blog.csdn.net/qq_35629563/article/details/87710101

Q:1K的存储器能存多少东西,怎么算的?

A:首先:1k= 2^10 bit

芯片手册上这样写的:

The AT24C01A provides 1024 bits of serial electrically erasable and rogrammable read-only memory (EEPROM) organized as 128 words of 8 bits each.
 

AT24C01A, 1K SERIAL EEPROM: Internally organized with 16 pages of 8 bytes each,the 1K requires a 7-bit data word address for random word addressing.

计算过程:
16页8字节
16= 2^4,  8= 2^3, 1 byte= 2^3 bit
16页8字节 = 2^7字节 = 2^10 bit

所以芯片上的1K是1k个bit,1K能存放128个字节

 Q:page write先写的是低字节还是高字节,Sequential Read先读的是低字节还是高字节?

A:都是先对低字节的数据进行操作的


http://chatgpt.dhexx.cn/article/Fk5JZhVO.shtml

相关文章

S32K144:12.LPI2C驱动AT24C16

1.打开官方例程 2.修改引脚配置 3.时钟可按照实际情况修改&#xff0c;也可不用更改&#xff0c;本例时钟不做更改 4.配置LPI2C模块 设置从机地址&#xff1a;从机地址如下图所示&#xff0c;低三位表示为AT24C16的块地址&#xff0c;AT24C16将2KB的内存空间分为8个块&…

stm32cubemx I2C读取AT24C16

本文对如何使用stm32cube生成I2C工程不作说明&#xff0c;仅对在对AT24Cxx系列的使用时作出易忽略的说明&#xff1b; 1、at24cxx页面结构&#xff1a; 从该图可以看出16K&#xff08;bit&#xff09;共有128个页&#xff0c;每页由16byte构成。16k 128 * 16 * 8; 特别注意&…

STM32之 AT24C16(EEPROM)驱动代码(程序稳定,清晰明了)

AT24C16电路图 第一部分&#xff1a;IIC协议代码头文件(iic.h) #ifndef IIC_H #define IIC_H #include "stm32f10x.h" #include "sys.h" #include "delay.h"#define write 0 #define read 1//IIC总线地址接口定义 #define IIC_SCL PBout(7) #d…

GD32F4xx MCU控制I2C EEPROM(AT24C16)记录

1、AT24C16简介 1.1 主要参数 工作电压:1.8v ~ 5.5v存储空间:2048 Bytes ,分128页,16Bytes/页, 地址范围 0~2047。接口: I2C 总线I2C时钟频率: 1MHz( 5v ) , 400KHz( 1.8v, 2.5v, 2.7v)。1.2 电路连接 1.3 其他说明 AT24C16未使用器件地址引脚,总线上最多只可以连接一…

AT24C04、AT24C08、AT24C16系列EEPROM芯片单片机读写驱动程序

一、概述 在之前的一篇博文中&#xff0c;记录了AT24C01、AT24C02芯片的读写驱动&#xff0c;先将之前的相关文章include一下&#xff1a; 1.IIC驱动&#xff1a;4位数码管显示模块TM1637芯片C语言驱动程序 2.AT24C01/AT24C02读写&#xff1a;AT24C01/AT24C02系列EEPROM芯片单…

IIC方式读驱动AT24C16芯片

闲来无事&#xff0c;找了块msp430的板子编写了个IIC驱动AT24C16的程序。 IIC作是一种简单&#xff0c;双向&#xff0c;同步的二进制总线&#xff0c;由SDA数据线和SCL时钟线组成&#xff0c;所有接到IIC总线上的各设备的SDA数据线都连接到总线的SDA数据线上&#xff0c;用来…

AT24C16页写和多页写

AT24C16 2K字节(存储内存) 128&#xff08;页面数&#xff09;* 16 &#xff08;每页的字节数&#xff09; 2^11 (寻址地址位数 11位)。 AT24C16有128(2^7128)页只需要7位地址&#xff0c;分为高3位和低4位&#xff0c;高3位在设备地址中&#xff0c;低4位在字地址中。 设备…

EEPROM(AT24C16)页写算法

1. 写在前面 学习单片机或者从事嵌入式开发的&#xff0c;对于EEPROM绝不会陌生&#xff0c;尤其的24系列的EEPROM很是经典&#xff0c;或者与此兼容的FRAM系列&#xff0c;如AT24C02、AT24C16、FM24C16等。 驱动起这个系列的EEPROM&#xff0c;可以说是没有任何难点&#xff0…

AT24C16 读写注意点

开篇一张时序图镇楼&#xff1a; 这篇文章介绍了AT24C16的页写、连续读、写保护功能&#xff1a;AT24C16 读写_D.luffy的博客-CSDN博客_at24c16 页写算法我是参考这篇文章的&#xff1a;https://acuity.blog.csdn.net/article/details/78550427?utm_ char ee_24clxx_writeby…

AT24C16 读写

at24c16 有8块 256字节组成&#xff0c;共2K字节16K bit I2C开始信号后&#xff0c;第一个字节为器件地址&#xff0c;由10103位块地址1位读写标志组成&#xff0c; 3位块地址刚好可以表示 8个块&#xff0c; 所以一次写完256字节&#xff0c;换到下一下块的时候&#xff0c;要…

进程间通信的方式(附代码分析)

进程间通信的方式 1. 进程间通信的几种方式 管道 比如 ls | grep 1;也就是将 进程 ls 拿到的结果作为 grep 1 这个进程的输入。实现了进程间的通信。 消息队列 消息队列就是我们的内核给我们创建的一种消息队列。我们可以往其中发送消息&#xff0c;也可以从其中接收消息。 …

linux进程--进程间通信方式(一)

一、多进程 首先&#xff0c;先来讲一下fork之后&#xff0c;发生了什么事情。 由fork创建的新进程被称为子进程&#xff08;child process&#xff09;。该函数被调用一次&#xff0c;但返回两次。两次返回的区别是子进程的返回值是0&#xff0c;而父进程的返回值则是新进程…

进程间通信的方式——信号、管道、消息队列、共享内存

进程间通信的方式——信号、管道、消息队列、共享内存 多进程&#xff1a; 首先&#xff0c;先来讲一下fork之后&#xff0c;发生了什么事情。 由fork创建的新进程被称为子进程&#xff08;child process&#xff09;。该函数被调用一次&#xff0c;但返回两次。两次返回的区别…

Android 进程间通信的几种实现方式

一、概述 由于应用程序之间不能共享内存。在不同应用程序之间交互数据&#xff08;跨进程通讯&#xff09;&#xff0c;在android SDK中提供了4种用于跨进程通讯的方式。这4种方式正好对应于android系统中4种应用程序组件&#xff1a;Activity、Content Provider、Broadcast和S…

进程间通信的方式及原理

# 进程间通信的方式 文章目录 # 进程间通信的方式消息队列使用步骤 管道 消息队列 信号 信号量 socket 消息队列 首先消息队列就是内核维护的一块链表区域&#xff0c;只要是有足够权限的进程都可以向队列中添加消息&#xff0c;只要是有读权限的进程都可以在里面拿出消息 克…

C | 进程间通信的方式

C | 进程间通信的方式 1.无名管道 无名管道是实现亲缘间进程通信的一种方式&#xff0c;属于半双工通信。 无名管道的实现是队列&#xff0c;不能使用lseek对读写指针偏移。 无名管道有两个端&#xff1a;数据流入端和数据流出端&#xff0c;也叫写端和读端。它们是两个固定…

面试题:进程间通信的方式

liunx六大进程间通信方式 管道&#xff0c;消息队列&#xff0c;共享内存&#xff0c;信号量&#xff0c;socket&#xff0c;信号&#xff0c;文件锁 1&#xff0c;管道 1&#xff0c;匿名管道&#xff1a; 概念&#xff1a;在内核中申请一块固定大小的缓冲区&…

进程间通信的几种方式浅谈(上)

程序员必须让拥有依赖关系的进程集协调&#xff0c;这样才能达到进程的共同目标。可以使用两种技术来达到协调。第一种技术在具有通信依赖关系的两个进程间传递信息。这种技术称做进程间通信&#xff08;interprocess communication&#xff09;。第二种技术是同步&#xff0c;…

进程间通信的几种方式

一、管道 在Linux 中&#xff0c;管道是一种使用非常频繁的通信机制。从本质上说&#xff0c;管道也是一种文件&#xff0c;但它又和一般的文件有所不同&#xff0c;管道可以克服使用文件进行通信的两个问题&#xff0c;具体表现如下所述。 • 限制管道的大小。实际上&#x…

【进程间通信】进程间通信方式汇总

个人主页&#xff1a;董哥聊技术 我是董哥&#xff0c;嵌入式领域新星创作者 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; 文章目录 1、管道模型1.1 匿名管道1.2 命名管道 2、消息队列2.1 创建消息队列2.2 发送消息2.3 接收消息 3、共…