1、24C02简介

  24C02是一个2Kbit的串行EEPROM存储芯片,可存储256个字节数据。工作电压范围为1.8V到6.0V,具有低功耗CMOS技术,自定时擦写周期,1000000次编程/擦除周期,可保存数据100年。24C02有一个16字节的页写缓冲器和一个写保护功能。通过I2C总线通讯读写芯片数据,通讯时钟频率可达400KHz。

  可以通过存储IC的型号来计算芯片的存储容量是多大,比如24C02后面的02表示的是可存储2Kbit的数据,转换为字节的存储量为2*1024/8 = 256byte;有比如24C04后面的04表示的是可存储4Kbit的数据,转换为字节的储存量为2*1024/8 = 512byte;以此来类推其它型号的存储空间。

  24C02的管脚图如下:

  VCC和VSS是芯片的电源和地,电压的工作范围为:+1.8V~+6.0V。

  A0、A1、A2是IC的地址选择脚。

  WP是写保护使能脚。

  SCL是I2C通讯时钟引脚。

  SDA是I2C通讯数据引脚。

2、24C02的设备地址和写写保护功能

  I2C主机在与24C02通讯时,需要发送一个设备地址进行寻址,在I2C总线上,每一个从机设备的地址都是唯一的。

  24C02的设备地址包含两部分,第一部分是bit7~bit4是固定的“1010”,第二部分bit3~bit1位由A2、A1、A0组成。主机在与24C02进行通讯时,除了发送设备地址还需要发送数据的读写方向位R/W,24C02的是设备地址与R/W位组成了一个字节的数据。如下图:

  上图列出了几个存储IC的设备地址与R/W位组成的字节。由图中可以看到,存储IC地址的bit7~bit4位固定为“1010”;bit3~bit1位由A2、A1、A0引脚的电平状态决定,如果Ax接的是电源(高电平),那么Ax=1,如果Ax接的是地,那么Ax=0,即由A2、A1、A0可以组合成8种设备地址,也就是说在同一个I2C总线上可以同时挂载8个24C02芯片。一般如果I2C总线上只有一片24C02芯片的话,A2、A1、A0引脚都接到地。

  由于24C02只有256个字节的存储空间,所以只需要1个字节就可以寻址完24C02的存储空间,但是无法寻址完更大容量的存储IC,比如24C04的存储容量是512字节,需要9个bit的地址位才能寻址完。由上图可以看到,24C04的设备地址内是没有A0参数的,被a8代替了,这个a8就是24C04的第9个bit的地址位,也就是说24C04的A0引脚是不起作用的,这样也就造成了在I2C总线上只能同时挂载4个24C04芯片。其它存储器如24C08、24C16也可以这么类推。

  24C02的WP引脚是写保护引脚,当WP引脚接高电平的时,24C02只能进行读取操作,不能进行写操作。只有当WP引脚悬空或接低电平时,24C02才能进行写操作。

3、24C02数据读取操作

  在这里只是对24C02的读写进行一些说明和一些注意的实现,不会涉及具体的程序代码,只是进行代码概述,工程代码已经上传到个人GitHub中,感兴趣的可以去GitHub中下载查看,GitHub代码地址如下:

https://github.com/h1019384803/STM32F103ZET6_I2C.git。这是一个使用STM32F103ZET6的IO模拟I2C操作24C02的工程。

  MCU通过使用I2C读取24C02任意存储空间地址内的数据,代码如下:  

 uint8_t AT24CXX_READ_ONE_BYTE(uint16_t address)
{
uint8_t dat; I2C_START();
AT24CXX_ERR = I2C_WRITE_BYTE(AT24CXX_WIRTE_CMD);
if(AT24CXX_ERR != )//没有响应直接退出
{
AT24CXX_ERR = I2C_WRITE_BYTE(address & 0xFF);
if(AT24CXX_ERR != )
{
I2C_START(); AT24CXX_ERR = I2C_WRITE_BYTE(AT24CXX_READ_CMD);
if(AT24CXX_ERR != )
{
dat = I2C_READ_BYTE();
I2C_STOP();
}
}
} return dat;
}

  第5行主机产生一个I2C起始信号,第6行发送设备地址和写数据位给24C02,第9行是发送需要读取的地址给24C02,第12行主机产生一个重复起始信号,第14行设备地址和读数据位给24C02,第17行是读取24C02相应地址存储的数据。第18行是主机产生I2C结束信号。

  在上面的程序代码中,AT24CXX_ERR是用来获取24C02的应答信号,如果主机与24C02的通讯正常,主机每发送一个字节给24C02,24C02都会反馈一个应答信号给主机,如果24C02没有反馈应答信号,那么说明24C02正在进行其它操作或者通讯异常导致无法通讯,主机会产生一个结束信号来结束操作。在I2C_WRITE_BYTE()函数内部有一个等待应答信号的操作,如果没有收到应答信号,在I2C_WRITE_BYTE()函数内会产生一个停止信号来结束当前操作。AT24CXX_ERR用来判断接下来的操作是否执行,如果AT24CXX_ERR=0说明没有收到应答信号,直接退本次读取操作;如果AT24CXX_ERR!=0说明有收到应答信号,继续读取操作。

  24C02内部有一个地址计数器,主机发送要读写的存储空间地址给24C02,就相当于改变24C02的内部地址计数器的值,主机每读写一个字节24C02之后,它内部地址计数器的值就会自动加1。也就是说如果当前地址是N,那么主机读取完一个字节的数据之后,再次读的话就变为了读取N+1地址的数据。

  这里需要注意的一点是,24C02的内部地址计数器的地址只能从0~255之间递增,这是因为24C02的存储控制只有256个字节,地址计数器只能在0~255(共256个地址)内变化。如果连续读取使得地址计数器超过255,那么地址计数器就会从0地址开始循环。比如说当前内部计数器地址为255,主机在读取一个字节数据之后会导致内部计数器地址变为0,那么主机再次读取数据的时候读取得到的是24C02地址0的数据。

  MCU使用I2C连续读取24C02内多个存储空间地址数数的代码如下:

 void AT24CXX_READ_BUFF(uint16_t address,uint8_t *buffer,uint16_t Len)
{
uint16_t i; I2C_START();
AT24CXX_ERR = I2C_WRITE_BYTE(AT24CXX_WIRTE_CMD);
if(AT24CXX_ERR != )//没有响应直接退出
{
AT24CXX_ERR = I2C_WRITE_BYTE(address & 0xFF);
if(AT24CXX_ERR != )
{
I2C_START();
AT24CXX_ERR = I2C_WRITE_BYTE(AT24CXX_READ_CMD);
if(AT24CXX_ERR != )
{
for(i=;i<Len;i++)
{
buffer[i] = I2C_READ_BYTE();
}
} if(AT24CXX_ERR != )
{
I2C_STOP();
}
}
}
}

  上面的代码,大部分跟读取一个字节的程序代码是一样的,不一样的是第16~19行,这里用一个for循环来连续读取24C02内的数据,这里并没有对超范围读取数据进行限制,所以在使用的时候需要注意不要连续读取超过24C02的存储空间,就算超过也不会有问题,只是会重新开始从0地址读取。

4、24C02数据写入操作

  MCU使用I2C写入一个字节数据到24C02任意存储空间地址内的代码如下:

 void AT24CXX_WRITE_ONE_BYTE(uint16_t address,uint8_t dat)
{
I2C_START();
AT24CXX_ERR = I2C_WRITE_BYTE(AT24CXX_WIRTE_CMD);
if(AT24CXX_ERR != )//没有响应直接退出
{
AT24CXX_ERR = I2C_WRITE_BYTE(address & 0xFF);
if(AT24CXX_ERR != )
{
AT24CXX_ERR = I2C_WRITE_BYTE(dat);
if(AT24CXX_ERR != )
{
I2C_STOP();
}
}
}
}

  第3行主机产生一个I2C起始信号,第4行发送设备地址和写数据位给24C02,第7行是发送需要写入数据的地址给24C02,第10行是将要写入的数据发送给24C02。第18行是主机产生I2C结束信号。上面大部分操作跟读取是一样的,不一样的只是最后将读取操作改为了写入操作。

  如果需要连续写入数据,可以如下:

     for(i = ;i < ;i ++)
{
AT24C02_BUFF[i] = i;
AT24CXX_WRITE_ONE_BYTE(i,AT24C02_BUFF[i]);
}

  但是在实际使用的过程中,发现只有一部分AT24C02_BUFF[]数组里面的数据被写入到了24C02当中,有一些数据没有写进24C02。这是因为24C02擦写数据没有那么快,需要一定的时间,在24C02正在擦写数据的过程中,是不会应答主机的通讯的,所以如果主机在写入一个数据之后又立马写入另一个数据,就会导致24C02跟不上主机的通讯速度从而导致无法写入数据。

  需要注意的是24C02并不是在主机发送数据给24C02之后就立马擦写数据的,24C02是在主机产停止信号之后才开始擦写数据的,并且在擦写数据完成之前不会响应主机的其它操作。

  可以通过一定的延时函数来等待24C02擦写完成,代码如下:

    for(i = ;i < ;i ++)
{
AT24C02_BUFF[i] = i;
AT24CXX_WRITE_ONE_BYTE(i,AT24C02_BUFF[i]);
HAL_Delay();
}

  通过调用HAL_Delay()函数进行延时,具体的延时时间可以通过调试来决定,这里使用1ms的延时时间,具体24C02擦写数据需要多久并不清楚。

  除了通过延时函数进行等待24C02擦写完成,也可以通过发送设备地址给24C02,然后查询是否有应答信号返回来判断24C02是否擦写完成。24C02在擦写数据时是不会反馈应答信号给主机的,这样就可以通过不断的发送数据给24C02,然后查询应答信号来判断24C02是否擦写完成,一旦擦写完成就可以进行下一个数据的写入。代码如下:

 void Wait_AT24CXX_WRITE_OK(void)
{
uint8_t Wait_Cnt; Wait_Cnt = ;
do
{
I2C_START();
AT24CXX_ERR = I2C_WRITE_BYTE(AT24CXX_WIRTE_CMD);
if(AT24CXX_ERR != )
{
I2C_STOP();//接收到响应信号退出
break;
} }while(Wait_Cnt--); }

  Wait_Cnt是一个次数限制变量,不能无限的在里面等待,不然遇到异常就有可能造成程序卡死。

  程序通过发送起始信号、发送设备地址和写数据方向给24C02,如果24C02反馈了一个应答信号给主机,主机就产生一个停止信号,然后退出当前循环。应用代码如下:

     for(i = ;i < ;i ++)
{
AT24C02_BUFF[i] = i;
AT24CXX_WRITE_ONE_BYTE(i,AT24C02_BUFF[i]);
Wait_AT24CXX_WRITE_OK();//可以通过发送设备地址给从机,通过从机反馈的响应信号来判断从机是否可以正常通讯
}

5、24C02页写入

  24C02有一个页写入功能,可以连续写入16个字节的数据。

  24C02可以以页来划分存储空间,每16个字节组成一个页,24C02的存储空间大小为256个字节,所以24C02总共有16个页。如:

  页0:地址从0x00~0x0F

  页1:地址从0x10~0x1F

  ......

  页15:地址从0xF0~0xFF  

  24C02可以在一个页内连续的写入数据,但是需要注意的是如果写入的数据超过页大小,那么就会覆盖页初始地址的值,比如说连续写入3个数据,第1个数据写入到地址0x0F当中,第2个数据由于溢出页的限制,会被写入到地址0x00当中,第3个数据会被写入到地址0x01当中。

  以个人的理解,24C02内部有一个16byte的数据缓存器,在上面的介绍中知道,主机在发送数据给24C02的时候,24C02是不会擦写数据的,只有当主机发送停止信号之后24C02才会擦写数据。那么当主机发送数据给24C02时,只是将数据写入到了24C02内部的缓存器中,只有当主机发送结束信号之后,24C02才将缓存器内的数据写入到内部存储空间。

  由24C02的数据缓存器只有16个byte(每个型号的存储IC的页大小是不一样的也就是缓存器大小是不不一样的)。所以如果写入的数据超过缓存器的大小就会覆盖之前写入的数据。

  使用页写入连续将数据写入24C02的代码如下:  

 void AT24CXX_WRITE_BUFF(uint16_t address,uint8_t *Buffer,uint16_t Len)
{
uint8_t i;
uint16_t re_main; if(address >= )//对输入的地址进行限制,24C02只有256个字节的存储空间,其它型号的存储器IC可以通过查资料
{
return;
} re_main = - address;//计算出还有多少存储空间 if(Len > re_main)//如果要写入的数据量超过剩余存储空间,则只写入剩余存储空间数量的数据
{
Len = re_main;
} re_main = - address%;//计算当前页还可以写入多少个数据 if(Len <= re_main)//如果要写入的数据小于等于当前页剩余的存储空间,则只写入Len个字节数据就好,不需要跨页操作
{
re_main = Len;
} do
{
I2C_START();
AT24CXX_ERR = I2C_WRITE_BYTE(AT24CXX_WIRTE_CMD);
if(AT24CXX_ERR == )//没有响应直接退出
{
break;
} I2C_WRITE_BYTE(address & 0xFF);
for(i = ;i < re_main;i ++)//最多连续写入一个页数据的大小
{
AT24CXX_ERR = I2C_WRITE_BYTE(Buffer[i]);
} I2C_STOP();
Wait_AT24CXX_WRITE_OK();//等待24C02完成擦写数据动作 if(re_main != Len)
{
address += re_main;//已经写入re_main个数据,
Buffer += re_main;
Len -= re_main; re_main = ;//写一个页的的大小也是16个字节 if(Len <= re_main)
{
re_main = Len;
}
}
else
{
break;//数据写入完成退出
}
}
while();
}

  AT24CXX_WRITE_BUFF()函数的思路如下:

  首先判断输入的地址是否超过存储IC的存储空间,如果超过则退出,如第6~9行。

  计算出输入的地址到存储器存储的结束地址剩余多少空间,如果要写入的数据比剩余空间还多,那么剩余多少空间就写入多少空间,需要写入的多余部分就去掉,如第11~16行。

  计算当前页还可以写入多少数据,如果当前页剩余的空间比Len要写入的数据长度还大,那么只要写入当前页就足够了,不需要再跨页写入数据。如第18~23行。

  通过一个while(1)循环连续写入数据到24C02中。

  第27~41行是将re_main个数据写入到24C02当中,由于是在一个页内操作,所以可以连续写入,写完之后在产生一个结束信号然后等待24C02擦写完成。

  如果不需要跨页写入就已经将数据全部写完,那么就可以直接break退出while循环,如第58行。

  如果需要跨页写入数据,还需要将地址、buffer、Len减去已经写入的数据量,然后下一个页可以写入整个页的数据量,也就是16个字节,通过比较判断一个页的剩余空间是否能够写完剩余的Len数据,如果不行就重复循环操作,如果可以写完,在写完之后就退出while循环。

EEPROM存储芯片24C02的更多相关文章

  1. 4.9版本的linux内核中eeprom存储芯片at24c512的驱动源码在哪里

    答:drivers/misc/eeprom/at24.c,内核配置项为CONFIG_EEPROM_AT24 Location: -> Device Drivers -> Misc devi ...

  2. 玩转X-CTR100 l STM32F4 l AT24C02 EEPROM存储

    我造轮子,你造车,创客一起造起来!塔克创新资讯[塔克社区 www.xtark.cn ][塔克博客 www.cnblogs.com/xtark/ ]      本文介绍X-CTR100控制器 板载EEP ...

  3. EEPROM的存储大小

    学习单片机时,常见的EEPROM如24C02的大小为2Kbit(有的也称2KB).这里的2KB到底能存储多少数据呢? 2KB中,B表示单位bit,K表示1024. 单片机编程中常用的数据类型为unsi ...

  4. EEPROM原理详解

    EEPROM(Electrically Erasable Programmable read only memory)即电可擦可编程只读存储器,是一种掉电后数据不丢失(不挥发)存储芯片. EERPOM ...

  5. EEPROM工作原理透彻详解

    原文链接点击这里 EEPROM(Electrically Erasable Programmable read only memory)即电可擦可编程只读存储器,是一种掉电后数据不丢失(不挥发)存储芯 ...

  6. STC8H开发(十二): I2C驱动AT24C08,AT24C32系列EEPROM存储

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  7. U盘详解

    摘要:U盘,称呼最早来源于朗科公司生产的一种新型存储设备,名曰“优盘”,使用USB接口进行连接.USB接口就连到电脑的主机后,U盘的资料可与电脑交换.而之后生产的类似技术的设备由于朗科已进行专利注册, ...

  8. 设备树..ing

    .dts==>.dtb ==>device_node ==>  platform_device ==> led_dev.c  ==>匹配 led_drv.c    (设备 ...

  9. 玩转 RTC时钟库 DS3231

    1.前言     接着博主的上一篇 玩转 RTC时钟库 + DS1302,这一篇我们重点讲解DS3231时钟模块.没有看过上一篇的同学,麻烦先去阅读一下,因为很多理论基础已经在上一篇做了详细讲解,这里 ...

随机推荐

  1. Linux环境下安装MySQL 5.7.28

    先进入MySQL官网: www.mysql.com 去下载安装包 进入DOWNLOADS选项,点击MySQL Community (GPL) Downloads. 点击进入MySQL Communit ...

  2. C++ 按行读取文件并打印

    #include<iostream> #include<fstream> #include<string> #include <vector> #inc ...

  3. 《前端之路》- TypeScript (三) ES5 中实现继承、类以及原理

    目录 一.先讲讲 ES5 中构造函数(类)静态方法和多态 1-1 JS 中原型以及原型链 例子一 1-2 JS 中原型以及原型链中,我们常见的 constructor.prototype.**prot ...

  4. Postgresql实战经验之alter table 开小差了

    Postgresql实战经验之alter table 开小差了 今天需要将一张有数据的表中一个字段varchar 类型转换为timestamp类型,但是pg的alter table 语句却开小差,出现 ...

  5. Netty Hello World 入门源码分析

    第一节简单提了什么是网络编程,Netty 做了什么,Netty 都有哪些功能组件.这一节就具体进入 Netty 的世界,我们从用 Netty 的功能实现基本的网络通信开始分析 各个组件的使用. 1. ...

  6. 一明单词本持续更新ing...

    introductionshuffingdeployspecifyingreliableclusters programming scalemachinesdeliveringsubmarineadd ...

  7. JavaScript 模式》读书笔记(4)— 函数2

    这篇,我们仍旧继续学习函数. 二.回调模式 函数都是对象,这表示它们可以作为参数传递给其它函数. function writeCode(callback) { // 执行一些事务... callbac ...

  8. Python编写“求一元二次方程的解”

    #求一元二次方程的解 import math def equation(a,b,c): h=b*b-4*a*c #一元二次方程的解,百度来的 if h>=0: x1=(-b+math.sqrt( ...

  9. python爬取网站页面时,部分标签无指定属性而报错

    在写爬取页面a标签下href属性的时候,有这样一个问题,如果a标签下没有href这个属性则会报错,如下: 百度了有师傅用正则匹配的,方法感觉都不怎么好,查了BeautifulSoup的官方文档,发现一 ...

  10. 01背包模板题 hdu2602 Bonecollector

    由于数组的滚动过程中当前值(i,j)的更新需要用到上一层的(i-1,j-wi)的值,所以在更新当前的j之前不能更新上一层的j之前的值,故01背包是从后向前更新的(重量取值是从大到小的). 代码如下: ...