IIC协议

原文章地址:http://blog.chinaunix.net/uid-16100003-id-3059814.html

- 原文内容

SDA和SCL已经通过上拉电阻被上拉,master可以控制(拉低或者释放)这两条线,而slaver只能控制SDA线。当master发送数据时,master会适时地将SDA和SCL拉低或释放(拉高)。确切的时序应该是这样的:

当mater要发送一个start时,mater会将SDA拉低,这就可以了,因为此时的SCL一定是High。好了,一个start就这样发出去了。而slaver也会发现这个start信号的发生,slaver便会准备好接收接下来的数据了。紧接着,master要发送一个Byte的数据了,一位一位的发出这8个bits。这时master会先将SCL拉低,然后在SCL为低的状态下将一个bit准备好放到SDA上(比如要发送一个 0,master就会通过拉低SDA来放好这个0),然后master会把SCL拉高(释放),此时slaver会立刻检测到SCL的变化,由此聪明的slaver便知道master已经将要发送的那个bit准备好了,slaver便会在这个SCL的高电平期间尽快(maser不会等你很久的哦)去读取一下SDA,嗯读到了一个0,slaver就把这个0放到自己的移位寄存器中待后续处理。master会在一个设定好的时间后把SCL再次拉低,然后在SCL为低电平期间把下一个bit放到SDA上,然后再把SCL拉高,然后slaver在SCL的高电平期间再去读SDA。。。。。如此反复8次,一个Byte的传输便告结束。当这8个bit发完后,SCL是处于低电平的(被master拉低的),SDA是出于高电平的(master已经释放了SDA)。

***当一个字节发送完毕后,master会释放SDA(拉高)并拉低SCL,此时slaver如果打算发出一个ACK的话,它必须在这个SCL被master拉低的短暂时间内去主动将SDA拉低并保持住 (此前我们说过,SDA此时已经被master释放,所以slaver才有机会去拉低这个SDA)***。master会在一个确定的时间后再次将SCL拉高,并在拉高的期间去读取SDA线的状态,如果读到低电平,则认为收到了来自slaver的响应(ACK),否则认为slaver没有响应(NACK)刚才发送的那一个Byte。这个过程就是我们说的i2c通讯中的第9个时钟周期。当master读完这个ACK / NACK 后,会再次将SCL拉低,用以通知slaver:第9个时钟周期已经结束,你现在可以释放SDA了。而此时master也可以向SDA上准备下一个Byte的第一个bit。继而重复上述过程。。。。。或者,master也许想在接下来发送一个stop过去,***那么master会在这个SCL为低的时间内将SDA拉低,而后再将SCL拉高,在SCL为高的期间再将SDA释放 (拉高) 。这样,一个STOP位就产生了。你会发现此后的***SDA和SCL都是高,这就是是所谓的总线空闲了

一句话:SCL是单向的,由master控制。而SDA是双向的,master可以控制,slaver也可以控制。

- 总结

使用STC89C52单片机与AT24C02进行模拟IIC协议通信

共SCL和SDA两条线,通过这两条线上电平高低的组合实现

  • 开始信号;
  • 字节发送信号;包括八位数据发送信号,从机应答接受信号;
  • 字节接受信号;只需对SCL进行控制,然后接受SDA上的信号,这里需要在SCL=1的时候读取SDA信号(因为此时SDA信号不变),应答信号直接由主机发出。
  • 结束信号;
  • 函数程序
#include "reg52.h"
#include "I2C.h"

void Delay10us(void)   //误差 0us
{
    unsigned char a,b;
    for(b=1;b>0;b--)
        for(a=2;a>0;a--);
}

void I2cStart()
{
    SCL=0;
	Delay10us();
	SDA=1;
	Delay10us();
	SCL=1;
	Delay10us();
	SDA=0;
	Delay10us();
}

void I2cStop()					
{
    SCL=0;
	Delay10us();
	SDA=0;
	Delay10us();
	SCL=1;
	Delay10us();
	SDA=1;
	Delay10us();
}

unsigned char I2cSendByte(unsigned char dat)	
{
    unsigned char a,b=0;
	for(a=0;a<8;a++)	 //发送8位数据位
	{
	    SCL=0;
		Delay10us();
		SDA=dat>>7;
		Delay10us();
		dat=dat<<1;
		SCL=1;
		Delay10us();    
	}
	SCL=0;			  //为释放SDA准备
	Delay10us();
	SDA=1;			  //释放SDA,把SDA控制权交给从机
	Delay10us();
    while(SDA)
	{
	    b++;
		if(b>200)
		{
		    SCL=1;	 //SCL置高时,SDA=1;表示无应答;  
			Delay10us();
			return 0;        
		}        
	}
	SCL=1;		   	//SCL置高时,SDA=0;表示有应答
	Delay10us();
	SCL=0;		    //为SDA变化做准备
	Delay10us();
	SDA=1;		    //SDA控制权交给主机
	Delay10us();
	return 1;
}

unsigned char I2cReadByte()			  	
{
    unsigned char a,dat=0;
	SCL=0;
	Delay10us();
	for(a=0;a<8;a++)
	{
	    dat=dat<<1;			//接收端左移一位
		SCL=1;				//SCL置高
		Delay10us();
		dat=dat|SDA;    		//dat从MSB到LSB开始接受数据
		SCL=0;
		Delay10us();
	}
    SDA=1;				   //交换控制权
	Delay10us();
	SDA=0;
	Delay10us();
	SCL=1;
	Delay10us();
	SCL=0;
	Delay10us();
	return dat;
}

void At24c02WriteByte(unsigned char addr,unsigned char dat)			
{									   	
    I2cStart();
	I2cSendByte(0xa0);
	I2cSendByte(addr);
	I2cSendByte(dat);
	I2cStop();            
}

unsigned char At24c02ReadByte(unsigned char addr)    
{								  	 
    unsigned char rec;
	I2cStart();
	I2cSendByte(0xa0);
	I2cSendByte(addr);
	I2cStart();
	I2cSendByte(0xa1);
	rec=I2cReadByte();
	return rec;	    							
}

- 注意事项

应答前需:在SCL=0的情况下,将SDA置1,代表着交换SDA控制权;
对于谨慎对待SCL=1的执行,SCL=0—>1的过程伴随着对SDA上数据的读取。


  • 发送数据函数流程:

开始信号——>发送总线地址(4位固定+3位可调+0)——>应答——>发送存储器地址(1-255)——>应答——>发送数据——>应答——>结束信号
读取数据函数流程:
开始信号——>发送总线地址(4位固定+3位可调+0)——>应答——>发送存储器地址(1-255)——>应答——>发送总线地址(4位固定+3位可调+1)——>应答——>发送数据——>应答——>结束信号

你可能感兴趣的:(单片机)