• 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);  
    }  
    
  • 相关阅读:
    Thread.join
    Thread.yield
    线程的生命周期
    HashMap底层原理
    Web Services
    Struts2框架
    hibernate乐观锁实现原理
    Hibernate框架
    oracle exp 无法导出空表
    linux 远程复制文件或文件夹
  • 原文地址:https://www.cnblogs.com/jontian/p/5717324.html
Copyright © 2020-2023  润新知