yaoxihua37 发表于 2015-12-23 09:29:40

IIC接口AT24C02存储芯片的操作

/**转载博客名“灰色的鱼”的文章,个人觉得可以在看到鸿哥的帖子,不熟悉AT24C02存储芯片的时候,可以很好的理解这款芯片**/从今天早上开始看AT24C02的手册,凭着有些撮的英语水平,24页的资料,愣是啃了半天,上午的时光就过去了。  AT24C02是一款EEPROM芯片,IIC接口,就是两条线:SDA与SCL;不过对于单单操纵这款芯片而言,没有设计到IIC总线协议之中所谓的仲裁。因而,大体看下了芯片手册,心中就知道大概怎么操作了。  另外,24C02的2K是指2Kbit。一般的话,存储器都用字节来衡量,所以其实24c02只有256byte。是不是比较小?这256byte又分成了32pages,每页有8byte。  现在来看看该如何编写代码,首先得知道起始与停止条件      http://images.cnitblog.com/blog/461108/201301/31184257-9afa97828829449390576039018abcb4.png               所谓起始条件:在SCL高电平期间,SDA的一个下降沿;所谓停止条件:在SCL高电平期间,SDA的一个上升沿;当然起始和停止条件与普通的位传送是不同的,位传送的时序如图所示 http://images.cnitblog.com/blog/461108/201301/31184336-f7a5a5c582b2497a8f4ee8b7f8616d6f.png可以看到,在SCL的高电平期间,SDA是不允许变化的;而只有在SCL的电平期间,SDA才能够出现变化;当传输完1字节之后,可能需要有一个应答信号。说到这儿,就需要明白几个概念:发送器、接受器、主器件、从器件。发送器:在总线上发送数据的器件。接受器:在总线上接受数据的器件。主器件:控制信息交互的器件,产生SCL时钟信息,产生起始与停止条件。从器件:受从器件控制的器件。那这儿的应答信号,就是总线上的接受器每接受到一个字节后产生的应答。如图所示: http://images.cnitblog.com/blog/461108/201301/31184415-28e356e22ac84da6bc7e0551f9f799d0.png需要注意的是,在等待应答信号之前,需要将SDA置高,即释放掉总线。24c02写的方式有两种:字节写、页写(就是连续写多个字节)。掌握了字节写,对于页写就容易的多了。字节写的时序如图所示:http://images.cnitblog.com/blog/461108/201301/31184510-0ce36f9ace744f908e431c501682718f.png字节写的时序:首先是起始条件(由MCU产生),接着单片机向总线上传送器件地址,总线上地址相同的器件会有一个ACK(即应答信息),然后向器件写入字地址(告诉24c02想把信息写在那个地址,24c02刚好有256个字节,8bit的字地址信息刚好表示),同样会有一个应答信息,紧接着需要写入需要传送的数据(8位),同理会有一个应答信息。最后,需要主器件产生一个停止条件。Ok,写字节就结束了,是不是很简单,页写只不过是在这一步没有发送停止条件,而是接着发数据,这个时候24c02会知道你要写下一个字节,它会自动的加地址。再来看看如何读取,读取有三种方式:当前地址读、随机读(我得承认这个名字不是太好)、顺序读。当前地址读取是指你可以读取到芯片内部地址计数器(最近一次操作留下的值)加上1的地址上的值。看清楚是加上1地址上的值,不过不太明白有何用处。接下来的随机读取跟是让我迷惑(刚看手册时候),随机的意思不就是产生一个无脑的地址,然后去读取,这样的话,读取有什么意义?不过我想错了,你可以看看时序图: http://images.cnitblog.com/blog/461108/201301/31184551-5dc74e3b01494189a772374fd3f28c71.png看到了,先发写命令了,也传送了字地址,然后没有发写的内容。转而,重新开始发起始条件、器件地址(这次最后一位是读),然后再接受总线传送来的数据,此后主控器作为接受方,不用发应答信号,直接发一个停止条件,就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;}
页: [1]
查看完整版本: IIC接口AT24C02存储芯片的操作