zoukankan      html  css  js  c++  java
  • 51单片机I2C总线

    I2C总线是飞利浦公司推出的一种串行总线,所有器件共用两根信号线,实现数据的传输。

    总线接口接了上拉电阻,默认为高电平,所以就可以用“当低电平出现”来标记出一种起始信号。我个人把它想象成:许多人在一条走廊上的不同房间(器件)里,大家都把门打开,连出两根长长的听筒(小时候玩的那种),每个人都从两根大主线上各接一根到自己房间里。两根听筒平时都是安静的(1)。如果有某房间的人叫了一声(0),那剩下的人就知道,我们准备开始通话了。

    为了保证秩序,大家选出一个人当领队,由他来主导通话的过程。这就是总线的主机,其他人就是从机。从机有多个,主机只有一个。

    两根信号线,一根叫数据线SDA,一根叫时钟线SCL。顾名思义,数据线用来传输数据,时钟线用来管理顺序。

    怎么样表示开始,怎么样表示结束,用下面的图表示。注意有严格的时间规定。

    首先,我们知道怎么样算是通话的开始和结束(起始信号和终止信号)。然后,规定怎样算是回答“可以”,怎么算是回答“不可以”(应答和非应答)。

    接着,我们要知道谁向谁喊话,所以要给每个房间的人都赋予一个名字,也就是地址。再用一个0或1表示方向,从谁到谁。

    因此,数据传输的过程,大体就是如此:领队喊出“开始”,说出一个房间名,同时,所有房间的人确认是不是自己的。领队表明目的,说出是向他传数据,还是从他那读数据;然后,确认是自己房间的队员给出应答,可以就开始传输数据。完成后,主机/从机给出应答,表明收到了没有。

    下面就是SDA上传送的数据格式。

    (a)主机向从机发送数据

    S表示起始信号。阴影表示主机发送。A表示应答,上加划线表示非应答。P表示停止。

    (b)主机发送数据后,从从机读数据

    (c)传输过程中,想改变方向

    方法是,重复一次起始信号和从机地址,加一个方向位来改变方向。

    SCL是用来管秩序的。只要SCL保持高电平状态,SDA正在传的数据就不能乱动,只有把它拉低以后,SDA才能变化。这样确保数据传输不会乱套,所以在实际的传输过程中,SCL会不断地翻转。

    另外,如果存在许多一样的器件,怎么区分呢?方法是把前几位固定不能动,表示是同一种器件,后面的几位可以动(可编程)。假如后面空出3位,那么就是可编程8个,也就是允许有8个同种器件接到总线上。前面的地址叫“器件地址”,后面的地址叫“首地址”。所以,每次主从通信时,要先传器件地址,加方向位,等器件应答;再传首地址来寻找特定的器件,再加方向位,等待它应答。接着开始数据传输。

    写入过程:

    读出过程:

    下面是对于I2C总线模拟的一些关键函数的注释。

    //延时10微秒函数
    void Delay10us(void)
    {
    	unsigned char a,b;
    	for (b=1;b>0;b--)
    		for (a=2;a>0;a--)
    			;
    }
    
    //I2C起始信号模拟
    void I2cStart()
    {
    	SDA = 1;
    	Delay10us();
    	SCL =1;
    	Delay10us();
    	SDA = 0;
    	Delay10us();
    	SCL =0;
    	Delay10us();
    
    }
    
    //I2C停止信号模拟
    void I2cStop()
    {
    	SDA = 0;
    	Delay10us();
    	SCL =1;
    	Delay10us();
    	SDA = 1;
    	Delay10us();
    						   
    }
    
    //I2C发送数据函数
    unsigned char I2cSendByte(unsigned char dat)
    {
    	unsigned char a = 0,b;
    	for(a=0;a<8;a++)	   //一位一位传输数据
    	{
    		SDA = dat>>7;	   //右移7位,最高位送给SDA
    		dat = dat<<1;	   //左移一位,次高位变成最高位
    		Delay10us();
    		SCL = 1;
    		Delay10us();
    		SCL = 0;	//翻转SCL,SCL为低电平时传输的数据才能改变
    		Delay10us();
    	}
    	SDA = 1;
    	Delay10us();
    	SCL = 1;	   //释放数据线和时钟线
    	while(SDA)	   //等待从机应答,如应答则SDA拉低跳出循环
    	{
    	 	b++;	   //一段时间没有应答就认定为失败
    		if(b>200)
    			{
    				SCL = 0;
    				Delay10us();
    				return 0;	//数据发送失败
    			}
    	}
    	SCL = 0;
    	Delay10us();
    	return 1;	 //数据发送成功
    
    }
    
    //I2C读取数据函数
    unsigned char I2cReadByte()
    {
     	unsigned char a = 0;
    	SDA = 1;	//拉高数据线,保持空闲等待数据
    	for(a=0;a<8;a++)	//一位一位读取数据
    	{
    		SCL = 1;	 //拉高时钟线,保持数据稳定,准备接收
    		Delay10us();
    		dat<<=1;		//左移一位,空出一位准备读数据
    		dat |= SDA;		//或运算,dat空出的位为0,如SDA也为0则为0,SDA为1就为1,相当于保存SDA数据
    		Delay10us();
    		SCL = 0;		//翻转时钟线,使下位数据能够改变
    		Delay10us();	
    	}
    	return dat;	   	//返回读取的数据
    }
    
    //向At24C02芯片写数据函数
    void At24c02Write(unsigned char addr,unsigned char dat)
    {
     	I2cStart();		//起始信号
    	I2cSendByte(0xa0);	  	//发送器件地址(固定)
    	I2cSendByte(addr);		//发送首地址(自定)
    	I2cSendByte(dat);		//发送数据
    	I2cStop();		//停止信号
    
    }
    
    //读数据函数
    unsigned char At24c02Read(unsigned char addr)
    {
    	unsigned char num;
    	I2cStart();		//起始信号
    	I2cSendByte(0xa0);	  	//发送器件地址(固定)
    	I2cSendByte(addr);		//发送首地址(自定)
    
    	I2cStart();				//加一个起始信号,用于改变数据传送方向
    	I2cSendByte(0xa1);		//读取器件地址,最后一位表示方向
    	num = I2cReadByte();	//保存读取的数据
    	I2cStop();		//停止信号
    
    	return num;
    }
    

      

  • 相关阅读:
    操作系统原理
    Linux三剑客正则表达式
    Linux通配符知识深度实践详解
    Linux文件属性之时间戳及文件名知识详解
    Linux系统文件权限
    date:显示与设置系统时间
    正则表达式--三剑客简单应用
    Linux习题小结
    Linux系统文件属性知识
    Linux系统目录结构知识
  • 原文地址:https://www.cnblogs.com/banmei-brandy/p/11259181.html
Copyright © 2011-2022 走看看