EEPROM使用之AT24C512_IIC

1. EEPROM是什么

EEPROM是指带电可擦可编程只读存储器。是一种掉电后数据不丢失的存储芯片

2.AT24C512是什么

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • AT24C512是一个串行传输(I2C总线)EEPROM存储器,每片内部有64K字节存储空间,
    每个存储单元有自己的(16位=2字节)地址,
    每片地址从0000H到FFFFH。
    这64KB存储空间又分成512个页,每页128个字节。(这个芯片代号512就是这么来的)
    512*128 = 65536Byte = 64Kbyte
    在这里插入图片描述

  • AT24Cxx是一系列类似的存储芯片)。分页存储在读取数据时不必理会,但在写入时要理会,否则会覆盖页内数据。另外与其他EEPROM不同的是写入操作不需要先擦除扇区(页),片内的管理单元代用户完成了这个动作。不用擦除命令

  • AT24Cxx是IIC(I2C)协议,每传输完一个字节数据要有一个应答信号然后才能继续。主控设备(MCU)向从设备(AT24C512)发送一字节数据后,从设备做出应答确认(拉低数据线),反之从设备向主设备发送一字节数据后,主设备要做出类似应答确认(拉低数据线),主动拉低数据线相当于输出”0” ,这个应答确认资料上写成 ACKNOWLEDGE,简写ACK。

  • 芯片有8个引脚,最重要的SCL和SDA,分别连接到单片机的两个I/O引脚上做时钟和数据,时钟线是单向的由主控设备MCU提供,数据线是双向的。单片机上这两个引脚应该是普通双向I/O,尤其SDA不能设置为其他模式,否则可能收不到应答和数据。VCC和GND不必说,但不能忽视芯片工作电压与系统电压是否匹配

  • WP引脚的功能是写保护,接到VCC就只能读不能写,接到GND可读可写,如果悬空内部接地

  • A0和A1可以接到VCC或GND,用来确定芯片物理地址。所以一个系统可以连接4片AT24C512。它们的地址分别是(二进制)00,01,10,11.如果悬空在内部也连接到地

  • 即使系统内只有一片AT24C512,根据你的连接它的物理地址你应该清楚,因为后面的操作需要这个地址写命令。(比如A1,A0都接地或悬空它的地址是00;如果A1接地或悬空A0接电源它的地址是01,余此类推)

  • NO是空引脚。芯片的功耗很低,写入数据时电流3mA,读出时更小,空闲状态几个uA。可以用单片机的I/O引脚为芯片供电,相当于片选

  • AT24C512的工作频率在5V时最高为1MHZ,2.7V时为400KHZ,就是说输入到SCL的时钟频率不能超过这个数值,否则读写失败

  • VCC=5V或3.3V时,SCL脉冲宽度上下边各不小于1us,保守点可以再宽些

  • 直译”设备地址”,我把它叫“寻址命令”,

  • 仅一个字节,前5位固定是 10100,

  • A1,A0就是上面提到的芯片物理地址,00

  • 所以设备的物理地址是1010000 = 0x50

  • 最后一位R/W如果填1,表示对选中芯片进行读操作,

  • 填0表示对选中芯片进行写操作。

  • 比如要对物理地址为00的芯片进行读操作,就发送命令:10100001,= 0XA1

  • 要对该芯片进行写操作就发送命令:10100000, = 0XA0

写数据的(时序)过程是:
1开始命令;
2发送芯片寻址命令(写);
3发送2字节地址;
4发送1字节数据;
如果继续发转回4;
5停止命令。
读数据的(时序)过程是:
1开始命令;
2发送芯片寻址命令(写);
3发送2字节地址;
4开始命令;
5发送芯片寻址命令(读);
6读1字节数据;
继续读转回6 ;
7停止命令。

  • 向AT24C512写入数据不能跨页,就是说上面例程如果写入129个数据,将把第129数据写到页的开始0000单元。如果跨页需要修改地址后再写
  • 读数据没有页限制,可以从任何地址开始读若干字节数据,每收到一个字节数据需要应答,但最后一个字节不发应答
  • 和同系列其他芯片一样,器件地址为8位,前5位固定,最低位为读/写标志,第二、三位为总线地址。电路中A0、A1引脚接地,故读、写的器件地址分别为0xA1,0xA0

写操作结束后,MCU会向EEPROM发送一个停止位,在发出下一个起始位之前,EEPORM芯片会进入写周期(internally-timed write cycle),这段时间内不能进行任何输入,所以在两个写操作或写操作与读操作之间需要有一个保证wirte cycle的延时,一般5~10ms即可

**

https://blog.csdn.net/weixin_43772810/article/details/122149151
链接1

https://blog.csdn.net/lin5103151/article/details/103551569
链接2

https://blog.csdn.net/wanglong3713/article/details/124909042
链接3

https://www.zhihu.com/tardis/zm/art/399830985?source_id=1005
链接4

https://www.cnblogs.com/leo0621/p/8251897.html
链接5

发送起始信号–>
发送器件地址(包含写入命令)–>
收到应答–>
发送需要写入数据的地址的高字节(8bit)–>
收到应答–>
发送需要写入数据的地址的低字节(8bit)–>
收到应答–>
发送需要写入的数据–>
收到应答–>
发送停止信号

  • 参考一写入一字节数据
/*******************************************************************************
  * 函数名:x24Cxx_WriteByte
  * 功  能:写一个字节
  * 参  数:u16Addr要写入的地址
			u8Data要写入的数据
  * 返回值:无
  * 说  明:无
*******************************************************************************/
void x24Cxx_WriteByte(uint16_t u16Addr, uint8_t u8Data)
{
	x24Cxx_WriteEnable();//使能写入
	IIC_Start();//起始信号
	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();//等待应答
	IIC_WriteByte(u8Data);
	IIC_WaitAck();//等待应答
	IIC_Stop();//停止信号
	x24Cxx_WriteDisble();//禁止写入
}

  • 参考二写入一页数据
/*******************************************************************************
  * 函数名:x24Cxx_WritePage
  * 功  能:页写
  * 参  数:u16Addr要写入的首地址;
			u8Len写入数据字节数,最大为PAGE_SIZE
			pData要写入的数据首地址
  * 返回值:无
  * 说  明:最多写入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);//器件寻址+读/写选择
	IIC_WaitAck();//等待应答
	IIC_WriteByte((uint8_t)((u16Addr >> 8) & 0xFF));
	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();//禁止写入
}

  • 参考三读一字节数据

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

/*******************************************************************************
  * 函数名:x24Cxx_ReadByte
  * 功  能:读一个字节
  * 参  数:u16Addr要读取的地址
  * 返回值:u8Data读出的数据
  * 说  明:无
*******************************************************************************/
uint8_t x24Cxx_ReadByte(uint16_t u16Addr)
{
	uint8_t u8Data = 0;
	IIC_Start();//起始信号	
	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();//等待应答
	IIC_Start();//起始信号
	IIC_WriteByte(DEV_ADDR | READ_CMD);//器件寻址+读
	IIC_WaitAck();//等待应答
	u8Data = IIC_ReadByte();
	IIC_NoAck();
	IIC_Stop();//停止信号
	return u8Data;
}

  • 参考四读一页数据
/*******************************************************************************
  * 函数名:x24Cxx_ReadPage
  * 功  能:页读
  * 参  数:u16Addr要读取的首地址;
			u8Len读取数据字节数,最大为PAGE_SIZE
			pBuff读取数据存入的缓存
  * 返回值:无
  * 说  明:最多读1页,防止翻卷,如果地址跨页则去掉跨页的部分
*******************************************************************************/
void x24Cxx_ReadPage(uint16_t u16Addr, uint8_t u8Len, uint8_t *pBuff)
{
	uint8_t i;	
	IIC_Start();//起始信号	
	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();//等待应答
	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();//停止信号
}

抓取时序图

  • 多啰嗦一句

  • 了解时序的方式是看波形图最直接

  • 快捷有效

  • 写入一字节数据
    iic写入数据

  • 读取一字节数据
    IIC读取数据

  • 写入多字节数据
    在这里插入图片描述

  • 读取多字节数据

在这里插入图片描述

  • 参考代码:写入一字节
/*
 * 写一字节数据_16位内存地址
 */
void user_write_byte_16bit_extend(iic_control_t *io_ctl, uint8_t dev_addr,uint16_t mem_addr, uint8_t mem_data)
{
    //开始
    user_iic_start_extend(io_ctl);
    //写地址
    user_iic_write_address_extend(io_ctl, dev_addr, IIC_WRITE);

    //内部寄存器地址
    user_iic_write_data_extend(io_ctl, mem_addr>>8);
    user_iic_write_data_extend(io_ctl, mem_addr&0xFF);

    //内部寄存器数据
    user_iic_write_data_extend(io_ctl, mem_data);
    //停止
    user_iic_stop_extend(io_ctl);
}
  • 参考代码写入多字节

/*
 * 写多字节数据_16位内存地址
 * IIC_EEPROM_at24c512
 * 页写
 */
void user_write_byte_16bit_page_extend(iic_control_t *io_ctl, uint8_t dev_addr,uint16_t mem_addr, uint8_t *mem_data, uint8_t len)
{
    //开始
    user_iic_start_extend(io_ctl);
    //写地址
    user_iic_write_address_extend(io_ctl, dev_addr, IIC_WRITE);

    //内部寄存器地址
    user_iic_write_data_extend(io_ctl, mem_addr>>8);
    user_iic_write_data_extend(io_ctl, mem_addr&0xFF);

    if(len >= AT24C512_PAGE_SIZE)
        len = AT24C512_PAGE_SIZE;

    if(mem_addr + len > AT24C512_ROM_SIZE)
        len = AT24C512_ROM_SIZE - mem_addr;

    if((mem_addr%AT24C512_PAGE_SIZE) + len > AT24C512_PAGE_SIZE)
        len = len - ((mem_addr+len)%AT24C512_PAGE_SIZE) ;

    for(uint8_t t=0; t<len; t++)
    {
        //内部寄存器数据
        user_iic_write_data_extend(io_ctl, *(mem_data+t));
    }
    //停止
    user_iic_stop_extend(io_ctl);
}

  • 参考代码读取一字节

/*
 * 读一字节数据_16位地址
 */
void user_read_byte_16bit_extend(iic_control_t *io_ctl, uint8_t dev_addr,uint16_t mem_addr, uint8_t *mem_data)
{
    uint8_t rec_data =0;
    //开始
    user_iic_start_extend(io_ctl);
    //写地址
    user_iic_write_address_extend(io_ctl, dev_addr, IIC_WRITE);
    //内部寄存器地址
    user_iic_write_data_extend(io_ctl, mem_addr>>8);
    user_iic_write_data_extend(io_ctl, mem_addr&0xFF);
    //开始
    user_iic_start_extend(io_ctl);
    //读地址
    user_iic_write_address_extend(io_ctl, dev_addr, IIC_READ);
    //读数据
    rec_data = user_iic_read_data_extend(io_ctl, IIC_NO_ACK);
    //停止
    user_iic_stop_extend(io_ctl);

    *mem_data = rec_data;
}
  • 参考代码读取多字节

/*
 * 读多字节数据_16位内存地址
 * IIC_EEPROM_at24c512
 * 页读
 */
void user_read_byte_16bit_page_extend(iic_control_t *io_ctl, uint8_t dev_addr,uint16_t mem_addr, uint8_t *mem_data, uint8_t len)
{
    //开始
    user_iic_start_extend(io_ctl);
    //写地址
    user_iic_write_address_extend(io_ctl, dev_addr, IIC_WRITE);
    //内部寄存器地址
    user_iic_write_data_extend(io_ctl, mem_addr>>8);
    user_iic_write_data_extend(io_ctl, mem_addr&0xFF);
    //开始
    user_iic_start_extend(io_ctl);
    //读地址
    user_iic_write_address_extend(io_ctl, dev_addr, IIC_READ);

    if(len >= AT24C512_PAGE_SIZE)
        len = AT24C512_PAGE_SIZE;

    if(mem_addr + len > AT24C512_ROM_SIZE)
        len = AT24C512_ROM_SIZE - mem_addr;

    if((mem_addr%AT24C512_PAGE_SIZE) + len > AT24C512_PAGE_SIZE)
        len = len - ((mem_addr+len)%AT24C512_PAGE_SIZE) ;

    for(uint8_t t=0; t<len; t++)
    {
        //内部寄存器数据
        *(mem_data+t) = user_iic_read_data_extend(io_ctl, IIC_ACK);
    }
    //停止
    user_iic_stop_extend(io_ctl);
}

  • 23
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值