I2C通信时序讲解,代码编写,PCF8591芯片使用范例,AT24C02芯片使用范例

来源:互联网 发布:股票交易大师软件下载 编辑:程序博客网 时间:2024/06/06 17:23

I2C总线是PHLIPS公司推出的一种串行总线,I2C总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL

每个接到I2C总线上的器件都有唯一的地址。主机与其它器件间的数据传送可以是由主机发送数据到其它器件,这时主机即为发送器。由总线上接收数据的器件则为接收器。


24C020的地址可以人为改变,只需改变A2,A1,A0的接线接到GNDVCC即可

由此原理图可以看出

24C020的地址  读地址 0xaf,写地址0xae

I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。

IIC总线协议规定,没传送一个字节数据后,都要有一个应答信号以确定数据传送是否被对方收到。应答信号由接受设备产生,在SCL为高电平期间,接受设备将SDA拉低为低电平,表示数据传输正确,产生应答

数据位的有效性规定

   I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

要掌握IIC的通信协议,需要掌握以下6个通信信号:

1.      起始信号

voidIIC_start()

{

    SDA = 1;

 SCL = 1;

 delay_us(1);

 SDA = 0;

 delay_us(1);

 

 SCL =0; //方便下次使用

}

2.      终止信号

voidIIC_stop()

{

    SDA = 0;

 SCL = 1;

 delay_us(1);

 SDA = 1;

 delay_us(1);

 

 SCL = 0;

}

3.写数据
void IIC_senddat(unsigned char dat)

{

   unsigned char i;

      //SCL= 0;

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

      {

         if((dat << i) & 0x80)

                SDA = 1;

             else

                SDA = 0;

             SCL= 1;

             delay_us(1);

             SCL= 0;

             delay_us(1);

      }

      SDA= 1;

      _nop_();

      _nop_();

      SCL= 1;

      delay_us(1);

      if(SDA== 1)

         ack = 0;   //表示无应答

      else

         ack = 1;

      SCL= 0;

      delay_us(10);

}

4.读数据
unsigned char IIC_revdat()

{

   unsigned char i;

      unsignedchar temp = 0;

      SDA= 1;

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

      {

         SCL = 0;

             delay_us(1);//通知被读器件变化数据

             SCL= 1;

             delay_us(1);

             temp<<= 1;

             if(1== SDA)

             temp= temp + 1;

      }

      SCL= 0;

      delay_us(10);

      returntemp;

}

5.应答信号
void IICACK()

{

   SDA=0;

   SCL=1;

   delay_us(1);

   SCL=0;

}

6.非应答信号

void IICNOACK()

{

  SDA=1;

   SCL=1;

   delay_us(1);

   SCL=0;

}

 


//devaddr 表示芯片的地址 0xae写,0xaf读, wordaddr24c02内部地址 num写的个数

unsigned char IIC_SendStr(unsigned chardevaddr, unsigned char wordaddr,

unsigned char *s ,unsigned char num)

{

   unsigned char i;

   IIC_start();    //start()信号

      IIC_senddat(devaddr);    //发送器件地址

      if(ack== 0)  return 0; //发送无应答

      IIC_senddat(wordaddr);   //发送要写的器件内部存储地址(0-255个字节)

      if(ack== 0)  return 0; //发送无应答

      for(i= 0; i < num; i++)   //开始发送数据

      {

         IIC_senddat(*s);

             if(ack== 0)  return 0; //发送无应答

             s++;

      }

 

      IIC_stop();    //停止信号

      return1; //发送成功

}

 

unsigned char IIC_RevStr(unsigned char devaddr, unsignedchar wordaddr,

unsigned char *s ,unsigned char num)

{

   unsigned char i;

   IIC_start();   //开始信号

      IIC_senddat(devaddr);    //发送器件写地址

      if(ack== 0)  return 0; //发送无应答

      IIC_senddat(wordaddr);  //发送要开始读的器件内部存储地址(0-255个字节)

 

      if(ack== 0)  return 0; //发送无应答

 

      IIC_start();   //开始信号

      IIC_senddat(devaddr+1);   //发送器件读地址

      if(ack== 0)  return 0; //发送无应答

 

   for(i = 0; i < num-1; i++)    //每读一次数据,要发送一次应答信号,向从器件表示

      {                         //接收到数据

         *s = IIC_revdat();       //但最后一次读取数据不能返回应答信号,表示读取结束

             IICACK();              //所以这里是num-1,最后一次在循环外,接受完返

             s++;                  //回非应答信号

      }

      *s= IIC_revdat();

      IICNOACK();               //读完最后一个数据,发送非应答信号

 

      IIC_stop();                //发送停止信号

      return1; //发送成功

}

注意事项

1.IIC时序的重要特点是在时钟SCK为高电平期间SDA作出各种变化来表示起始,终止,应答,非应答,发送数据时是在时钟为高电平期间让SDA稳定,读数据是在SCK为高电平期间采样从设备的数据,所以起始,终止,应答,非应答,发送数据都要先操作SDA,再操作SCK,如果先操作SCK,则会导致起始,终止,应答,非应答,发送数有干扰。

2.2.接收数据为读SDA线的数据,不会有这个问题。 

3.3.IIC时序的数据传送从高位开始

4.很多人会遇到这样一个现象:连续从EEPROM读取数据,第一个读出来的是对的,往后读出来的都是0,原因是当读取第一个字节时,主机给从机的应答信号将SDA拉低,即SDA线被钳住,从从机读取数据时,SDA给出的高电平无法将SDA线拉高,导致读出来的数据都为0,所以在读取一个新的字节时,一定要将SDA拉高,才可以正常接收EEPROM的数据。 

应用举例:

#include "bsp.h"

void main()

{

    uchar write[10] = {0,1,2,3,4,5,6,7,8,9};

      ucharread[10] = {0};

      ucharlcd[10] = {0};

      uchari = 0;

bsp_init(); //LCD1602显示屏初始化

 

IIC_SendStr(0xae,0,write,10); //write数组里面的数据写到AT24C02里面第0个字节

delay_ms(100);            //开始的地方去

IIC_RevStr(0xae,0,read,10);  //把数据都出来

 

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

{

      lcd[i] = read[i] + 0x30;   // int型转化为char型,方便在LCD1602液晶屏}

}                         // 上显示出来

LCDwritestring(0,0,lcd);     //送到液晶屏上显示

while(1);

  

 

PCF8591ADC芯片.c

--------------------

#include "bsp.h"

 

#define ADRESSWR 0x90

#define ADRESSRD 0x91

extern char ack;

uchar ReadAdc(uchar ch)

{

     uchar val;

      IIC_start();

      IIC_senddat(ADRESSWR);

      if(ack ==0) return 0;

      IIC_senddat(0x40|ch);  //control byte

      if(ack ==0) return 0;

 

      IIC_start();             

      IIC_senddat(ADRESSRD);     //读取

      if(ack ==0) return 0;

      val=IIC_revdat();

      IICNOACK();

      IIC_stop();

 

      return val;

}

uchar SetDac(uchar val)

{

   IIC_start();

      IIC_senddat(ADRESSWR);

      if(ack ==0) return 0;

      IIC_senddat(0x40);  //control byte

      if(ack ==0) return 0;

      IIC_senddat(val);

      if(ack ==0) return 0;

      IIC_stop();

      return 1;

}


#include "bsp.h"

 

void main()

{

   uchar write[10] ={22,10,35};

      uchar read[10] = {0};

      uchar lcd[10] = {0};

      uchar i = 0;

     uchar val;

      uchar valshow[16];

      uchar brthled = 0;

      bsp_init();

 

      while(1)

      {

             val =ReadAdc(0); //读取通道0AD    

             sprintf(valshow,"v%2d",(int)val);

             LCDwritestring(0,1,valshow);

            SetDac(val);

             sprintf(valshow,"b%2d",(int)brthled);

             LCDwritestring(11,1,valshow);

      }