一、简述
I2C(Inter-Integrated Circuit BUS) 集成电路总线, 只有两根双向信号线,一根是数据线SDA,另一根是时钟线SCL。
系统中的所有外围器件都具有一个7位的“从器件专用地址码”,其中高4位为器件类型,由生产厂家制定,低3位为器件引脚定义地址,由使用者定义( 之所以7bit因为1个bit要代表方向,主向从和从向主)。主控器件通过地址码建立多机通信的机制,因此I2C总线省去了外围器件的片选线,这样无论总线上挂接多少个器件,其系统仍然为简约的二线结构。终端挂载在总线上,有主端和从端之分,主端必须是带有CPU的逻辑模块,在同一总线上同一时刻只能有一个主端,可以有多个从端,从端的数量受地址空间和总线的最大电容400pF的限制。
二、连接方式
![](https://i-blog.csdnimg.cn/blog_migrate/b1906c203458be84a970305d1a95e819.png)
I2C 总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。
在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。
-
SCL(Serial Clock):串行时钟线,传输CLK信号,一般是主设备向从设备提供。
-
SDA(Serial Data):串行数据线,传输通信数据。
I2C使用一个7bit的设备地址,一组总线最多和112个节点通信。最大通信数量受限于地址空间及400pF的总线电容。
常见的I2C总线以传输速率的不同分为不同的模式:标准模式(100Kbit/s)、快速模式(400Kbit/s)、高速模式(3.4Mbit/s),时钟频率可以被下降到零,即暂停通信。 可以在总线上放置多个主设备节点,在停止位(P)发出后,即通讯结束后,主设备节点可以成为从设备节点。
-
主设备节点:产生时钟并发起通信的设备节点
-
从设备节点:接收时钟并响应主设备节点寻址的设备节点
1)I2C通信双方地位不对等,通信由主设备发起,并主导传输过程,从设备按I2C协议接收主设备发送的数据,并及时给出响应。
2)主设备、从设备由通信双方决定(I2C协议本身无规定),既能当主设备,也能当从设备(需要软件进行配置)。
3)主设备负责调度总线,决定某一时刻和哪个从设备通信。同一时刻,I2C总线上只能有一对主设备、从设备通信。
4)每个I2C从设备在I2C总线通讯中有一个I2C从设备地址,该地址唯一,是从设备的固有属性,通信中主设备通过从设备地址来找到从设备。
![](https://i-blog.csdnimg.cn/blog_migrate/a4312b37a51331778f542d644974b4cf.png)
三、通讯协议
1、基本特征
-
串行通信,所有的数据以位为单位在SDA线上串行传输
-
同步通信,即双方工作在同一个时钟下,一般是通信的A方通过一根CLK信号线,将A设备的时钟传输到B设备,B设备在A设备传输的时钟下工作。同步通信的特征是:通信线中有CLK。
-
低速率,数据量不大,速率低。速率:几百KHz,速率可能不同,不能超过IC的最高速率。
-
每一个设备都可以作为主设备或者从设备,而且 每一个设备都会对应一个唯一的地址, 主从设备之间就通过这个地址来确定与哪个器件进行通信,在通常的应用中,我们把 CPU带I2C总线接口的模块作为主设备,把挂接在总线上的其他设备都作为从设备。
-
主设备与从设备之间以字节(8位)为单位进行 双向的数据传输。
2、起始和结束
I2C协议规定,总线上数据的传输
必须以
一个起始信号作为开始条件,以
一个结束信号作为传输的停止条件。
起始和结束信号
总是
由主设备产生,
所有的通信都是主设备发起的,主设备可以发出询问的command,然后等待从设备的通信。
起始和结束信号产生条件:总线在空闲状态时,SCL和SDA都保持着高电平,当SCL为高电平而SDA由高到低的跳变,表示产生一个起始条件;当SCL为高而SDA由低到高的跳变,表示产生一个停止条件。
在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线;而在停止条件产生后,本次数据传输的主从设备将释放总线,总线再次处于空闲状态。
![](https://i-blog.csdnimg.cn/blog_migrate/8a42f80841e063e321308d311161f334.png)
3、数据传输原理
前面我们已经提到过,数据传输以字节为单位。主设备在SCL线上产生每个时钟脉冲的过程中将在SDA线上传输一个数据位,当一个字节按数据位从高位到低位的顺序传输完后,紧接着从设备将拉低SDA线,回传给主设备一个
应答位, 此时才认为一个字节真正的被传输完成。当然,
并不是所有的字节传输都
必须有一个应答位,比如:当从设备不能再接收主设备发送的数据时,从设备将回传一个否 定应答位。
数据传输的过程如图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/33acc1779c09d2d90de9d386940be999.png)
在前面我们还提到过,I2C总线上的每一个设备都对应一个唯一的地址,主从设备之间的数据传输是建立在地址的基础上,也就是说,主设备在传输有效数据之前
要先指定从设备的地址,地址指定的过程和上面数据传输的过程一样,只不过大多数从设备的地址是7位的,然后协议规定再给地址添加一个最低位用来表示接下来数据传输的方向,0表示主设备向从设备写数据,1表示主设备向从设备读数据。
向指定设备发送数据的格式如图所示:(每一最小包数据由9bit组成,8bit内容+1bit ACK, 如果是地址数据,则8bit包含1bit方向)
![](https://i-blog.csdnimg.cn/blog_migrate/3d2bb928f7ee87bc2037e5bafb5aa1d1.png)
注意:所有的数据传输过程中,SDA线的电平变化必须在SCL为低电平时进行
4、三种数据传输
-
主设备往从设备中写数据:
开始数据传输后,先发送一个起始位(S),主设备发送一个地址数据(由7bit的从设备地址,和最低位的写标志位组成的8bit字节数据,该读写标志位决定数据的传输方向),然后,主设备释放SDA线,并等待从设备的应答信号(ACK)。每一个字节数据的传输都要跟一个应答信号位。数据传输以停止位(P)结束,并且释放I2C总线。
![](https://i-blog.csdnimg.cn/blog_migrate/77eebdcde6e76b4247cda213b0a6ec4e.png)
-
主设备从从设备中读数据:
开始通讯时,主设备先发送一个起始信号(S),主设备发送一个地址数据(由7bit的从设备地址,和最低位的写标志位组成的8bit字节数据),然后,主设备释放SDA线,并等待从设备的应答信号(ACK),从设备应答主设备后,主设备再发送要读取的寄存器地址,从设备应答主设备(ACK),主设备再次发送起始信号(Sr),主设备发送设备地址(包含读标志),从设备应答主设备,并将该寄存器的值发送给主设备;
![](https://i-blog.csdnimg.cn/blog_migrate/fa6fc24e212ca7ebd087f3ab4f933a1c.png)
-
主设备往从设备中连续读写数据:
主设备要读取的数据,如果是大于一个字节的多个数据,就发送ACK应答信号(ACK),而不是非应答信号(NOACK),然后主设备再次接收从设备发送的数据,依次类推,直到主设备读取的数值是最后一个字节数据后,需要主设备给从设备发送非应答信号(NOACK),再发送结束信号(P),结束I2C通讯,并释放I2C总线。
![](https://i-blog.csdnimg.cn/blog_migrate/f126da314e57c04225b7a1f13ef30daa.png)
5、仲裁机制
![](https://i-blog.csdnimg.cn/blog_migrate/48b7df02c96254077d4be8c1f4c62d53.png)
上图是以两个节点为例的仲裁过程。DATA1和DATA2分别是主节点向总线所发送的数据信号,SDA为总线上所呈现的数据信号,SCL是总线上所呈现的时钟信号。当主节点1、2同时发送起始信号时,两个主节点都发送了高电平信号。这时总线上呈现的信号为高电平,两个主节点都检测到总线上的信号与自己发送的信号相同,继续发送数据。第2个时钟周期,2个主节点都发送低电平信号,在总线上呈现的信号为低电平,仍继续发送数据。在第3个时钟周期,主节点1发送高电平信号,而主节点2发送低电平信号。根据总线的线“与”的逻辑功能,总线上的信号为低电平,这时主节点1检测到总线上的数据和自己所发送的数据不一样,就断开数据的输出级,转为从机接收状态。这样主节点2就赢得了总线,而且数据没有丢失,即总线的数据与主节点2所发送的数据一样,而主节点1在转为从节点后继续接收数据,同样也没有丢掉SDA线上的数据。因此在仲裁过程中数据没有丢失。
四、代码相关
先以TP为例看下大概配置及使用方法。
1、首先TP作为一个外围器件,也就是从机,我们需要知道他的I2C寻址地址。这里可以通过datesheet或者直接问vendor来确认。我们这颗是0x4B。
2、需要看下硬件原理图,确认我们使用的是哪条I2C总线,因为我们有好多根I2C总线。
确认完以上两步,我们就可以在dtsi里配置相关信息
/vendor/qcom/proprietary/devicetree-4.19/product/pre-p0/product-pre-p0.dtsi
&qupv3_se13_i2c {
synaptics_tcm@4B {
compatible = "synaptics,tcm-i2c";
reg = <0x4B>;
...
}
}
这里的4B就是系统与TP通讯时的寻址空间。我们来看下对应的读写方法:
![](https://i-blog.csdnimg.cn/blog_migrate/408d49272194bf62f8aa87edc07b2605.png)
msg.add对应的即寻址地址,flags即读写标志位,len是读写长度,buf是需要写入的数据或者存储读取出来的数据。
TP把I2C的读取封装在
syna_tcm_read和
syna_tcm_write中。而i2c_transfer(最终调用的是
trace_i2c_read或者
trace_i2c_write)就是数据传输的平台方法,adaper是主从通讯的适配器,msg即为通讯的数据包,包含了通信时所需要的信息,第三个参数即数据包的个数。也就是说,如果我们通讯一次,num就为1,如果需要通讯多次,num就要设置为更大的值。我们看TP代码里用的都是1,也就是每次都只通信了一次。
参考文档: