1、I2C协议详解及裸机程序分析 - 简书(通俗易懂)
根据上图,我们首先设置IICCON(来设置时钟),时钟源是PCLK(是50MHZ)太快了我们需要设置这个分频系数,把时钟降低,降低到我们想要的SCL,然后我们要发出start信号,我们需要设置寄存器发出start信号,之后我们需要发出数据啊,我们的程序可以把数据写入到IICDS寄存器,一写入就会自动的发出时钟,并且把这8位数据从SDA发送给从设备,数据发送之后,在第九个时钟会收到回应信号,可以查询IICSTAT是否有ACK(有ACK表示数据发送成功了),可以继续发送数据,等发完数据之后,再来设置IICSTAT让它发出P信号。
关于i2c的响应问题:对于每一个接收设备(从设备,slaver),当它被寻址后,都要求在接收到每一个字节后产生一个响应。因此,the master device 必须产生一个额外的时钟脉冲(第九个脉冲)用以和这个响应位相关联。
在这个脉冲期间,发出响应的从设备必须将SDA拉低并在时钟脉冲的高电平期间保持住。这表示该设备给出了一个ACK。如果它不拉低SDA线,就表示不响应(NACK)。
另外,在从机(发送方)发送完最后一个字节后主设备(接收方)必须产生一个不响应位,用以通知从机(发送方)不要再发送信息了,这样从机就知道该将SDA释放了,而后,主机发出一个停止位给slaver。
总结下,标准的IIC协议中,SDA 和 SCL 都是有主机控制的,从设备只是能够将SDA线拉低而已。对于SCL线,从机是没有任何能力去控制的。从机只能被动跟随SCL
再说的清楚些:
主机发送数据到从机的状态下:主机控制SCL信号线和SDA信号线,从机只是在SCL线为高的时候去被动读取SDA线。
主机读取从机的数据:主机来发出时钟信号,从机只是保证在时钟信号为高电平的时候的SDA的状态而已。
冲突争论点:从机到底能不能拉低SCL?
标准的IIC协议中说明,IIC通讯中,SDA 和 SCL 都是有主机控制的,从设备只是能够将SDA线拉低而已。对于SCL线,从机是没有任何能力去控制的。从机只能被动跟随SCL。从这段话得出,从机无权控制SCL线。
但是!!!在实际应用中,我遇到了从机将SCL拉低的情况。一笔数据(8bit+ACK)完成之后,从机开始处理数据(从机忙碌),无法接受下一笔数据,此时从机将SCL拉低,主机需等待SCL变为高电平时才可以继续进行数据传输。
比如我主机没有给足够的处理数据时间(此时间要看软件也即程序需跑多久才处理完成),主机想强行拉高SCL发送下一帧数据,这时候抓取波形来看,就像是SCL想拉高但是拉不起来,被拽住了一样。而后果就是下一帧的数据错乱,严重的甚至导致IIC通信死掉。
从机拉低SCL的情况是存在的:当从机正忙、无法保质保量完成接下来的通讯时,可以主动将SCL拉低,是以谓之 Clock Stretching。这是整个I²C总线中从机唯一有权限拉低SCL的特例。
5.1 SCL信号的同步
如果被控器希望主控器降低传送速度可以通过将SCL主动拉低延长其低电平时间的方法来通知主控器,当主控器在准备下一次传送发现SCL的电平被拉低时就进行等待,直至被控器完成操作并释放SCL线的控制控制权。这样以来,主控器实际上受到被控器的时钟同步控制。可见SCL线上的低电平是由时钟低电平最长的器件决定;高电平的时间由高电平时间最短的器件决定。这就是时钟同步,它解决了I2C总线的速度同步
根据上面的描述,“时钟同步”与“总线仲裁”可以总结如下规律:
①主控器通过检测SCL上的电平来调节与从器件的速度同步问题——时钟同步;
②主控器通过检测SDA上自身发送的电平来判断是否发生总线“冲突”——总线仲裁。
因此,I2C总线的“时钟同步”与“总线仲裁”是靠器件自身接口的特殊结构得以实现的。
当Master速度过快Slave端来不及处理时,Slave设备可以拉低SCL不放(SCL=0将发生“线与”)以阻止Master发送更多的数据。此时Master将视情况减慢或结束数据传送。
不同观点1:在第九个时钟之后
主机发送数据时,被发送方即主机拉低;主机接收数据时,被接收方即从机拉低
不同观点2:
在第9个时钟之后,如果有某一方处于繁忙状态,它可以一直把SCL拉低当SCL为低电平时候,大家都不应该使用IIC总线,只有当SCL从低电平变为高电平的时候,IIC总线才能被使用。
不同观点3:
在第九个CLK,就会产生一个中断,在中断处理过程中SCL被拉为低电平,谁都不能再使用IIC总线,等待中断处理完成.
重点: 发生中断时,我们的IIC控制器=IICCON寄存器会把SCL拉低,阻止任何设备再使用IIC总线,清中断之后才能继续使用,这种机制就给我们中断服务程序的执行提供了时间。
IICCON寄存器(Multi-masterIIC-buscontrol)
IICCON寄存器用于控制是否发出ACK信号、设置发送器的时钟、开启,i2c中断,并标识中断是否发生。它的各位含义如表:
//----------------------------------------
补充@201108311142
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也可以控制。
Yasin Lee@201109091400
昨天在调试mma8452q的过程中发现一个新的问题:
我一直以为用i2c读取slaver的某个寄存器的过程是:1.向slaver写一个寄存器地址过去。2.读取slaver。就是这样两步,可是这样的想法在读取mma8452q时却出现了问题,总是无法读到正确的数据。上述两步我是通过这样的方法实现的:1.调用i2c_master_send发送一个字节的数据(寄存器地址)过去。2.调用i2c_master_recv读取一个字节。完毕。之所以这么做是基于这样的想法:当向slaver写入(发送)一个寄存器地址过去后,slaver就会把当前的读写指针(假想的)指向这个寄存器,此后,读取的时候自然是读到这个寄存器的值了。
可是这样的想法不行,考虑到,这个假设可能不对,我便直接使用i2c_transfer来进行操作,因为这个函数可以实现在由写入状态切换到读取状体的过程中不发送stop,也就是直接再次发送一个start,即Restart。
问题解决,看来对mma8452q的读取操作必须经由restart来做中间的切换。而不能在切换过程中发送stop命令。而先前采用的分部操作在每一步完成后都有一个i2c stop命令发生,所以出了问题。
I2C总线时序以及ACK和NACK(NAK)_Leo丶Fun的博客-CSDN博客_i2c nak
2、IIC 总线接口-详细介绍
在第9个时钟之后,如果有某一方处于繁忙状态,它可以一直把SCL拉低当SCL为低电平时候,大家都不应该使用IIC总线,只有当SCL从低电平变为高电平的时候,IIC总线才能被使用。