言归正传,对于I2C的使用,通常来说有两种实现方法,一种是利用单片机自带的硬件实现I2C,另外一个就是根据I2C协议和GPIO的输出,来实现软件I2C。虽然我没有实操过I2C,但是我的很多朋友(巨佬)都和我说硬件I2C巨坑,会有很多玄学问题,相比之下软件I2C会靠谱许多,于是就决定把软件I2C给办了。至于硬件I2C嘛.。。。嘛。。。有空再说!
其实想要用好I2C很简单,只需要上CSDN上扒一些代码移植移植就完事了。但是要是想真正用好I2C,个人认为理论基础也必须要过关,所以上代码之前,我们先来过一遍I2C的原理。
硬件的电气连接:
I2C通信中,主机SDA端与从机SDA端相连,主机SCL端与从机SCL端相连,且必须共地,这样就完成了我们的电气连接。在I2C总线空闲时,SDA和SCL应该处于高电平,我们上百度一搜索就能发现参考电路有一个上拉电阻。但是不是意味着我们也要接一个上拉电阻呢,个人认为未必。因为接上拉电阻的前提是单片机的GPIO输出是开漏输出,但是对于MSP432来说,似乎根本没有在STM32中开漏输出和推挽输出的概念(至少我查文献是查不到的),因此我们连这个输出是不是开漏输出都搞不清楚,直接接上一个上拉电阻恐怕并非良策。针对该问题的解决方案是,输出低电平的时候使用单片机的输出模式输出低电平,输出高电平的时候使用单片机的输入上拉模式让总线处于高电平。有的人可能会问你这不是脱裤子放屁嘛,明明我可以只靠输出模式来拉高电平,搞那么复杂干什么?似乎很对,但是我们想一下I2C通信中,从机应答的时候是要主机交出SDA线的控制权的,那么这个时候主机在这根线输出高电平,从机在这根线输出低电平会导致什么后果?(我也不知道什么后果)亦或者说总线上有两个主机,那在一个主机空闲的时候用输出把电平拉高了,那另外一个主机想发起通信的时候他还能把电平拉低嘛?这种听起来就很危险的实验我没有做过,所以无从得知是否会有不良后果,但是如果我们可以通过上拉输入的方式来输出高电平,那是不是上面的矛盾都完全解决了呢?所以嘛,何乐而不为。
我们看到电气连接图中有时钟信号线,这意味着I2C通信是一个同步通信,有无时钟信号线是同步通信与异步通信的最主要的区别。
为了理解时钟线在信息传输中的作用,我举一个比较简单的例子:假设你是主机,我是从机,你的手上有一面红旗,我们编码:举起红旗代表1,放下红旗代表0,我们两个的位置相距很远无法交谈以至于你要用红旗来给我传输信息,那么,当你要给我传输信息"1111"时你会怎么做呢?有的人会说,那就连续举起四次红旗啊。但其实这样的想法是错误的,因为"举起"的动作前提必然是"放下",如果你连续举起了四次红旗,在我看来,你是"举起-放下-举起-放下-举起-放下-举起",按照编码规则,我认为你传输的信息应该是"1010101",这样一来信息的传递就出现了误差。你一看,欸,多了三个零,那你一直举着红旗不就行了?这样一来你给我发送的信息必定只有1而没有0。但问题又来了,你一直举着红旗,我怎么知道你要给我传输的数据是"1111111"(7个1)还是"11"(2个1)还是其他位数的"11111…"呢?在你发出同样信息的前提下,我对你发出的信息的采样起始时间,采样频率决定了我接收到的信息是什么样的。
那如何确定信息采样的起始时间和采样频率,才能让我接收到的信息恰恰就是你想表达的信息呢?这里有两个解决方案:
方案1:
我和你都去买一块10块钱的电子手表,我们两个手表的时间都是一样地转动,我和你约定,在你作出某一个特定动作(随便什么都行)的一瞬间,我和你同时开始掐表计时,约定每隔5秒就对信息进行一次采样。那如果你要给我传输的第一个信息是0,你就在第一个5秒前放下红旗,到了第一个5秒的时刻,我一看,发现你是放下红旗,那么我就记录下一个0;如果你要给我传输的第二个信息是1,那就在第二个5秒前举起红旗,我一看,就能记录下一个1,以此类推。这里要注意的是,既然以及约定好了采样的时间点,那么在两个相邻时间点之间,我是没有必要看你做了什么动作的(可能是因为我比较懒x),你在这段时间可以干任何事情(比如说放松下手臂),只要在我采样的时间点你能够给我你想要给的信息就行。换句话说,信息只在某一个特定的时间点才进行传输,而不允许在相邻两个时间点之间进行传输,就算你心血来潮想要在这段时间内多传输几个比特的信息,也没有用,因为我根本不会看(就算看了我也不知道你是在休息还是在传递信息)。
方案2:
你去买多一面蓝旗,我们约定,当你举起蓝旗的瞬间,我就去看一下你拿红旗的手是举起还是放下,从而记录下1或0,这样也能够接收到准确的信息。
如果你以前对单片机的几种通信比较了解,那么你一定能够看出,我所描述的第一种通信方式就是异步通信,第二种通信方式就是同步通信。
就我个人理解,异步通信和同步通信都需要时钟,只不过异步通信的时钟是同时存在于主机和从机的"北京时间",而同步通信的时钟是由主机单方面控制的"红绿灯"。
好,有了同步和异步通信的概念,我们就能够通过约定一系列的通信规则,来准确传输信息,这也就是我们所说的通信协议。I2C通信就是一种同步通信,而研究现有的同步通信的通信协议,是绝对少不了"时序图"这一概念的,所以在开始学习I2C协议前,我们先来了解一下两个时序图:(以下部分借用了网络图片,如有侵权请与我联系)
1.方波时序图
这样的时序图代表着信号线的电平在0和1之间反复横跳,按这种规律变化的信号线常常被人们用来传输时钟信号(类似于前面举起蓝旗又放下的例子)。由于实际器件是无法做到瞬时改变电压值的,所以在跳变沿的地方有时也会是一个斜线而不是一条垂直线。
2.Either or (非此即彼)信号时序图
我们可以看到,第二条信号的时序图有很多交叉,在同一时间点电平又上又下的,令人迷惑。但其实它的意思是,在"又上又下"的地方,你可以置高电平,或者也可以置低电平(不存在又高又低的电平,这里不是量子力学),而这个电平就代表着你想传输出去的信息。
好,理解了这两个基本的时序图,我们就足以阅读I2C的时序图了。
首先我们来看信号的起始与终止的时序图:
观察时序图可以发现以下规律:
信号的起始:SCL高电平时,SDA产生一个下降沿
信号的终止:SCL高电平时,SDA产生一个上升沿
另外要记得,在I2C通讯的大部分过程中,SDA和SCL线的控制权都在主机手上(后面会提到从机控制的情况)。
接下来,我们看看数据是如何同步传输的:
SDA线置为高电平代表1,置为低电平代表0,在从机进行数据采样之前,SCL被置于低电位,而在采样时,SCL线拉高,此时SDA线就不允许切换电平了,它处在什么电平就决定了从机读到的数据是0还是1。而当SCL线被拉回低电位的时候,SDA线既可以切换电平,也可以保持不变,为下一比特信息的传输作准备。
好,我们来看数据传输的字节格式:
对于I2C通信而言,可以无限制地传输字节,但是每一个字节的长度必须是8bit。我们知道I2C通信一种总线通信,一个总线上可能会挂有许多部从机,但每一个从机都不一定是同样功能的,我们希望一个主机调遣目标从机时不会对其他的从机产生影响,因此,主机在发起通信后,必须先发送地址位进行寻址,只有地址与主机输出相符的从机才会有响应,这个地址位一般是占用7bit。另外,主机还需要告诉从机自己是想对它进行读还是写的操作,因此还要再加多1bit存放该信息,其中"写"代表"0","读"代表"1"。这样一来,主机发送的第一字在发送完8位数据之后,就确定了应答的从机是哪一个、以及它的工作模式。
当完成一个字节的发送之后,需要有一个来自从机的响应告诉主机已经收到了信息。因为即使主机联系了从机要他去干活,但没准别人正在忙别的没看微信,也不知道你发了消息过来,如果从机没有收到主机的报文,那么后面主机的数据发送将毫无意义。因此,从机响应是必须的。具体的方法是,当8位数据传输完以后,主机继续控制SCL线,但释放SDA线(也就是用上拉输入置于高电平),SDA线的控制权交给从机,这时从机如果收到了信号,便会把SDA线拉低。SCL置高时,主机发现SDA变低电平了,就能够得知从机已经收到了数据,于是就可以继续发送下一个字节的信息了。如果从机没有响应,那主机只好重新发起通信。
相关文章