i2c 总线

一:i2c 内部电路图

    

通过该图可看出:

    1:当DEVICE 没有工作的时候,通过上拉电阻,SDA与SCL都为高电平;

    2:当DEVICE1或者2 有一个DATAN1或者2为低时,整个SDA为低电平;

    3:当DEVICE1或者2 有一个SCLKN1或者2为低时,整个SCL为低电平  ;

二:传输

    1:开始信号与结束信号

        

2:数据传输

三:时钟同步

        如果被控器希望主控器降低传送速度可以通过将SCL主动拉低延长其低电平时间的方法来通知主控器,当主控器在准备下一次传送发现SCL的电平被拉低时就进行等待,直至被控器完成操作并释放SCL线的控制控制权。这样以来,主控器实际上受到被控器的时钟同步控制。可见SCL线上的低电平是由时钟低电平最长的器件决定;高电平的时间由高电平时间最短的器件决定。这就是时钟同步,它解决了I2C总线的速度同步

四:关于设备地址7位或者10位传输

  • 主发送器10位地址的从接收器发送数据。如图14所示,在整个过程中,数据传送方向没有改变。在START发起之后,所有从机对比总线上第一个字节的前七位(1111 0XX)是否和自身地址一致,并检测第八位是否为0(写)。可能有一个以上设备会检测到地址匹配(因为只对比了10位地址的最高2位),它们都会产生响应A1。接下来,所有上面响应的从机对比总线上第二个字节和它们各自地址的后八位(XXXX XXXX)是否一致。只有一个设备的地址匹配,并产生响应A2。被寻址的从机一直受主机控制,直到STOP或Sr指向另外的地址;、

图14. 主发送器寻址从接收器(10位地址空间)
  • 主接收器读取10位地址的从发送器发送的数据。数据传送方向在第二个R/W后发生改变。如图15所示,A2之前的操作和主发送器向从接收器写数据没有区别。通过Sr,匹配地址的从机得知它就是被寻址的设备,接下来从机检测Sr后的七个比特是否和先前START后的七比特一致(1111 0XX),并检测第八位(R/W)是否为1。如果是,则从机据此判断它被寻址并将要作为发送器往总线上发送数据,此时从机产生响应A3。从机一直占用总线,直到接收到STOP或Sr指向另一个从机地址。收到Sr信号后,所有从机都会对比Sr后第一个字节的前七位(1111 0XX),并检测该字节的第八位(R/W)。对于10位地址设备R/W=1,对于7位地址设备,地址(1111 0XX)不匹配,因此这些设备都不会响应Sr,不会被寻址。
  • 主要实现代码:
 if (flags & I2C_M_TEN) {
		/* a ten bit address */
		addr = 0xf0 | ((msg->addr >> 7) & 0x06); //传输 11110XX0 写
		bit_dbg(2, &i2c_adap->dev, "addr0: %d\n", addr);
		/* try extended address code...*/
		ret = try_address(i2c_adap, addr, retries); //传输
		if ((ret != 1) && !nak_ok)  {
			dev_err(&i2c_adap->dev,
				"died at extended address code\n");
			return -EREMOTEIO;
		}
		/* the remaining 8 bit address */
		ret = i2c_outb(i2c_adap, msg->addr & 0xff);//传输地址的后十位
		if ((ret != 1) && !nak_ok) {
			/* the chip did not ack / xmission error occurred */
			dev_err(&i2c_adap->dev, "died at 2nd address code\n");
			return -EREMOTEIO;
		}
		if (flags & I2C_M_RD) {//如果是读信号
			bit_dbg(3, &i2c_adap->dev, "emitting repeated "
				"start condition\n");
			i2c_repstart(adap);
			/* okay, now switch into reading mode */
			addr |= 0x01;//修改地址最后一位,读信号
			ret = try_address(i2c_adap, addr, retries);//再发送一次
			if ((ret != 1) && !nak_ok) {
				dev_err(&i2c_adap->dev,
					"died at repeated address code\n");
				return -EREMOTEIO;
			}
		}
	} else {		/* normal 7bit address	*/
		addr = msg->addr << 1;
		if (flags & I2C_M_RD)
			addr |= 1;
		if (flags & I2C_M_REV_DIR_ADDR)
			addr ^= 1;
		ret = try_address(i2c_adap, addr, retries);//七位地址按照协议传输就行
		if ((ret != 1) && !nak_ok)
			return -ENXIO;
	}
五:总线 仲裁
 当有多个主控制器传输数据时,需要比对多个bit位,那个在scl为高电平时,与sdA一致,那就谁占领;

六:i2c挂死问题

   1:SDA挂死: 一般由于传输第9个信号,从设备需要ack时候,ack为低电平(SDA为低电平),主设备复位,导致(SCL)为高电平,由于自SCL时,SDA不能有电平变化,所以该从设备一直让SDA处于低电平状态,无法通信,挂死,

    解决办法:1:复位从设备,但是一般从设备没有复位信号;

                     2:使用GPIO模仿,SCL信号,拉低,一般需要9个时钟信号;

                     3:查找导致原因,可能由于自己写的i2c驱动,延时问题,主从设备延时不同步,主设备延时少,导致从设备没有反应过来。

    2:SCL挂死,一般很少出现,出现建议直接复位了

7,实现GPIO模仿i2c   

   假设:  GPIO1 模仿 SDA 
          GPIO2 模仿 SCL
        
        初始化:
            GPIO1 = 1;
            GPIO2 = 1;
        
        i2c启动信号
             GPIO1 = 0;
             delay = 根据频率算;
             GPIO2 = 0;
        
        写数据:
            加入一个字节  01001100  = data
            for(i = 7;i >=0;i--)
            {
                GPIO1 = (data>>i)&0x01;
                delay
                GPIO2 = 1;
                GPIO2 = 0;
            }
            GPIO1= 1;
            GPIO2 = 1
            GPIO1 = 0;;
         读数据
               for(i = 7;i>=0;i--)
              {
                GPIO2 = 1
                read GPIO 1
                GPIO2 = 0
              }
            GPIO1= 1;
            GPIO2 = 1
            GPIO1 = 0;;


猜你喜欢

转载自blog.csdn.net/flyxiao28/article/details/80457371