zoukankan      html  css  js  c++  java
  • i2c协议简要分析(转载)

    声明

    本文大部分内容为转载,因此标定为转载
    源地址:
    http://www.cnblogs.com/zym0805/archive/2011/07/31/2122890.html
    http://blog.csdn.net/lxl123/article/details/22884719

    I2C协议

    简介

    I2C是由Philips公司发明的一种串行数据通信协议,仅使用两根信号线:SerialClock(简称SCL)和SerialData(简称SDA)。I2C是总线结构,1个Master,1个或多个Slave,各Slave设备以7位地址区分,地址后面再跟1位读写位,表示读(=1)或者写(=0),所以我们有时也可看到8位形式的设备地址,此时每个设备有读、写两个地址,高7位地址其实是相同的。

    数据格式

    I2C数据格式如下:
    无数据:SCL=1,SDA=1;
    开始位(Start):当SCL=1时,SDA由1向0跳变;
    停止位(Stop):当SCL=1时,SDA由0向1跳变;
    数据位:当SCL由0向1跳变时,由发送方控制SDA,此时SDA为有效数据,不可随意改变SDA;
    当SCL保持为0时,SDA上的数据可随意改变;
    地址位:定义同数据位,但只由Master发给Slave;
    应答位(ACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=0;
    否应答位(NACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=1。

    传输过程

    当数据为单字节传送时,格式为:
    开始位,8位地址位(含1位读写位),应答,8位数据,应答,停止位。
    当数据为一串字节传送时,格式为:
    开始位,8位地址位(含1位读写位),应答,8位数据,应答,8位数据,应答,……,8位数据,应答,停止位。

    注意事项

    1. SCL一直由Master控制,SDA依照数据传送的方向,读数据时由Slave控制SDA,写数据时由Master控制SDA。当8位数据传送完毕之后,应答位或者否应答位的SDA控制权与数据位传送时相反。
    2. 开始位“Start”和停止位“Stop”,只能由Master来发出。
    3. 地址的8位传送完毕后,成功配置地址的Slave设备必须发送“ACK”。否则否则一定时间之后Master视为超时,将放弃数据传送,发送“Stop”。
    4. 当写数据的时候,Master每发送完8个数据位,Slave设备如果还有空间接受下一个字节应该回答“ACK”,Slave设备如果没有空间接受更多的字节应该回答“NACK”,Master当收到“NACK”或者一定时间之后没收到任何数据将视为超时,此时Master放弃数据传送,发送“Stop”。
    5. 当读数据的时候,Slave设备每发送完8个数据位,如果Master希望继续读下一个字节,Master应该回答“ACK”以提示Slave准备下一个数据,如果Master不希望读取更多字节,Master应该回答“NACK”以提示Slave设备准备接收Stop信号。
    6. 当Master速度过快Slave端来不及处理时,Slave设备可以拉低SCL不放(SCL=0将发生“线与”)以阻止Master发送更多的数据。此时Master将视情况减慢或结束数据传送。

    伪代码实现

    起始位,停止位

    /* 
    函数:I2C_Start() 
    功能:产生I2C总线的起始状态 
    说明: 
     SCL处于高电平期间,当SDA出现下降沿时启动I2C总线 
     不论SDA和SCL处于什么电平状态,本函数总能正确产生起始状态 
     本函数也可以用来产生重复起始状态 
     本函数执行后,I2C总线处于忙状态 
    */  
    void I2C_Start()  
    {  
     EA=0;  
      
     I2C_SCL = 1;  
     I2C_Delay();  
     I2C_SDA = 1;  
     I2C_Delay();  //起始条件建立时间大于4.7us延时  
     I2C_SDA = 0;  //发送起始信号  
     I2C_Delay();  
     I2C_SCL = 0;  //钳住I2C总线,准备发送或接收数据  
     I2C_Delay();  
     I2C_Delay();  
     I2C_Delay();  
    }  
      
    /* 
    函数:I2C_Stop() 
    功能:产生I2C总线的停止状态 
    说明: 
     SCL处于高电平期间,当SDA出现上升沿时停止I2C总线 
     不论SDA和SCL处于什么电平状态,本函数总能正确产生停止状态 
     本函数执行后,I2C总线处于空闲状态 
    */  
    void I2C_Stop()  
    {  
     unsigned int t = I2C_STOP_WAIT_VALUE;  
      
     I2C_SDA = 0;  //发送结束条件的数据信号  
     I2C_Delay();  
      
     I2C_SCL = 1;  //发送结束条件的时钟信号  
     I2C_Delay();  
     I2C_SDA = 1;  //发送I2C总线结束信号  
     I2C_Delay();  
     EA=1;  
     while ( --t != 0 );  //在下一次产生Start之前,要加一定的延时  
    }
    
    

    单字节读和写

    /* 
    函数:I2C_Write() 
    功能:向I2C总线写1个字节的数据 
    参数: 
     dat:要写到总线上的数据 
    */  
    void I2C_Write(unsigned char dat)  
    {  
      /*发送1,在SCL为高电平时使SDA信号为高*/  
      /*发送0,在SCL为高电平时使SDA信号为低*/  
     unsigned char t ;  
     for(t=0;t<8;t++)  
     {  
      
      I2C_SDA = (bit)(dat & 0x80);  
      I2C_Delay();  
      I2C_SCL = 1;  //置时钟线为高,通知被控器开始接收数据位  
      I2C_Delay();  
      I2C_SCL = 0;    
      I2C_Delay();  
      dat <<= 1;  
     }  
      
      
    }  
      
      
    /* 
    函数:I2C_Read() 
    功能:从从机读取1个字节的数据 
    返回:读取的一个字节数据 
    */  
    unsigned char I2C_Read()  
    {  
     unsigned char dat=0;  
     unsigned char t ;  
     bit temp;  
     I2C_Delay();  
     I2C_Delay();  
     I2C_SDA = 1; //在读取数据之前,要把SDA拉高  
      
     I2C_Delay();  
       
     for(t=0;t<8;t++)  
     {  
      I2C_SCL = 0; /*接受数据*/  
      I2C_Delay();  
      I2C_SCL = 1;//置时钟线为高使数据线上升沿数据有效  
      I2C_Delay();  
      temp = I2C_SDA;  
      dat <<=1;  
      if (temp==1) dat |= 0x01;     
     }  
       
      I2C_SCL = 0;  
      I2C_Delay();  
      
     return dat;  
    }  
    

    ACK & NACK

    /* 
    函数:I2C_GetAck() 
    功能:读取从机应答位 
    返回: 
     0:从机应答 
     1:从机非应答 
    说明: 
     从机在收到每个字节的数据后,要产生应答位 
     从机在收到最后1个字节的数据后,一般要产生非应答位 
    */  
    bit I2C_GetAck()  
    {  
     bit ack;  
     unsigned char Error_time=255;  
      
        I2C_Delay();    
     I2C_SDA = 1; /*8位发送完后释放数据线,准备接收应答位 释放总线*/  
     I2C_Delay();  
     I2C_SCL = 1; /*接受数据*/  
     I2C_Delay();  
     do  
     {    
      ack = I2C_SDA;  
        Error_time--;  
       if(Error_time==0)  
       {  
         I2C_SCL = 0;  
       I2C_Delay();  
       return 1;  
       }  
     }while(ack);   //判断是否接收到应答信号  
      
      
     I2C_SCL = 0;  //清时钟线,钳住I2C总线以便继续接收  
     I2C_Delay();  
     I2C_Delay();  
     I2C_Delay();  
     return 0;  
    }  
      
      
    /* 
    函数:I2C_PutAck() 
    功能:主机产生应答位或非应答位 
    参数: 
     ack=0:主机产生应答位 
     ack=1:主机产生非应答位 
    说明: 
     主机在接收完每一个字节的数据后,都应当产生应答位 
     主机在接收完最后一个字节的数据后,应当产生非应答位 
    */  
    void I2C_PutAck(bit ack)  
    {  
      
     I2C_SDA = ack;  //在此发出应答或非应答信号  
     I2C_Delay();  
     I2C_SCL = 1;   //应答  
     I2C_Delay();   
      
     I2C_SCL = 0;  //清时钟线,钳住I2C总线以便继续接收  ,继续占用  
     I2C_Delay();  //等待时钟线的释放  
     I2C_Delay();  
     I2C_Delay();  
     I2C_Delay();  
      
    }  
    

    多字节读和写

    /* 
    函数:I2C_Puts() 
    功能:主机通过I2C总线向从机发送多个字节的数据 
    参数: 
     SlaveAddr:从机地址(高7位是从机地址,最低位是写标志0) 
     SubAddr:从机的子地址 
     Size:数据的字节数 
     *dat:要发送的数据 
    返回: 
     0:发送成功 
     1:在发送过程中出现异常 
    */  
    bit I2C_Puts(unsigned char SlaveAddr, unsigned char SubAddr, unsigned char Size, char *dat)  
    {  
    //检查长度  
     if ( Size == 0 ) return 0;  
    //确保从机地址最低位是0  
     SlaveAddr &= 0xFE;  
    //启动I2C总线  
     I2C_Start();  
    //发送从机地址  
     I2C_Write(SlaveAddr);  
     if ( I2C_GetAck() )  
     {  
      I2C_Stop();  
      return 1;  
     }  
    //发送子地址  
     I2C_Write(SubAddr);  
     if ( I2C_GetAck() )  
     {  
      I2C_Stop();  
      return 1;  
     }  
    //发送数据  
     do  
     {  
      I2C_Write(*dat++);  
      if ( I2C_GetAck() )  
      {  
       I2C_Stop();  
       return 1;  
      }  
     } while ( --Size != 0 );  
    //发送完毕,停止I2C总线,并返回结果  
     I2C_Stop();  
     return 0;  
    }  
      
      
    /* 
    函数:I2C_Put() 
    功能:主机通过I2C总线向从机发送1个字节的数据 
    参数: 
     SlaveAddr:从机地址(高7位是从机地址,最低位是写标志0) 
     SubAddr:从机的子地址 
     dat:要发送的数据 
    返回: 
     0:发送成功 
     1:在发送过程中出现异常 
    */  
    bit I2C_Put(unsigned char SlaveAddr, unsigned char SubAddr, char dat)  
    {  
     return I2C_Puts(SlaveAddr,SubAddr,1,&dat);  
    }  
      
      
    /* 
    函数:I2C_Gets() 
    功能:主机通过I2C总线从从机接收多个字节的数据 
    参数: 
     SlaveAddr:从机地址(高7位是从机地址,最低位是读标志1) 
     SubAddr:从机的子地址 
     Size:数据的字节数 
     *dat:保存接收到的数据 
    返回: 
     0:接收成功 
     1:在接收过程中出现异常 
    */  
    bit I2C_Gets(unsigned char SlaveAddr, unsigned char SubAddr, unsigned char Size, unsigned char *dat)  
    {  
      
    //检查长度  
     if ( Size == 0 ) return 0;  
    //确保从机地址最低位是0  
     SlaveAddr &= 0xFE; //确保最低位是0  
    //启动I2C总线  
     I2C_Start();  
    //发送从机地址  
     I2C_Write(SlaveAddr);  
     if ( I2C_GetAck() )  
     {  
      I2C_Stop();  
      return 1;  
     }  
    //发送子地址  
     I2C_Write(SubAddr);  
     if ( I2C_GetAck() )  
     {  
      I2C_Stop();  
      return 1;  
     }  
    //发送重复起始条件  
     I2C_Start();  
    //发送从机地址  
     SlaveAddr |= 0x01;  
     I2C_Write(SlaveAddr);  
     if ( I2C_GetAck() )  
     {  
      I2C_Stop();  
      return 1;  
     }  
    //接收数据  
     for (;;)  
     {  
      *dat++ = I2C_Read();  
      if ( --Size == 0 )  
      {  
       I2C_PutAck(1);  
       break;  
      }  
      I2C_PutAck(0);  
     }  
      
    //接收完毕,停止I2C总线,并返回结果  
     I2C_Stop();  
     return 0;  
    }  
      
      
    /* 
    函数:I2C_Get() 
    功能:主机通过I2C总线从从机接收1个字节的数据 
    参数: 
     SlaveAddr:从机地址(高7位是从机地址,最低位是读标志1) 
     SubAddr:从机的子地址 
     *dat:保存接收到的数据 
    返回: 
     0:接收成功 
     1:在接收过程中出现异常 
    */  
    bit I2C_Get(unsigned char SlaveAddr, unsigned char SubAddr, unsigned char *dat)  
    {  
     return I2C_Gets(SlaveAddr,SubAddr,1,dat);  
    }  
    
  • 相关阅读:
    D. Constructing the Array
    B. Navigation System
    B. Dreamoon Likes Sequences
    A. Linova and Kingdom
    G. Special Permutation
    B. Xenia and Colorful Gems
    Firetrucks Are Red
    java getInstance()的使用
    java 静态代理和动态代理
    java 类加载机制和反射机制
  • 原文地址:https://www.cnblogs.com/jontian/p/5717324.html
Copyright © 2011-2022 走看看