x24Cxx系列EEPROM芯片C语言通用读写程序

本文档详细介绍了各种ATMEL x24C系列EEPROM芯片的单片机读写驱动程序,包括AT24C01-AT24C512,通过宏定义和条件编译实现代码通用,适用于不同容量芯片的移植。内容涵盖了地址寻址策略和注意事项,可供开发者快速上手移植。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

EEPROM芯片读写程序相关索引

1.AT24C01/AT24C02系列EEPROM芯片单片机读写驱动程序
2.AT24C04、AT24C08、AT24C16系列EEPROM芯片单片机读写驱动程序
3.AT24C32、AT24C64、AT24C128、AT24C256、AT24C512系列EEPROM芯片单片机读写驱动程序
4.x24Cxx系列EEPROM芯片C语言通用读写程序

一、概述

在之前的3篇文章中,介绍了x24C01~x24C512的读写程序,相关文章如下:
1.IIC驱动:4位数码管显示模块TM1637芯片C语言驱动程序
2.AT24C01、AT24C02读写:AT24C01/AT24C02系列EEPROM芯片单片机读写驱动程序
3.AT24C04、AT24C08、AT24C16读写:AT24C04、AT24C08、AT24C16系列EEPROM芯片单片机读写驱动程序
4.AT24C32、AT24C64、AT24C128、AT24C256、AT24C512读写:AT24C32、AT24C64、AT24C128、AT24C256、AT24C512系列EEPROM芯片单片机读写驱动程序
本篇博文将综合前述几篇文章,将x24C01-x24C512写成可以通用的程序,以方便不同容量EEPROM芯片移植。为此,笔者斥巨资购入了x24C01-x24C512共10个EEPROM芯片以及读写模块,全家福如下图:
在这里插入图片描述

在这里插入图片描述

以下的所有程序均在所有型号芯片上经过验证。

二、芯片对比

型号容量bit容量byte页数字节/页器件寻址位可寻址器件数WordAddress位数/字节数备注
AT24C011k128168A2A1A087/1WordAddress1个字节
AT24C022k256328A2A1A088/1WordAddress1个字节
AT24C044k5123216A2A149/1WordAddress1个字节+P0位
AT24C088k10246416A2210/1WordAddress 1个字节+P0、P1位
AT24C1616k204812816-111/1WordAddress1个字节+P0、P1、P2位
AT24C3232k4k12832A2A1A0812/2WordAddress2个字节
AT24C6464k8k25632A2A1A0813/2WordAddress2个字节
AT24C128128k16k25664A1A0414/2WordAddress2个字节
AT24C256256k32k51264A1A0415/2WordAddress2个字节
AT24C512512k64k512128A2A1A0816/2WordAddress2个字节

通过上表,结合前3篇文章的介绍,我们可以总结出,x24C01/x24C02的读写最基本,在这个基础上,由于x24C04/x24C08/x24C16的存储地址的增多,需要在对存储地址寻址的时候,多出几位,这几位加在P0/P1/P2即“页选择位”上;还是在这个基础上,x24C32/x24C64/x24C128/x24C256/x24X512的存储地址更多,1个字节+页选择位也无法满足,因此干脆将WordAddress搞成2个字节的,不再需要页选择位。

三、主要程序代码

经过上述的分析,我们还是通过宏定义的方式,先定义器件型号,再以条件编译的方式,对不同的芯片编译不同的代码。

/*******************************************************************************
型号	  Byte容量	  页数	  页内字节数    WORD_ADDR位数 WORD_ADDR字节数
x24C01	  128Byte	  16页	  8Byte			7bit			1Byte
x24C02	  256Byte	  32页	  8Byte			8bit			1Byte
x24C04	  512Byte	  32页	  16Byte		9bit			1Byte
x24C08	  1024Byte	  64页	  16Byte		10bit			1Byte
x24C16	  2048Byte	  128页	  16Byte		11bit			1Byte
x24C32	  4096Byte	  128页	  32Byte		12bit			2Byte
x24C64	  8192Byte	  256页	  32Byte		13bit			2Byte
x24C128	  16384Byte	  256页	  64Byte		14bit			2Byte
x24C256	  32768Byte	  512页	  64Byte		15bit			2Byte
x24C512	  65536Byte	  512页	  128Byte		16bit			2Byte
*******************************************************************************/
#define READ_CMD				1
#define WRITE_CMD				0

#define x24C01//器件名称,x24C01~x24C512

#define DEV_ADDR				0xA0					//设备硬件地址

#ifdef x24C01
	#define PAGE_NUM			16						//页数
	#define PAGE_SIZE			8						//页面大小(字节)
	#define CAPACITY_SIZE		(PAGE_NUM * PAGE_SIZE)	//总容量(字节)
	#define ADDR_BYTE_NUM		1						//地址字节个数
#endif 
 
#ifdef x24C02
	#define PAGE_NUM			32						//页数 
	#define PAGE_SIZE			8						//页面大小(字节)
	#define CAPACITY_SIZE		(PAGE_NUM * PAGE_SIZE)	//总容量(字节)
	#define ADDR_BYTE_NUM		1						//地址字节个数
#endif
 
#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
 
#ifdef x24C32
 	#define PAGE_NUM			128						//页数
	#define PAGE_SIZE			32						//页面大小(字节)
	#define CAPACITY_SIZE		(PAGE_NUM * PAGE_SIZE)	//总容量(字节)
	#define ADDR_BYTE_NUM		2						//地址字节个数
#endif
 
#ifdef x24C64
 	#define PAGE_NUM			256						//页数
	#define PAGE_SIZE			32						//页面大小(字节)
	#define CAPACITY_SIZE		(PAGE_NUM * PAGE_SIZE)	//总容量(字节)
	#define ADDR_BYTE_NUM		2						//地址字节个数
#endif
 
#ifdef x24C128
 	#define PAGE_NUM			256						//页数
	#define PAGE_SIZE			64						//页面大小(字节)
	#define CAPACITY_SIZE		(PAGE_NUM * PAGE_SIZE)	//总容量(字节)
	#define ADDR_BYTE_NUM		2						//地址字节个数
#endif
 
#ifdef x24C256
 	#define PAGE_NUM			512						//页数
	#define PAGE_SIZE			64						//页面大小(字节)
	#define CAPACITY_SIZE		(PAGE_NUM * PAGE_SIZE)	//总容量(字节)
	#define ADDR_BYTE_NUM		2						//地址字节个数
#endif
 
#ifdef x24C512
 	#define PAGE_NUM			512						//页数
	#define PAGE_SIZE			128						//页面大小(字节) 
	#define CAPACITY_SIZE		(PAGE_NUM * PAGE_SIZE)	//总容量(字节)
	#define ADDR_BYTE_NUM		2						//地址字节个数
#endif
/*******************************************************************************
  * 函数名:x24Cxx_WriteByte
  * 功  能:写一个字节
  * 参  数:u16Addr要写入的地址
			u8Data要写入的数据
  * 返回值:无
  * 说  明:器件地址(包含写入命令) -> 1或2个字节WORD ADDR -> 数据
*******************************************************************************/
void x24Cxx_WriteByte(uint16_t u16Addr, uint8_t u8Data)
{
	x24Cxx_WriteEnable();//使能写入
	IIC_Start();//起始信号	
	#if	(ADDR_BYTE_NUM == 1)//地址只有1个字节
	{
		IIC_WriteByte(DEV_ADDR | WRITE_CMD | (((uint8_t)((u16Addr >> 8) & 0x07)) << 1));//器件寻址+写+页选择位
		IIC_WaitAck();//等待应答
		IIC_WriteByte((uint8_t)(u16Addr & 0xFF));//只取地址的低字节
		IIC_WaitAck();//等待应答
	}
	#endif
	#if	(ADDR_BYTE_NUM == 2)//地址有2个字节
	{
		IIC_WriteByte(DEV_ADDR | WRITE_CMD);//器件寻址+写
		IIC_WaitAck();//等待应答
		IIC_WriteByte((uint8_t)((u16Addr >> 8) & 0xFF));//地址高字节
		IIC_WaitAck();//等待应答
		IIC_WriteByte((uint8_t)(u16Addr & 0xFF));//地址低字节
		IIC_WaitAck();//等待应答
	}
	#endif
	IIC_WriteByte(u8Data);
	IIC_WaitAck();//等待应答
	IIC_Stop();
	x24Cxx_WriteDisble();//禁止写入
}

/*******************************************************************************
  * 函数名:x24Cxx_ReadByte
  * 功  能:读一个字节
  * 参  数:u16Addr要读取的地址
  * 返回值:u8Data读出的数据
  * 说  明:无
*******************************************************************************/
uint8_t x24Cxx_ReadByte(uint16_t u16Addr)
{
	uint8_t u8Data = 0;
	IIC_Start();//起始信号	
	#if	(ADDR_BYTE_NUM == 1)//地址只有1个字节
	{
		IIC_WriteByte(DEV_ADDR | WRITE_CMD | (((uint8_t)((u16Addr >> 8) & 0x07)) << 1));//器件寻址+写+页选择位
		IIC_WaitAck();//等待应答
		IIC_WriteByte((uint8_t)(u16Addr & 0xFF));//只取地址的低字节
		IIC_WaitAck();//等待应答
	}
	#endif
	#if	(ADDR_BYTE_NUM == 2)//地址有2个字节
	{
		IIC_WriteByte(DEV_ADDR | WRITE_CMD);//器件寻址+写
		IIC_WaitAck();//等待应答
		IIC_WriteByte((uint8_t)((u16Addr >> 8) & 0xFF));//地址高字节
		IIC_WaitAck();//等待应答
		IIC_WriteByte((uint8_t)(u16Addr & 0xFF));//地址低字节
		IIC_WaitAck();//等待应答
	}
	#endif
	IIC_Start();//起始信号
	IIC_WriteByte(DEV_ADDR | READ_CMD);//器件寻址+读
	IIC_WaitAck();//等待应答
	u8Data = IIC_ReadByte();
	IIC_NoAck();
	IIC_Stop();
	return u8Data;
}
/*******************************************************************************
  * 函数名:x24Cxx_WritePage
  * 功  能:页写
  * 参  数:u16Addr要写入的首地址;
			u8Len写入数据字节数,最大为PAGE_SIZE
			pData要写入的数据首地址
  * 返回值:无
  * 说  明:最多写入1页,防止翻卷,如果地址跨页则去掉跨页的部分
*******************************************************************************/
void x24Cxx_WritePage(uint16_t u16Addr, uint8_t u8Len, uint8_t *pData)
{
	uint8_t i;
	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);//跨页,截掉跨页的部分
	}
	x24Cxx_WriteEnable();//使能写入
	IIC_Start();//起始信号
	#if	(ADDR_BYTE_NUM == 1)//地址只有1个字节
	{
		IIC_WriteByte(DEV_ADDR | WRITE_CMD | (((uint8_t)((u16Addr >> 8) & 0x07)) << 1));//器件寻址+写+页选择位
		IIC_WaitAck();//等待应答
		IIC_WriteByte((uint8_t)(u16Addr & 0xFF));//只取地址的低字节
		IIC_WaitAck();//等待应答
	}
	#endif
	#if	(ADDR_BYTE_NUM == 2)//地址有2个字节
	{
		IIC_WriteByte(DEV_ADDR | WRITE_CMD);//器件寻址+写
		IIC_WaitAck();//等待应答
		IIC_WriteByte((uint8_t)((u16Addr >> 8) & 0xFF));//地址高字节
		IIC_WaitAck();//等待应答
		IIC_WriteByte((uint8_t)(u16Addr & 0xFF));//地址低字节
		IIC_WaitAck();//等待应答
	}
	#endif	
	for (i = 0; i < u8Len; i++)
	{
		IIC_WriteByte(*(pData + i));
		IIC_WaitAck();//等待应答
	}
	IIC_Stop();	
	x24Cxx_WriteDisble();//禁止写入
}
/*******************************************************************************
  * 函数名:x24Cxx_ReadPage
  * 功  能:页读
  * 参  数:u16Addr要读取的首地址;
			u8Len读取数据字节数,最大为PAGE_SIZE
			pBuff读取数据存入的缓存
  * 返回值:无
  * 说  明:无
*******************************************************************************/
void x24Cxx_ReadPage(uint16_t u16Addr, uint8_t u8Len, uint8_t *pBuff)
{
	uint8_t i;
	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);//跨页,截掉跨页的部分
	}
	IIC_Start();//起始信号	
	#if	(ADDR_BYTE_NUM == 1)//地址只有1个字节
	{
		IIC_WriteByte(DEV_ADDR | WRITE_CMD | (((uint8_t)((u16Addr >> 8) & 0x07)) << 1));//器件寻址+写+页选择位
		IIC_WaitAck();//等待应答
		IIC_WriteByte((uint8_t)(u16Addr & 0xFF));//只取地址的低字节
		IIC_WaitAck();//等待应答
	}
	#endif
	#if	(ADDR_BYTE_NUM == 2)//地址有2个字节
	{
		IIC_WriteByte(DEV_ADDR | WRITE_CMD);//器件寻址+写
		IIC_WaitAck();//等待应答
		IIC_WriteByte((uint8_t)((u16Addr >> 8) & 0xFF));//地址高字节
		IIC_WaitAck();//等待应答
		IIC_WriteByte((uint8_t)(u16Addr & 0xFF));//地址低字节
		IIC_WaitAck();//等待应答
	}
	#endif
	IIC_Start();//起始信号
	IIC_WriteByte(DEV_ADDR | READ_CMD);//器件寻址+读
	IIC_WaitAck();//等待应答
	for (i = 0; i < (u8Len - 1); i++)
	{
		*(pBuff + i) = IIC_ReadByte();
		IIC_Ack();
	}
	*(pBuff + u8Len - 1) = IIC_ReadByte();
	IIC_NoAck();//最后一个不应答
	IIC_Stop();
}

四、总结

1.器件地址必须与A2/A1/A0引脚的硬件连接对应;
2.该程序,写入或读取的地址,不要超过芯片的容量,否则结果会异常,读者可以自行修改程序,做功能更全面的地址是否合规的检查;
3.x24Cxx_WriteEnable()函数为WP写保护功能使能,根据单片机的引脚配置自行定义;
4.调用写入程序(无论是单字节写入还是页写),需要延时10ms(即twr,有的芯片手册说是5ms)后再对器件进行操作,否则这段时间内器件不响应命令;

void start 开始信号 void stop 停止信号 void Ack 发确认信号 void NoAck 发无确认信号 void init 初始化信号 拉高SDA和SCL两条总线 bit write byte uchar date 写一字节 将 date 写入AT24C02 中 uchar read byte 读一字节 从 AT24C02 中读一字节 bit busy 应答查询 stop 后 启动AT24C02内部写周期 启动查询 初始化EEPROM程序内容为0XFF nPage 0 31 void Init Flash uchar nPage 8 bytes 1 page init 0xFF void write add uchar address uchar date 向 AT24C02 中写数据 从AT24C02中给定的地址nAddr起 将存放在以指针nContent开头的存储空间中的nLen个字节数据 连续写入AT24C02 void write flash uchar nContent uchar nAddr uchar nLen uchar read add uchar address 从 AT24C02 中读出数据 从AT24C02中给定的地址nAddr起 读取nLen个字节数据存放在以指针nContent开头的存储空间 void read flash uchar nContent uchar nAddr uchar nLen 单片机P2口接74HC138(三八译码器)P2 3 74HC138: EI P2 2 74HC138:A2 P2 1 74HC138:A1 P2 0 74HC138:A0 译码器输出 Y0 Y1 Y2 Y3 Y4 Y5 Y6 Y7均低电平有效 分别选通1 8个数码管 包括2个四位一体数码管LG3641BH 共2x4 8个数码管 数码管数据口为P0口 数码管为共阳4位一体数码管 功能: 译码器输出为1 8个数码管的段选信号 轮流选择1 8数码管 void display uchar nContent uchar nLen 功能:在8段数码管上显示nLen个字符 这些字符存储在指针nContent开头的往下的内容中 显示原理: 1 送出要显示的段数 2 P2译码 选择要显示的位 3 延时1 2ms 时间不能太长 否则会闪烁 也不能太短 否则会很暗 4 取消段选 消隐 若要显示多段 重复以上4步 ">void start 开始信号 void stop 停止信号 void Ack 发确认信号 void NoAck 发无确认信号 void init 初始化信号 拉高SDA和SCL两条总线 bit write byte uchar date 写一字节 将 date 写入AT24C02 中 uchar read byte 读一字节 从 AT24C02 中读一 [更多]
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值