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

article/2025/10/9 12:03:13

一、概述

在之前的一篇博文中,记录了AT24C01、AT24C02芯片的读写驱动,先将之前的相关文章include一下:
1.IIC驱动:4位数码管显示模块TM1637芯片C语言驱动程序
2.AT24C01/AT24C02读写:AT24C01/AT24C02系列EEPROM芯片单片机读写驱动程序
本文记录分享AT24C04、AT24C08、AT24C16芯片的单片机C语言读写驱动程序。

二、芯片对比介绍

型号容量bit容量byte页数字节/页器件寻址位可寻址器件数WordAddress位数/字节数备注
AT24C044k5123216A2A149/1WordAddress使用P0位
AT24C088k10246416A2210/1WordAddress使用P0、P1位
AT24C1616k204812816-111/1WordAddress使用P0、P1、P2位

上表中的3款芯片,容量不同,均超过了256byte,这样的话,每个byte的地址会超过8bit,但在对byte寻址时,WordAddress的字节数仍然都是1,只不过位数不同,多出来的位数需要使用“页选择位”,即P0/P1/P2位。
在这里插入图片描述
器件地址如上图,与24C02有3个引脚作为硬件连接的地址不同,AT24C04、AT24C08、AT24C16分别有2、1、0个引脚作为硬件连接的地址,Px代表的位在寻址时被用作“页选择位”。
其他内容与AT24C01/AT24C02类似,不再赘述。

三、读写操作

3.1 写操作

3.1.1 Byte Write写一个字节

在这里插入图片描述
上图是x24C04(实际为BR24G04)的写单个字节的时序,可看出与x24c01/x24c02的写单个字节基本相同,不同的是SlaveAddress中只有A2、A1两位表示硬件地址,另外一位为P0,用来扩展内存字节的地址。x24C08则只有一位A2表示器件的硬件地址,页选择位有P1、P0两位,x24C16没有硬件地址位,也就是说使用x24C16只能在同一条IIC总线上连接1个器件,本来表示地址的3个bit全部用作“页选择位”P2、P1、P0。我们可以通过一些设置,将这3款芯片的读单字节的驱动程序统一起来。

3.1.2 Page Write写一页

在这里插入图片描述
上图是x24C04的页写时序,与x24C01、x24C02的也基本相同,仅红框中的部分有区别,和3.1.1中的写单个字节一样,器件地址只有2位,另一位为页选择位;x24C08、x24C16与此类似。

3.2 读操作

3.2.1 读任意地址

在这里插入图片描述
上图是x24C04的读任意地址时序,同样,读任意地址的时序与x24C01、x24C02的也基本相同,只是第一次发送的SlaveAddress包含页选择位P0-P2。

3.2.2 顺序读(页读)

在这里插入图片描述
上图是x24C04的顺序读即页读的时序,与前述类似,顺序读在发送SlaveAddress的时候,也会包含页选择位。

四、主要代码

4.1 宏定义

同上一篇一样,先对器件地址等信息进行宏定义,根据不同的器件进行条件编译:

#define READ_CMD				1
#define WRITE_CMD				0#define x24C04//器件名称,x24C04、x24C08或x24C16
#define DEV_ADDR				0xA0					//设备硬件地址#ifdef x24C04#define PAGE_NUM			32						//页数#define PAGE_SIZE			16						//页面大小(字节)#define CAPACITY_SIZE		(PAGE_NUM * PAGE_SIZE)	//总容量(字节)#define ADDR_BYTE_NUM		1						//地址字节个数
#endif#ifdef x24C08#define PAGE_NUM			64						//页数#define PAGE_SIZE			16						//页面大小(字节)#define CAPACITY_SIZE		(PAGE_NUM * PAGE_SIZE)	//总容量(字节)#define ADDR_BYTE_NUM		1						//地址字节个数
#endif#ifdef x24C16#define PAGE_NUM			128						//页数#define PAGE_SIZE			16						//页面大小(字节)#define CAPACITY_SIZE		(PAGE_NUM * PAGE_SIZE)	//总容量(字节)#define ADDR_BYTE_NUM		1						//地址字节个数
#endif

4.2 写单个字节(写任意地址)

发送起始信号–>发送器件地址(包含写入命令、页选择位)–>收到应答–>发送需要写入数据的地址(8bit)–>收到应答–>发送需要写入的数据–>收到应答–>发送停止信号

/******************************************************************************** 函数名:x24Cxx_WriteByte* 功  能:写一个字节* 参  数:u16Addr要写入的地址u8Data要写入的数据* 返回值:无* 说  明:无
*******************************************************************************/
void x24Cxx_WriteByte(uint16_t u16Addr, uint8_t u8Data)
{x24Cxx_WriteEnable();//使能写入IIC_Start();//起始信号IIC_WriteByte(DEV_ADDR | WRITE_CMD | (((uint8_t)((u16Addr >> 8) & 0x07)) << 1));//器件寻址+写+页选择位IIC_WaitAck();//等待应答IIC_WriteByte((uint8_t)(u16Addr & 0xFF));//只取地址的低字节,高字节如果有,已经按照页选择位处理过了IIC_WaitAck();//等待应答IIC_WriteByte(u8Data);IIC_WaitAck();//等待应答IIC_Stop();//停止信号x24Cxx_WriteDisble();//禁止写入
}

4.3 写一页

发送起始信号–>发送器件地址(包含写入命令、页选择位)–>收到应答–>发送需要写入数据的首地址低字节(8bit)–>收到应答–>发送需要写入的第1个数据–>收到应答–>发送需要写入的第2个数据–>收到应答…–>发送需要写入的第n个数据–>收到应答–>发送停止信号

/******************************************************************************** 函数名:x24Cxx_WritePage* 功  能:页写* 参  数:u16Addr要写入的首地址;u8Len写入数据字节数,最大为PAGE_SIZEpData要写入的数据首地址* 返回值:无* 说  明:最多写入1页,防止翻卷,如果地址跨页则去掉跨页的部分
*******************************************************************************/
void x24Cxx_WritePage(uint16_t u16Addr, uint8_t u8Len, uint8_t *pData)
{uint8_t i;x24Cxx_WriteEnable();//使能写入IIC_Start();//起始信号IIC_WriteByte(DEV_ADDR | WRITE_CMD | (((uint8_t)((u16Addr >> 8) & 0x07)) << 1));//器件寻址+写+页选择位IIC_WaitAck();//等待应答IIC_WriteByte((uint8_t)(u16Addr & 0xFF));//只取地址的低字节,高字节如果有,已经按照页选择位处理过了IIC_WaitAck();//等待应答if (u8Len > PAGE_SIZE)//长度大于页的长度{u8Len = PAGE_SIZE;}if ((u16Addr + (uint16_t)u8Len) > CAPACITY_SIZE)//超过容量{u8Len = (uint8_t)(CAPACITY_SIZE - u16Addr);}if (((u16Addr % PAGE_SIZE) + (uint16_t)u8Len) > PAGE_SIZE)//判断是否跨页{u8Len -= (uint8_t)((u16Addr + (uint16_t)u8Len) % PAGE_SIZE);//跨页,截掉跨页的部分}for (i = 0; i < u8Len; i++){IIC_WriteByte(*(pData + i));IIC_WaitAck();//等待应答}IIC_Stop();//停止信号	x24Cxx_WriteDisble();//禁止写入
}

4.4 读单个字节(读任意地址)

发送起始信号–>发送器件地址(包含写入命令、页选择位)–>收到应答–>发送需要读取数据的地址低字节–>收到应答–>发送起始信号–>发送器件地址(包含读取命令)–>收到应答–>读取数据–>不应答–>发送停止信号

/******************************************************************************** 函数名:x24Cxx_ReadByte* 功  能:读一个字节* 参  数:u16Addr要读取的地址* 返回值:u8Data读出的数据* 说  明:无
*******************************************************************************/
uint8_t x24Cxx_ReadByte(uint16_t u16Addr)
{uint8_t u8Data = 0;IIC_Start();//起始信号	IIC_WriteByte(DEV_ADDR | WRITE_CMD | (((uint8_t)((u16Addr >> 8) & 0x07)) << 1));//器件寻址+写+页选择位IIC_WaitAck();//等待应答IIC_WriteByte((uint8_t)(u16Addr & 0xFF));//只取地址的低字节,高字节如果有,已经按照页选择位处理过了IIC_WaitAck();//等待应答IIC_Start();//起始信号IIC_WriteByte(DEV_ADDR | READ_CMD);//器件寻址+读IIC_WaitAck();//等待应答u8Data = IIC_ReadByte();IIC_NoAck();IIC_Stop();//停止信号return u8Data;
}

4.5 读一页

发送起始信号–>发送器件地址(包含写入命令、页选择位)–>收到应答–>发送需要读取数据的首地址低字节–>收到应答–>发送起始信号–>发送器件地址(包含读取命令)–>收到应答–>读取第1个数据–>发送应答–>读取第2个数据–>发送应答…–>读取第n个数据–>不应答–>发送停止信号

/******************************************************************************** 函数名:x24Cxx_ReadPage* 功  能:页读* 参  数:u16Addr要读取的首地址;u8Len读取数据字节数,最大为PAGE_SIZEpBuff读取数据存入的缓存* 返回值:无* 说  明:最多读1页,防止翻卷,如果地址跨页则去掉跨页的部分
*******************************************************************************/
void x24Cxx_ReadPage(uint16_t u16Addr, uint8_t u8Len, uint8_t *pBuff)
{uint8_t i;	IIC_Start();//起始信号	IIC_WriteByte(DEV_ADDR | WRITE_CMD | (((uint8_t)((u16Addr >> 8) & 0x07)) << 1));//器件寻址+写+页选择位IIC_WaitAck();//等待应答IIC_WriteByte((uint8_t)(u16Addr & 0xFF));//只取地址的低字节,高字节如果有,已经按照页选择位处理过了IIC_WaitAck();//等待应答IIC_Start();//起始信号IIC_WriteByte(DEV_ADDR | READ_CMD);//器件寻址+读IIC_WaitAck();//等待应答if (u8Len > PAGE_SIZE)//长度大于页的长度{u8Len = PAGE_SIZE;}if ((u16Addr + (uint16_t)u8Len) > CAPACITY_SIZE)//超过容量{u8Len = (uint8_t)(CAPACITY_SIZE - u16Addr);}if (((u16Addr % PAGE_SIZE) + (uint16_t)u8Len) > PAGE_SIZE)//判断是否跨页{u8Len -= (uint8_t)((u16Addr + (uint16_t)u8Len) % PAGE_SIZE);//跨页,截掉跨页的部分}for (i = 0; i < (u8Len - 1); i++){*(pBuff + i) = IIC_ReadByte();IIC_Ack();//主机应答}*(pBuff + u8Len - 1) = IIC_ReadByte();IIC_NoAck();//最后一个不应答IIC_Stop();//停止信号
}

五、注意事项

1.仅适用于x24C04、x24C08、x24C16系列EEPROM芯片;
2.器件地址必须与A2/A1/A0引脚的硬件连接对应;
3.调用写入程序(无论是单字节写入还是页写),需要延时10ms(即twr,有的芯片手册说是5ms)后再对器件进行操作,否则这段时间内器件不响应命令;


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

相关文章

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、共…

android中进程间通信的几种方式

进程间通信&#xff08;IPC&#xff09;方式 使用Bundle使用文件共享使用Messenger使用AIDL使用COntentProvider使用Socket 一、使用Bundle 我们都知道Android中三大组件Activity&#xff0c;Service&#xff0c;Receiver都支持在Intent中传递Bundle数据&#xff0c;而Bundle…

操作系统——进程间通信

文章目录 其他文章管道消息队列共享内存信号量信号Socket总结 个人博客网站&#xff1a; https://xingkongdiyiren.github.io/myblog/,完整的Java知识体系&#xff0c;包括408&#xff0c;架构&#xff0c;业务&#xff0c;产品&#xff0c;软技能等 其他文章 操作系统——概…

进程间的通信方式(六种)

进程之间的通信 参考文章&#xff1a;https://blog.csdn.net/qq_34827674/article/details/107678226 前提知识&#xff1a;每个进程都有自己的用户空间&#xff0c;而内核空间是每个进程共享的。因此进程之间想要进行通信&#xff0c;就需要通过内核来实现。 管道&#xff1…

【操作系统】进程间通信的五种方式

引言1.进程对白&#xff1a;管道、记名管道、套接字1.管道2.虫洞&#xff1a;套接字3.信号 4.信号旗语&#xff1a;信号量5.进程拥抱&#xff1a;共享内存 引言 进程作为人类的发明&#xff0c;自然免不了脱离人类的习性&#xff0c;也有通信需求。如果进程之间不进行任何通信…

进程之间的通信方式

进程之间的通信方式包括管道&#xff0c;消息队列&#xff0c;共享内存&#xff0c;信号&#xff0c;信号量&#xff0c;socket六种方式&#xff0c;下面来对这6种方式分别进行介绍。 一、管道 管道的结构示意图如上所示&#xff0c;管道包含一个输入端和一个输出端&#xff0…