html网页制作自我介绍seo需要培训才能找到工作吗
看了些书籍和I2C协议,感觉讲的太细,没有一个提纲挈领的介绍,关于I2C的流程,大都是文字介绍,画流程图的一般是学FPGA的人,没有详细介绍。因此,总结下。
I2C协议规范
规范可以上网搜,里面有比较完整的介绍,这里主要介绍一些核心概念。如总线是什么?
总线
总线=格式+流程
即总线规定了传输的格式和传输的流程,因此,我们学I2C协议,主要是学这两样。很多介绍都不去作区分,所以学起来,感觉知识很多很杂。
流程
我们先不管具体的驱动实现代码,学I2C,第一步就是学写和读操作的流程,流程图如下面的状态图,
图1
上图主要概念是
初始化idle、读read、写write、应答ack、开始start、停止pause;
写控制字(地址和主从设置,占一个字节),地址和控制字R/W在一个字节里面,代码上分两步,实际传输时,是同一个字节。这是我个人理解,不一定对,暂不考究,不影响整体思路。
格式
有了流程,还需要传输格式,保证端口发送消息时候不会冲突,注意,I2C只用两个接口传输信息,SCL和SDA,且SCL是时钟信号。
格式如下图2(发了三个字节数据,1个是地址,2个数据),第一个蓝色波形,前7位是地址,第8位是读写标识位,0表示写。第九位是ACK信号,是从机应答。可见,总线上传输的数据,有9位,其中7位是地址位,一个是方向位(读或者写),一个是应答位ACK。而起始位和停止位,可以看做是标识信号,不当作可传输数据来看。
SCL有什么作用?
因为,对于整合了I2C控制器的不同器件来说,它们的工作时钟是不统一的,因此,需要时钟同步,如下图黄色的波形。
图2
ACK
我觉得ACK是个挺难理解的点,所以单独说明。ACK其实就是从机告诉主机:“我接收成功了,你可以发下一个数据”或者“我正在忙,处理不过来,你暂时不要发了”。
首先,在C语言要如何实现一个位的设置?有两种办法:
第一种
Keil C 51单片机,使用sbit定义一个位。如:
sbit SDA = P1^7; //I2C 总线驱动使用的数据线
sbit SCL = P1^6; //I2C 总线驱动使用的时钟线
其中P1是P1端口的寄存器地址。参考http://blog.csdn.net/xzongyuan/article/details/26452609
第二种
ARM linux 下,使用一个字节表示某个端口对应的寄存器的数据,如PartB的8个端口对应的寄存器都为低电平时,对应的寄存器GPIO_PortB值为0x00H。具体的ACK标识,可用一个无符号整形unsigned int来表示0或者1,通过判断GPIO_PortB的值来设置。示例代码如下:
#define uint unsigned int //映射uint 为无符号整数 uint ACK; //应答位
//程序名称:GET_I2C_ACK() [检查应答位子程序]
//程序功能:获取一个总线响应[应答]
//入口参数:无
//出口参数:ACK(低电平为有效应答,人为地使ACK=1返回一个高电平用于判断)
//编程与整理:刘同法
//编写日期:2007年6月30日
//说明:返回值:ACK=1时表示有应答
//------------------------------------------------
uint GET_I2C_ACK()
{GPIODirModeSet(GPIO_PORTB_BASE, SDA, GPIO_DIR_MODE_IN);//SDA=1; //应答的时钟脉冲期间,发送器释放SDA线(高) GPIOPinWrite( GPIO_PORTB_BASE, SDA, SDA );NOPNOP//SCL=1; //保持时钟线为高电平GPIOPinWrite( GPIO_PORTB_BASE, SCL, SCL );ACK=0; //初始化应答信号用于后判断NOPNOP// C=SDA; //应答的时钟脉冲期间,接收器会将SDA线拉低[从机在应答时拉低此线]C=GPIOPinRead( GPIO_PORTB_BASE ,SDA);if(C==0x00) // 判断应答位,SDA为高,则ACK=0,表示无应答ACK=1; //SDA为低,则使ACK=1,表示有应答NOP//SCL=0; //钳住总线GPIOPinWrite( GPIO_PORTB_BASE, SCL, ~SCL );NOPGPIODirModeSet(GPIO_PORTB_BASE, SDA, GPIO_DIR_MODE_OUT); return ACK; }
以上两种办法,估计都可以参考,还没实践过,所以不知道有没限制。如果看不懂上面函数的赋值方法,可以参考:http://blog.csdn.net/xzongyuan/article/details/26383713
该协议的办法是,主机端把SDA线电平拉高,如果从机把SDA线电平拉低,整条SDA线都会被拉低,这时主机SDA端口能侦测到电平为0,这样的情况表示从机应答了。上面的代码用C等于0,表示应答了,然后把标志位ACK置为1。代码看起来好像挺复杂,其实原理很简单。
最后总结下:主机拉高SDA电平,等从机答复。如果从机准备好了,就拉低SDA,就表示应答了。如果从机很忙,接收不了,就保持SDA不变。这样,等下一个时钟到来,主机根据SDA电平高低,就知道从机怎么想的了。
地址
地址也是个很重要的概念,你说要寻址,那地址怎么定义?如下图,芯片管脚A0-A2,就可以设置了。该芯片地址为7位,但是高4位固定为1010,而低三位根据A0-A2端口电平高低决定,剩下的1bit是给R/W用的,用于设定数据流的方向,决定读或者写操作。另外,I2C最初只有7位地址,现在的标准有10bit的了。地址线中,固定的高4位,用于区分不同的IC,而低3位是用于区别同一类型的芯片,所以,同一个类型的芯片,最高可以寻址8个。
起始位/停止位
这个协议的设计思想是:SCL是时钟信号,不停变化,有高电平和低电平两种状态在切换。那么就利用这两种情况来区分数据和起始信号:
SCL高电平时——数据有效
高电平时,要求SDA不变,以保证数据有效性。所以,在实际传输数据时,读取SDA数据的时候,都是在SCL为高电平时,这就叫做有效数据(符合数据有效性)。那么起始和停止的标识,其实就是“无效”数据位。因为SCL为高电平时,SDA理应保证电平不变,而起始和结束信号都违反了这点。如下图
SCL低电平时——数据允许变化
低电平时,允许SDA变化,所以,如果要传命令和数据,就要让SDA在SCL为低电平时“变化”数据。如上面的图2,电平变化都是在SCL(黄线)为低电平时发生的,而有效数据的获取,都是在SCL(黄线)是高电平时获取。
有兴趣的同学可以自己画个状态图,那就一目了然了,很简单,我就不画了。
总结
我觉得这是最核心的知识了,把I2C的格式和流程原理搞明白后,电平的千变万化就不那么可怕了。
示波器图参考:http://blog.csdn.net/k_linux_man/article/details/7224810
参考:http://blog.csdn.net/xzongyuan/article/details/26452609