EEPROM存储芯片24C02

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任意存储空间地址内的数据,代码如下:  

 1 uint8_t AT24CXX_READ_ONE_BYTE(uint16_t address)
 2 {    
 3     uint8_t dat;
 4     
 5     I2C_START();
 6     AT24CXX_ERR = I2C_WRITE_BYTE(AT24CXX_WIRTE_CMD);
 7     if(AT24CXX_ERR != 0)//没有响应直接退出
 8     {
 9         AT24CXX_ERR = I2C_WRITE_BYTE(address & 0xFF);
10         if(AT24CXX_ERR != 0)
11         {    
12             I2C_START();
13             
14             AT24CXX_ERR = I2C_WRITE_BYTE(AT24CXX_READ_CMD);    
15             if(AT24CXX_ERR != 0)
16             {         
17                dat = I2C_READ_BYTE(0); 
18                I2C_STOP();
19             }
20         }
21     }
22     
23     return dat;
24 }

  第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内多个存储空间地址数数的代码如下:

 1 void AT24CXX_READ_BUFF(uint16_t address,uint8_t *buffer,uint16_t Len)
 2 {    
 3     uint16_t i;
 4     
 5     I2C_START();
 6     AT24CXX_ERR = I2C_WRITE_BYTE(AT24CXX_WIRTE_CMD);
 7     if(AT24CXX_ERR != 0)//没有响应直接退出
 8     {
 9         AT24CXX_ERR = I2C_WRITE_BYTE(address & 0xFF);
10         if(AT24CXX_ERR != 0)
11         {    
12             I2C_START();
13             AT24CXX_ERR = I2C_WRITE_BYTE(AT24CXX_READ_CMD);   
14             if(AT24CXX_ERR != 0)
15             {               
16                 for(i=0;i<Len;i++)
17                 {
18                     buffer[i] = I2C_READ_BYTE(0); 
19                 }
20             }
21             
22             if(AT24CXX_ERR != 0)
23             {         
24                 I2C_STOP();
25             }            
26         }
27     }
28 }

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

4、24C02数据写入操作

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

 1 void AT24CXX_WRITE_ONE_BYTE(uint16_t address,uint8_t dat)
 2 {    
 3     I2C_START();
 4     AT24CXX_ERR = I2C_WRITE_BYTE(AT24CXX_WIRTE_CMD);
 5     if(AT24CXX_ERR != 0)//没有响应直接退出
 6     {
 7         AT24CXX_ERR = I2C_WRITE_BYTE(address & 0xFF);
 8         if(AT24CXX_ERR != 0)
 9         {    
10             AT24CXX_ERR = I2C_WRITE_BYTE(dat);    
11             if(AT24CXX_ERR != 0)
12             {         
13                I2C_STOP();
14             }
15         }
16     }
17 }

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

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

1     for(i = 0;i < 256;i ++)
2     {    
3         AT24C02_BUFF[i] = i;
4         AT24CXX_WRITE_ONE_BYTE(i,AT24C02_BUFF[i]);
5     }

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

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

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

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

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

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

 1 void Wait_AT24CXX_WRITE_OK(void)
 2 {
 3     uint8_t Wait_Cnt;
 4 
 5     Wait_Cnt = 50; 
 6     do
 7     {
 8         I2C_START();
 9         AT24CXX_ERR = I2C_WRITE_BYTE(AT24CXX_WIRTE_CMD);
10         if(AT24CXX_ERR != 0)
11         {
12             I2C_STOP();//接收到响应信号退出
13             break;
14         }
15             
16     }while(Wait_Cnt--);
17 
18 }

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

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

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

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的代码如下:  

 1 void AT24CXX_WRITE_BUFF(uint16_t address,uint8_t *Buffer,uint16_t Len)
 2 {
 3     uint8_t i;
 4     uint16_t re_main;
 5     
 6     if(address >= 256)//对输入的地址进行限制,24C02只有256个字节的存储空间,其它型号的存储器IC可以通过查资料
 7     {
 8         return;
 9     }
10     
11     re_main = 256 - address;//计算出还有多少存储空间
12     
13     if(Len > re_main)//如果要写入的数据量超过剩余存储空间,则只写入剩余存储空间数量的数据
14     {
15         Len = re_main;
16     }
17     
18     re_main = 16 - address%16;//计算当前页还可以写入多少个数据
19     
20     if(Len <= re_main)//如果要写入的数据小于等于当前页剩余的存储空间,则只写入Len个字节数据就好,不需要跨页操作
21     {
22         re_main = Len;
23     }
24     
25     do
26     {
27         I2C_START();
28         AT24CXX_ERR = I2C_WRITE_BYTE(AT24CXX_WIRTE_CMD);
29         if(AT24CXX_ERR == 0)//没有响应直接退出
30         {
31             break;
32         }
33         
34         I2C_WRITE_BYTE(address & 0xFF);
35         for(i = 0;i < re_main;i ++)//最多连续写入一个页数据的大小
36         {
37             AT24CXX_ERR = I2C_WRITE_BYTE(Buffer[i]);
38         }
39         
40         I2C_STOP();
41         Wait_AT24CXX_WRITE_OK();//等待24C02完成擦写数据动作    
42         
43         if(re_main != Len)
44         {
45             address += re_main;//已经写入re_main个数据,
46             Buffer += re_main;
47             Len -= re_main;
48             
49             re_main = 16;//写一个页的的大小也是16个字节
50             
51             if(Len <= re_main)
52             {
53                 re_main = Len;
54             }    
55         }
56         else
57         {
58             break;//数据写入完成退出
59         }
60     }
61     while(1);    
62 }

  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循环。

posted @ 2019-09-14 06:10  Mars-King  阅读(17335)  评论(2编辑  收藏  举报