IIC接口AT24C02存储芯片的操作

时间:2021-04-17 00:17:19

  从今天早上开始看AT24C02的手册,凭着有些撮的英语水平,24页的资料,愣是啃了半天,上午的时光就过去了。

  AT24C02是一款EEPROM芯片,IIC接口,就是两条线:SDA与SCL;不过对于单单操纵这款芯片而言,没有设计到IIC总线协议之中所谓的仲裁。因而,大体看下了芯片手册,心中就知道大概怎么操作了。

  另外,24C02的2K是指2Kbit。一般的话,存储器都用字节来衡量,所以其实24c02只有256byte。是不是比较小?这256byte又分成了32pages,每页有8byte。

  现在来看看该如何编写代码,首先得知道起始与停止条件

      IIC接口AT24C02存储芯片的操作                 

所谓起始条件:在SCL高电平期间,SDA的一个下降沿;

所谓停止条件:在SCL高电平期间,SDA的一个上升沿;

当然起始和停止条件与普通的位传送是不同的,位传送的时序如图所示

 IIC接口AT24C02存储芯片的操作

可以看到,在SCL的高电平期间,SDA是不允许变化的;而只有在SCL的电平期间,SDA才能够出现变化;

当传输完1字节之后,可能需要有一个应答信号。说到这儿,就需要明白几个概念:发送器、接受器、主器件、从器件。

发送器:在总线上发送数据的器件。

接受器:在总线上接受数据的器件。

主器件:控制信息交互的器件,产生SCL时钟信息,产生起始与停止条件。

从器件:受从器件控制的器件。

那这儿的应答信号,就是总线上的接受器每接受到一个字节后产生的应答。如图所示:

 IIC接口AT24C02存储芯片的操作

需要注意的是,在等待应答信号之前,需要将SDA置高,即释放掉总线。

24c02写的方式有两种:字节写、页写(就是连续写多个字节)。

掌握了字节写,对于页写就容易的多了。

字节写的时序如图所示:

IIC接口AT24C02存储芯片的操作

字节写的时序:首先是起始条件(由MCU产生),接着单片机向总线上传送器件地址,总线上地址相同的器件会有一个ACK(即应答信息),然后向器件写入字地址(告诉24c02想把信息写在那个地址,24c02刚好有256个字节,8bit的字地址信息刚好表示),同样会有一个应答信息,紧接着需要写入需要传送的数据(8位),同理会有一个应答信息。最后,需要主器件产生一个停止条件。Ok,写字节就结束了,是不是很简单,页写只不过是在这一步没有发送停止条件,而是接着发数据,这个时候24c02会知道你要写下一个字节,它会自动的加地址。

再来看看如何读取,读取有三种方式:当前地址读、随机读(我得承认这个名字不是太好)、顺序读。

当前地址读取是指你可以读取到芯片内部地址计数器(最近一次操作留下的值)加上1的地址上的值。看清楚是加上1地址上的值,不过不太明白有何用处。

接下来的随机读取跟是让我迷惑(刚看手册时候),随机的意思不就是产生一个无脑的地址,然后去读取,这样的话,读取有什么意义?不过我想错了,你可以看看时序图:

 IIC接口AT24C02存储芯片的操作

看到了,先发写命令了,也传送了字地址,然后没有发写的内容。转而,重新开始发起始条件、器件地址(这次最后一位是读),然后再接受总线传送来的数据,此后主控器作为接受方,不用发应答信号,直接发一个停止条件,就ok了。

所以要想指定一个地址,就可以用随机读这个方法。不过随机读说法不太形象,改为指定地址读跟容易理解。

顺序读跟页写是对应的,只是在随机读或当前地址读后面接着接受数据。

 

写了三页,貌似不是很长。那就贴上代码,来充长。

个人喜欢写一个头文件,然后,再写一个c文件。

头文件部分:

#ifndef __hal_24c02_h__

#define __hal_24c02_h__

 

#include"reg52.h"

#include"hal.h"

#include"datatype.h"

#include"delay.h"

 

sbit scl=P3^7;//IIC时钟线

sbit sda=P3^6;//IIC数据线

 

#define SCL scl

#define SDA sda

 

//起始条件,SCL高电平时候,SDA的一个下降沿

#define HAL_24C02_START() {SDA=1;SCL=1;SDA=0;}

//停止条件,SCL高电平时候,SDA的一个上升沿

#define HAL_24C02_STOP() {SDA=0;SCL=1;SDA=1;}

#define HAL_24C02_BYTE_ADDR(addr) hal_24c02_send_char(addr)

#define HAL_24C02_ACK() hal_24c02_acknowledge(0)

 

void hal_24c02_init();

void hal_24c02_send_char(uchar val);

uchar hal_24c02_receive_char();

void hal_24c02_search_device(bit a2,bit a1,bit a0,bit rw);

void hal_24c02_acknowledge(bit mcu);

 

//学会使用以下两个函数即可

//需要注意写完之后,等待5ms之后,再去读取

void hal_24c02_byte_write(bit a2,bit a1,bit a0,uchar byte_addr,uchar byte_data);

uchar hal_24c02_byte_read(bit a2,bit a1,bit a0,uchar byte_addr);

 

#endif

 

 

C文件部分:

#include"hal_24c02.h"

 

//初始化,需将时钟线和数据线拉高

void hal_24c02_init()

{

         SCL=1;

         SDA=1;

}

 

//IIC协议要求从高位开始传送,传送1字节

void hal_24c02_send_char(uchar val)

{       

         uchar i;

         SCL=0;//此时,SCL被拉低,才允许SDA变化

         for(i=0;i<8;i++)

         {

                    if(val&(0x80>>i))

                           SDA=1;

                    else

                           SDA=0;

                   SCL=1;//拉高SCL,不允许SDA变化

                   delay_ms(1);

                   SCL=0;//拉低SCL,开始下1bit传送

         }

         //不包括应答脉冲,此时,SCL被拉低,SDA未被释放

         SDA=1;//释放数据线

         //SCL处于低电平;SDA处于高电平

}

 

uchar hal_24c02_receive_char()

{

          //SCL低电平 SDA高电平

          uchar tmp=0,i;

          for(i=0;i<8;i++)

          {

                 SCL=1;

                   if(SDA)

                            tmp=tmp|(0x80>>i);

                   SCL=0;

          }

          return tmp;

          //SCL低电平 SDA高电平

}

 

//a2、a1、a0:表示器件地址

//rw:1 代表读操作;0 代表写操作

void hal_24c02_search_device(bit a2,bit a1,bit a0,bit rw)

{

         uchar tmp;

         tmp=0xa0|(a2?0x08:0x00)|(a1?0x04:0x00)|(a0?0x02:0x00)|rw;

         hal_24c02_send_char(tmp);

}

 

//参数mcu=1,表示由主机主动产生应答信号,此时还需要将SDA拉低

//参数mcu=0,表示非主机参数应答信号,此时主机只需要产生SCL脉冲

//返回1:表示有应答;0:无应答

void hal_24c02_acknowledge(bit mcu)

{

         //SCL处于低电平;SDA处于高电平

         if(mcu)      SDA=0;//产生1应答信号

         SCL=1;

         delay_ms(1);

         while(SDA);//等待应答

         SCL=0;

         if(mcu)      SDA=1;//释放总线

         //SCL处于低电平;SDA处于高电平

}

 

//参数a2,a1,a0:器件地址

//参数byte_addr:字节地址

//参数byte_data:需要写的数据

void hal_24c02_byte_write(bit a2,bit a1,bit a0,uchar byte_addr,uchar byte_data)

{

          HAL_24C02_START()

          hal_24c02_search_device(a2,a1,a0,0);

          HAL_24C02_ACK();

          HAL_24C02_BYTE_ADDR(byte_addr);

          HAL_24C02_ACK();

          hal_24c02_send_char(byte_data);

          HAL_24C02_ACK();

          HAL_24C02_STOP()

}

 

//参数a2 a1 a0为器件地址;

//参数byte_char为需要读取的字节地址

//返回一个uchar型

uchar hal_24c02_byte_read(bit a2,bit a1,bit a0,uchar byte_addr)

{

          uchar val=0;

          HAL_24C02_START()

          hal_24c02_search_device(a2,a1,a0,0);//伪写

          HAL_24C02_ACK();

          HAL_24C02_BYTE_ADDR(byte_addr);//伪写的目的,就是为了传送地址

          HAL_24C02_ACK();

          HAL_24C02_START();

          hal_24c02_search_device(a2,a1,a0,1);//真读

          HAL_24C02_ACK();

          val=hal_24c02_receive_char();

          //NO ACK

          HAL_24C02_STOP()

          return val;

}