I2C串行总线

【一、简介】

  I2C总线(Inter IC Bus)是PHLIPS公司推出的一种串行总线标准,是具备多主机系统所需的包括总线裁决和高低速器件同步功能的高性能串行总线。I2C总线支持多主和主从(常用)两种工作方式。

  在主从工作方式中,可以有多个I2C总线器件同时并联接到I2C总线上,但是只有一个主器件(单片机),其它器件都是具有I2C总线的外围从器件。在主从工作方式中,主器件启动数据的发送(发出启动信号),产生时钟信号,发出停止信号。

  所有与I2C兼容的器件都具有标准接口,通过地址来识别通信对象,使它们可以经由I2C总线互相直接通信。

  I2C总线由数据线SDA和时钟线SCL两条线构成通信线路,既可以发送数据,也可以接收数据。可以进行双向通信,在信息传输过程中,I2C总线上并联的每一个器件既是被控器(或主控器),又是发送器(或接收器),这取决于它所要完成的功能。CPU发出的控制信号分为地址码(选址,即选通信目标器件)和数据码(通信内容)两部分。

【二、特点】

  接口线少、控制简单、器件封装形式小、通信速率较高等。

【三、I2C总线硬件结构图】

  说明:SDA是数据线,SCL是时钟线。总线上的各器件都采用漏极开路结构与总线相连,因而SDA和SCL需要接上拉电阻(如1.5K),使得总线在空闲状态下保持高电平。连接到总线上的任一器件输出的低电平,都将使总线的信号电平变低——即各器件的SDA及SCL都是线“与”关系。

           

【四、数据位的有效性规定】

   I2C总线进行数据传送时,SCL线为高电平期间,SDA线上的数据必须保持稳定(否则就可能变成起始或停止等无效信号了)。只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

      

【五、起始和终止信号】

  SCL线为高电平期间,SDA线由高电平向低电平的变化(负跳变)表示起始信号;

  SCL线为高电平期间,SDA线由低电平向高电平的变化(正跳变)表示终止信号。

    

  起始和终止信号都是由主机发出的,在起始信号产生后,总线就处于被占用的状态;在终止信号产生后,总线就处于空闲状态。

  接收器件收到一个完整的数据字节后,有可能需要完成一些其它工作,如处理内部中断服务等,可能无法立刻接收下一个字节,这时接收器件可以将SCL线拉成低电平,从而使主机处于等待状态。直到接收器件准备好接收下一个字节时,再释放SCL线使之为高电平,从而使数据传送可以继续进行。

【六、发送寻址信号】

  主机发送启动(起始)信号后,再发出寻址信号。器件地址有7位和10位两种,常见的是7位的。

    

  寻址字节的位定义:D7~D1位组成从机的地址。D0位是数据传送方向位,为“0”时表示主机向从机写数据,为“1”时表示主机由从机读数据。主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,如果相同,则认为自己正被主机寻址,根据R/T位将自己确定为发送器或接收器。

  从机的地址由固定部分和可编程部分组成。在一个系统中可能希望接入多个相同的从机,从机地址中可编程部分决定了可接入总线该类器件的最大数目。如一个从机的7位寻址位有4位是固定位,3位是可编程位,这时仅能寻址8个同样的器件,即可以有8个同样的器件接入到该I2C总线系统中。

【七、应答信号】

  I2C总线协议规定,每传送一个8位的字节数据(含地址及命令字)后,都要有一个应答信号(即一帧共有9位),以确定数据传送是否被对方收到。数据传送时,先传送最高位(MSB)。应答信号由接收设备产生,在SCL信号为高电平期间,接收设备将SDA拉为低电平,表示数据传输正确,产生应答。

  若由于某种原因从机不对主机寻址信号应答时(如从机正在进行实时性的处理工作而无法接收总线上的数据),它必须将数据线置于高电平,而由主机产生一个终止信号以结束总线的数据传送。

  

  如果从机对主机进行了应答,但在数据传送一段时间后无法继续接收更多的数据时,从机可以通过对无法接收的第一个数据字节的“非应答”通知主机,主机则应发出终止信号以结束数据的继续传送。

  当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。

【八、软件模拟I2C总线协议——C程序】

#include <intrins.h>
sbit SDA = P2^4;   //需改成实际对应的IO脚,sbit是C51的语法,该语句的意思是用SDA标识符表示P2口的引脚4
sbit SCL = P2^5;    //需改成实际对应的IO脚
uint8_t NackFlag;

void delay5us()   //延时5us以上
{
  _nop_();
  _nop_();
  _nop_();
  _nop_();
  _nop_();
}

void IIC_Start()   //启动IIC总线
{
  SDA = 1;

  delay5us();
  SCL = 1;
  delay5us();
  SDA = 0;
  delay5us();
  SCL = 0;
}

void IIC_Stop()   //停止IIC总线
{

  SCL = 0;
  delay5us();

  SDA = 0;

  delay5us();
  SCL = 1;
  delay5us();
  SDA = 1;
  delay5us();
}

void IIC_Ack()   //主器件为发送方,主控器发送完数据后,主控器件等待从器件的应答
{
  uint8_t errtime = 200;  //数值根据具体CPU的工作频率来定,保证有5us以上即可
  SDA = 1;
  SCL = 1;
  NackFlag=0;
  while(SDA)
  {
    errtime--;
    if(errtime==0)
    {
      IIC_Stop();
      NackFlag=1;   //没有应答
      return;
    }
  }
  SCL = 0;

  delay5us();               //应答
}

void IIC_SendByte(uint8_t num)   //主控器件发送数据到IIC总线
{

  uint8_t i;
  for(i=0; i<8; i++)
  {
    SCL = 0;
    delay5us();
    SDA = num & 0x80;

    delay5us();
    SCL = 1;
    delay5us();
    num <<= 1;
  }
  SCL = 0;

  delay5us();

  IIC_Ack();
}

uint8_t IIC_ReceiveByte(void)     //主控器件接收IIC总线传来的数据
{
  uint8_t i = 0;

  uint8_t datax = 0;
  SDA = 1;
  for(i=0; i<8; i++)
  {
    datax <<= 1;
    SCL = 0;
    delay5us();
    SCL = 1;
    delay5us();
    datax |= SDA;
  }
  SCL = 0;

  delay5us();
  return datax;
}

void sendAckIIC(void)    //主器件为接收方,从器件发送完数据后,从器件等待主器件的应答信号
{

  SDA = 0;
  delay5us();
  SCL = 1;
  delay5us();
  SCL = 0;
}

Chance favors the prepared mind.
原文地址:https://www.cnblogs.com/jamiechen/p/IIC.html