i2c总线相关知识

接口/总线/驱动

1120人已加入

描述

  I2C总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。

  一、 技术性能:

  工作速率有100K和400K两种;

  支持多机通讯;

  支持多主控模块,但同一时刻只允许有一个主控;

  由数据线SDA和时钟SCL构成的串行总线;

  每个电路和模块都有唯一的地址;

  每个器件可以使用独立电源

  二、 基本工作原理:

  以启动信号START来掌管总线,以停止信号STOP来释放总线;

  每次通讯以START开始,以STOP结束;

  启动信号START后紧接着发送一个地址字节,其中7位为被控器件的地址码,一位为读/写控制位R/W,R. /W位为0表示由主控向被控器件写数据,R/W为1表示由主控向被控器件读数据;

  当被控器件检测到收到的地址与自己的地址相同时,在第9个时钟期间反馈应答信号;

  每个数据字节在传送时都是高位(MSB)在前;

  写通讯过程:

  1. 主控在检测到总线空闲的状况下,首先发送一个START信号掌管总线;

  2. 发送一个地址字节(包括7位地址码和一位R/W);

  3. 当被控器件检测到主控发送的地址与自己的地址相同时发送一个应答信号(ACK);

  4. 主控收到ACK后开始发送第一个数据字节;

  5. 被控器收到数据字节后发送一个ACK表示继续传送数据,发送NACK表示传送数据结束;

  6. 主控发送完全部数据后,发送一个停止位STOP,结束整个通讯并且释放总线;

  读通讯过程:

  1. 主控在检测到总线空闲的状况下,首先发送一个START信号掌管总线;

  2. 发送一个地址字节(包括7位地址码和一位R/W);

  3. 当被控器件检测到主控发送的地址与自己的地址相同时发送一个应答信号(ACK);

  4. 主控收到ACK后释放数据总线,开始接收第一个数据字节;

  5. 主控收到数据后发送ACK表示继续传送数据,发送NACK表示传送数据结束;

  6. 主控发送完全部数据后,发送一个停止位STOP,结束整个通讯并且释放总线;

     I2C总线特征

  I2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址(可以从I2C器件的数据手册得知),主从设备之间就通过这个地址来确定与哪个器件进行通信,在通常的应用中,我们把CPU带I2C总线接口的模块作为主设备,把挂接在总线上的其他设备都作为从设备。

I2C总线

  I2C总线上可挂接的设备数量受总线的最大电容400pF 限制,如果所挂接的是相同型号的器件,则还受器件地址位的限制。

  I2C总线数据传输速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s。一般通过I2C总线接口可编程时钟来实现传输速率的调整,同时也跟所接的上拉电阻的阻值有关。

  I2C总线上的主设备与从设备之间以字节(8位)为单位进行双向的数据传输。

   I2C总线协议

  I2C协议规定,总线上数据的传输必须以一个起始信号作为开始条件,以一个结束信号作为传输的停止条件。起始和结束信号总是由主设备产生。总线在空闲状态时,SCL和SDA都保持着高电平,当SCL为高电平而SDA由高到低的跳变,表示产生一个起始条件;当SCL为高而SDA由低到高的跳变,表示产生一个停止条件。在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线;而在停止条件产生后,本次数据传输的主从设备将释放总线,总线再次处于空闲状态。如图所示:

  I2C总线

  在了解起始条件和停止条件后,我们再来看看在这个过程中数据的传输是如何进行的。前面我们已经提到过,数据传输以字节为单位。主设备在SCL线上产生每个时钟脉冲的过程中将在SDA线上传输一个数据位,当一个字节按数据位从高位到低位的顺序传输完后,紧接着从设备将拉低SDA线,回传给主设备一个应答位,此时才认为一个字节真正的被传输完成。当然,并不是所有的字节传输都必须有一个应答位,比如:当从设备不能再接收主设备发送的数据时,从设备将回传一个否定应答位。数据传输的过程如图所示:

  I2C总线

  在前面我们还提到过,I2C总线上的每一个设备都对应一个唯一的地址,主从设备之间的数据传输是建立在地址的基础上,也就是说,主设备在传输有效数据之前要先指定从设备的地址,地址指定的过程和上面数据传输的过程一样,只不过大多数从设备的地址是7位的,然后协议规定再给地址添加一个最低位用来表示接下来数据传输的方向,0表示主设备向从设备写数据,1表示主设备向从设备读数据。如图所示:

  I2C总线

   I2C总线操作

  对I2C总线的操作实际就是主从设备之间的读写操作。大致可分为以下三种操作情况:

  第一,主设备往从设备中写数据。数据传输格式如下:

  I2C总线

  第二,主设备从从设备中读数据。数据传输格式如下:

  I2C总线

  第三,主设备往从设备中写数据,然后重启起始条件,紧接着从从设备中读取数据;或者是主设备从从设备中读数据,然后重启起始条件,紧接着主设备往从设备中写数据。数据传输格式如下:

  I2C总线

  第三种操作在单个主设备系统中,重复的开启起始条件机制要比用STOP终止传输后又再次开启总线更有效率。

  1.2 I2C总线硬件接口电路示例

  1.2.1 I2C总线硬件接口电路示例一

  I2C总线

  这个电路是基于LPC2368 ARM7芯片进行设计的,使用其内部的I2C接口作为主设备,使用ADT75和SC16IS740作为两个从设备的I2C总线应用。

  ADT75是一个带I2C接口的温度传感器器件,数据手册上对其地址的描述如下:

  I2C总线

  由此,其地址跟A0、A1、A2引脚的接法有关,我们这里的实例是将A0、A1、A2全部接到高电平上,因此其地址是:1001111(即0x4F),又因根据协议再给地址添加一个最低位(方向位,默认给写方向),因此最后这个温度传感器作为从设备的地址是:10011110(即0x9E)。

  SC16IS740是一个具有I2C或者SPI接口的扩展UART的器件(通过第8脚来决定使用I2C还是SPI接口,我们这里要求使用I2C接口,因此将第8脚接到高电平)。根据数据手册,我们同样的可以知道地址跟A0、A1的接法有关,我们这里的A0接高电平,A1接低电平。因此这个器件作为从设备的地址是:10010010(即0x92)。

  1.2.2 I2C总线硬件接口电路示例二

  I2C总线

  这个电路是Mini2440开发板上I2C总线接口的应用。我们可以看到,SDA和SCL线上接了一个10K的上拉排阻。AT24C08是一个容量为8Kbit的EEPROM存储器件(注意是8Kbit,也就是1KB) ,根据数据手册中器件地址部分的描述,AT24C08的地址是:1010+A2A1A0+方向位,其中1010是EEPROM的类型识别符;仅仅使用A2来确定总线访问本器件的从设备地址,这里接的低电平,所以为0;A1和A0是器件内部页地址,在对器件擦除或者编程时使用,虽然这里也接的低电平,但器件内部并不使用引脚的输入值,也就是说A1和A0的值是由软件进行设定的。

  1.3 脱离操作系统的I2C总线驱动示例(以电路示例一为例)

  1.3.1 LPC2368中I2C接口寄存器描述

  LPC2368中有三个I2C总线接口,分别表示为I2C0、I2C1和I2C2,每个I2C接口都包含7个寄存器。它们分别是:

  I2C控制置位寄存器(I2CONSET): 8位寄存器,各位不同的设置是对I2C总线不同的控制。

I2C总线

I2C总线

  在前面的I2C总线特征中我们提到过,I2C总线的速率通过可编程时钟来调整,即必须通过软件对I2SCLH和I2SCLL寄存器进行设置来选择合适的数据频率和占空比。 频率由下面的公式得出(fPCLK是PCLK的频率)。

  I2C总线

   LPC2368中I2C总线操作

  在1.1.4中我们已经讲过了对I2C总线的操作,但那只是从协议和时序上的描述,那我们如何从软件上去体现出来呢?接下来我们就讨论这个问题。

  对I2C总线上主从设备的读写可使用两种方法,一是使用轮询的方式,二是使用中断的方式。轮询方式即是在一个循环中判断I2C状态寄存器当前的状态值来确定总线当前所处的状态,然后根据这个状态来进行下一步的操作。中断方式即是使能I2C中断,注册I2C中断服务程序,在服务程序中读取I2C状态寄存器的当前状态值,再根据状态值来确定下一步的操作。

  不管使用哪种方法,看来I2C状态寄存器的值是至关重要的。这些状态值代表什么意思呢?下面我们描述一些常用的状态值(详细的状态值含义请参考数据手册)。

  0x08: 表明主设备向总线已发出了一个起始条件;

  0x10: 表明主设备向总线已发出了一个重复的起始条件;

  0x18: 表明主设备向总线已发送了一个从设备地址(写方向)并且接收到从设备的应答;

  0x20: 表明主设备向总线已发送了一个从设备地址(写方向)并且接收到从设备的非应答;

  0x28: 表明主设备向总线已发送了一个数据字节并且接收到从设备的应答;

  0x30: 表明主设备向总线已发送了一个数据字节并且接收到从设备的非应答;

  0x40: 表明主设备向总线已发送了一个从设备地址(读方向)并且接收到从设备的应答;

  0x48: 表明主设备向总线已发送了一个从设备地址(读方向)并且接收到从设备的非应答;

  0x50: 表明主设备从总线上已接收一个数据字节并且返回了应答;

  0x58: 表明主设备从总线上已接收一个数据字节并且返回了非应答;

  四、 总线信号时序分析

  1. 总线空闲状态

  SDA和SCL两条信号线都处于高电平,即总线上所有的器件都释放总线,两条信号线各自的上拉电阻把电平拉高;

  2. 启动信号START

  时钟信号SCL保持高电平,数据信号SDA的电平被拉低(即负跳变)。启动信号必须是跳变信号,而且在建立该信号前必修保证总线处于空闲状态;

  3. 停止信号STOP

  时钟信号SCL保持高电平,数据线被释放,使得SDA返回高电平(即正跳变),停止信号也必须是跳变信号。

  4. 数据传送

  SCL线呈现高电平期间,SDA线上的电平必须保持稳定,低电平表示0(此时的线电压为地电压),高电平表示1(此时的电压由元器件的VDD决定)。只有在SCL线为低电平期间,SDA上的电平允许变化。

  5. 应答信号ACK

  I2C总线的数据都是以字节(8位)的方式传送的,发送器件每发送一个字节之后,在时钟的第9个脉冲期间释放数据总线,由接收器发送一个ACK(把数据总线的电平拉低)来表示数据成功接收。

  6. 无应答信号NACK

  在时钟的第9个脉冲期间发送器释放数据总线,接收器不拉低数据总线表示一个NACK,NACK有两种用途:

  a. 一般表示接收器未成功接收数据字节;

  b. 当接收器是主控器时,它收到最后一个字节后,应发送一个NACK信号,以通知被控发送器结束数据发送,并释放总线,以便主控接收器发送一个停止信号STOP。

  五、 寻址约定

  地址的分配方法有两种:

  1. 含CPU的智能器件,地址由软件初始化时定义,但不能与其它的器件有冲突;

  2. 不含CPU的非智能器件,由厂家在器件内部固化,不可改变。

  高7位为地址码,其分为两部分:

  1. 高4位属于固定地址不可改变,由厂家固化的统一地址;

  2. 低三位为引脚设定地址,可以由外部引脚来设定(并非所有器件都可以设定);

  主控器向被控器发送的信息种类有:启动信号、停止信号、7位地址码、读/写控制位、10位地址码、数据字节、重启动信号、应答信号、时钟脉冲。

  被控器向主控器发送的信息种类有:应答信号、数据字节、时钟低电平。

  下面对I2C总线通信过程中出现的几种信号状态和时序进行分析。

  ①总线空闲状态。

  I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

  ②启动信号。

  在时钟线SCL保持高电平期间,数据线SDA上的电平被拉低(即负跳变),定义为I2C总线总线的启动信号,它标志着一次数据传输的开始。

  启动信号是一种电平跳变时序信号,而不是一个电平信号。启动信号是由主控器主动建立的,在建立该信号之前I2C总线必须处于空闲状态,如图1所示。

  I2C总线

  图1 I2C总线上的启动信号和停止信号

  ③停止信号。

  在时钟线SCL保持高电平期间,数据线SDA被释放,使得SDA返回高电平(即正跳变),称为I2C总线的停止信号,它标志着一次数据传输的终止。

  停止信号也是一种电平跳变时序信号,而不是一个电平信号,停止信号也是由主控器主动建立的,建立该信号之后,I2C总线将返回空闲状态。

  ④数据位传送。

  在I2C总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。

  进行数据传送时,在SCL呈现高电平期间,SDA上的电平必须保持稳定,低电平为数据0,高电平为数据1。

  只有在SCL为低电平期间,才允许SDA上的电平改变状态。逻辑0的电平为低电压,而逻辑1的电平取决于器件本身的正电源电压VDD(当使用独立电源时),如图2所示。

  I2C总线

  图2 I2C总线上的数据位传送

  ⑤应答信号。

  I2C总线上的所有数据都是以8位字节传送的,发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。

  应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。

  对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。

  如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号P,如图3所示。

  I2C总线

  图3 I2C总线上的应答时序

  ⑥插入等待时间。

  如果被控器需要延迟下一个数据字节开始传送的时间,则可以通过把时钟线SCL电平拉低并且保持,使主控器进入等待状态。

  一旦被控器释放时钟线,数据传输就得以继续下去,这样就使得被控器得到足够时间转移已经收到的数据字节,或者准备好即将发送的数据字节。

  带有CPU的被控器在对收到的地址字节做出应答之后,需要一定的时间去执行中断服务子程序,来分析或比较地址码,其间就把SCL线钳位在低电平上,直到处理妥当后才释放SCL线,进而使主控器继续后续数据字节的发送,如图4所示。

  I2C总线

  图4 I2C总线上的插入等待时间

  ⑦重启动信号。

  在主控器控制总线期间完成了一次数据通信(发送或接收)之后,如果想继续占用总线再进行一次数据通信(发送或接收),而又不释放总线,就需要利用重启动Sr信号时序。

  重启动信号Sr既作为前一次数据传输的结束,又作为后一次数据传输的开始。利用重启动信号的优点是,在前后两次通信之间主控器不需要释放总线,这样就不会丢失总线的控制权,即不让其他主器件节点抢占总线。

  ⑧时钟同步。

  如果在某一I2C总线系统中存在两个主器件节点,分别记为主器件1和主器件2,其时钟输出端分别为CLK1和CL【0,它们都有控制总线的能力。

  假设在某一期间两者相继向SCL线发出了波形不同的时钟脉冲序列CLK1和CLK2(时钟脉冲的高、低电平宽度都是依靠各自内部专用计数器定时产生的),在总线控制权还没有裁定之前这种现象是可能出现的。

  鉴于I2C总线的“线与”特性,使得时钟线SCL上得到的时钟信号波形,既不像主器件1所期望的CLK1,也不像主器件2所期望的CLK2,而是两者进行逻辑与的结果。

  CLKI和CLK2的合成波形作为共同的同步时钟信号,一旦总线控制权裁定给某一主器件,则总线时钟信号将会只由该主器件产生,如图5所示。

  I2C总线

  图5 I2C总线上的时钟同步

  ⑨总线冲突和总线仲裁。

  假如在某I2C总线系统中存在两个主器件节点,分别记为主器件1和主器件2,其数据输出端分别为DATA1和DATA2,它们都有控制总线的能力,这就存在着发生总线冲突(即写冲突)的可能性。

  假设在某一瞬间两者相继向总线发出了启动信号,鉴于:I2C总线的“线与”特性,使得在数据线SDA上得到的信号波形是DATA1和DATA2两者相与的结果,该结果略微超前送出低电平的主器件1,其DATA1的下降沿被当做SDA的下降沿。

  在总线被启动后,主器件1企图发送数据“101……”,主器件2企图发送数据“100101……”。

  两个主器件在每次发出一个数据位的同时都要对自己输出端的信号电平进行抽检,只要抽检的结果与它们自己预期的电平相符,就会继续占用总线,总线控制权也就得不到裁定结果。

  主器件1的第3位期望发送“1”,也就是在第3个时钟周期内送出高电平。

  在该时钟周期的高电平期间,主器件1进行例行抽检时,结果检测到一个不相匹配的电平“0”,这时主器件1只好决定放弃总线控制杈;因此,主器件2就成了总线的惟一主宰者,总线控制权也就最终得出了裁定结果,从而实现了总线仲裁的功能。

  从以上总线仲裁的完成过程可以得出:仲裁过程主器件1和主器件2都不会丢失数据;各个主器件没有优先级别之分,总线控制权是随机裁定的,即使是抢先发送启动信号的主器件1最终也并没有得到控制杈。

  系统实际上遵循的是“低电平优先”的仲裁原则,将总线判给在数据线上先发送低电平的主器件,而其他发送高电平的主器件将失去总线控制权,如图6所示。

  I2C总线

  图6 I2C总线上的总线仲裁

  ⑩总线封锁状态。

  在特殊情况下,如果需要禁止所有发生在I2C总线上的通信活动,封锁或关闭总线是一种可行途径,只要挂接于该总线上的任意一个器件将时钟线SCL锁定在低电平上即可。

  I2C总线是由Philips公司开发的两线式串行总线,用于连接微控制器和外围设备。

  I2C总线支持多主控模式,任何能够进行发送和接收的设备都可以成为主设备。主控能够控制数据的传输和时钟频率,在任意的时刻只能有一个主控。

  组成I2C总线的两个信号为数据线SDA和时钟线SCL。为避免总信号线的混乱,要求各设备连接到总线的输出端必须是开漏输出或集电极开路输出的结构。根据这种结构的“线与”逻辑,I2C总线上任意器件输出低电平都会使相应总线上的信号线变低。

  总线空闲时,上拉电阻使SDA和SCL线都保持高电平。

  数据线 SDA 的电平状态必须在时钟线 SCL 处于高电平期间保持稳定不变。SDA 的电平状态只有在 SCL 处于低电平期间才允许改变。但是在 I2C总线的起始和结束时例外。

  当SCL稳定在高电平时,SDA由高到低的变化将产生一个开始位,而由低到高的变化则产生一个停止位。开始位和停止位都是由I2C主设备产生的。如果从设备采用7位地址,则主设备在发起传输前,需先发送一字节的地址信息,前7位为设备地址,最后1位为读写标志。之后每次传输的数据也是一个字节,从MSB位(Most Significant Bit 最高有效位,对应有LSB: Least Significant Bit 最低有效位)开始传输。每个字节传完后,在SCL的第9个上升沿到来之前,接受方应该发出一个ACK位。

  应答位的时钟脉冲仍由主机产生,而应答位的数据状态则遵循“谁接收谁产生”的原则,即总是由接收器产生应答位。主机向从机发送数据时,应答位由从机产生;主机从从机接收数据时,应答位由主机产生。I2C总线标准规定:应答位为 0 表示接收器应答(ACK) ,常常简记为 A;为 1 则表示非应答(NACK) ,常常简记为NA。发送器发送完 LSB 之后,应当释放 SDA 线(拉高 SDA,输出晶体管截止) ,以等待接收器产生应答位。

  在切换数据的传输方向时,可以不必先产生停止条件再开始下次传输,而是直接再一次产生开始条件。I2C 总线在已经处于忙的状态下,再一次直接产生起始条件的情况被称为重复起始条件。例如:访问某一具有 I2C总线接口的 E2PROM 存储器时,主机先向存储器输入存储单元的地址信息(发送数据) ,然后再读取其中的存储内容(接收数据)。

  带有 I2C 总线的器件除了有从机地址(Slave Address)外,还可能有子地址。从机地址是指该器件在 I2C 总线上被主机寻址的地址, 而子地址是指该器件内部不同部件或存储单元的编址。 与从机地址一样,子地址实际上也是像普通数据那样进行传输的,传输格式仍然是与数据相统一的,区分传输的到底是地址还是数据要靠收发双方具体的逻辑约定。子地址的长度必须由整数个字节组成,可能是单字节(8 位子地址) ,也可能是双字节(16 位子地址) ,还可能是 3 字节以上,这要看具体器件的规定。

  I2C体系结构分3个部分:I2C核心、I2C总线驱动、I2C设备驱动。

  I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。

  I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。包含了i2c_adapter、i2c_algorithm和控制I2C适配器产生通信信号的函数。通过I2C总线驱动的代码,我们可以控制I2C适配器以主控方式产生开始位、停止位读写周期,以及以从设备方式被读写,产生ACK等。

  I2C设备驱动是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。包含了i2c_driver和i2c_client。

  所有的I2C设备都在sysfs文件系统中显示,存于/sys/bus/i2c/目录下,以适配器地址和芯片地址的形式列出。

  i2c_adapter对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个I2C适配器需要i2c_algorithm提供的通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用的i2c_algorithm的指针。

  i2c_driver对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client来描述。i2c_client一般被包含在I2C字符设备的私有信息结构体中。

  i2c_driver于i2c_client发生关联的时刻在i2c_driver的attach_adapter()函数被运行时。attach_adapter()会探测物理设备,当确定一个client存在时,把该client使用的i2c_client数据结构的adapter指针指向对应的i2c_adapter,driver指针指向该i2c_driver,并会调用i2c_adapter的client_register()函数。相反的过程会发生在i2c_driver的detach_client()函数被调用的时候。

  i2c_transfer()函数用于进行I2C适配器和I2C设备之间的一组消息交互,i2c_master_send()函数和i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息。i2c_transfer()函数本身并不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer函数真正驱动硬件流程。

  I2C总线驱动模块加载函数需完成两个工作:

  1.初始化I2C适配器所使用的硬件资源,如申请I/O地址、中断号等。

  2.通过i2c_add_adapter()添加i2c_adapter的数据结构,当然这个i2c_adapter数据结构的成员已经被xxx适配器的相应函数指针所初始化。

  I2C总线驱动模块的卸载函数要完成的工作与加载函数相反。

  I2C总线通信方法:我们要为特定的I2C适配器实现其通信方法,主要实现i2c_algorithm的master_xfer()函数和functionality()函数。functionality()函数用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C、I2C_FUNC_10BIT_ADDR、I2C_FUNC_SMBUS_READ_BYTE、I2C_FUNC_SMBUS_WRITE_BYTE等。master_xfer()函数在I2C适配器上完成传递给它的i2c_msg数组中的每个I2C消息。

  master_xfer()函数处理I2C消息数组,对于数组中的每个消息,判断消息类型,若为读消息,则赋从设备地址为(msg-》addr《《1)| 1 ,否则为msg-》addr《《1(赋地址为setaddr)。对每个消息产生一个开始位,紧接着传送从设备地址,然后开始数据的发送或接收,对最后的消息还需产生一个停止位。

  I2C设备驱动(i2c_driver和i2c_client)。

  I2C设备驱动要使用i2c_driver和i2c_client数据结构并填充其中的成员函数。i2c_client一般被包含在设备的私有信息结构体yyy_data中,而i2c_driver则适合被定义成全局变量并初始化。

  I2C设备驱动的模块加载函数中会做:

  (1)通过register_chrdev()函数将I2C设备注册为一个字符设备。

  (2)通过I2C核心的i2c_add_driver()函数添加i2c_driver。

  模板卸载函数中则相反。

  yyy_init()-》i2c_add_driver()-》yyy_attach_adapter()-》i2c_probe()-》yyy_detect()-》i2c_client初始化-》i2c_attach_client()-》yyy_init_client

  yyy_exit()-》i2c_del_driver()-》yyy_detach_client()-》i2c_detach_client()

  作为一种字符类设备,Linux I2C设备驱动的文件操作接口与普通的设备驱动是完全一致的。I2C设备的写操作经历了如下几个步骤:

  (1)从用户空间到字符设备驱动写函数接口,写函数构造I2C消息数组。

  (2)写函数把构造的I2C消息数组传递给I2C核心的传输函数i2c_transfer()。

  (3)I2C核心的传输函数i2c_transfer()找到对应适配器algorithm的通信方法函数master_xfer()去最终完成I2C消息的处理。

  read()/write()(用户空间)-》yyy_read()/yyy_write()(I2C设备驱动文件操作接口)-》i2c_transfer()(I2C核心)-》master_xfer()(I2C总线驱动)

  i2c-dev.c文件完全可以被看做一个I2C设备驱动,不过,它实现的一个i2c_client是虚拟、临时的,随着设备文件的打开而产生,并随设备文件的关闭而撤销,并没有被添加到i2c_adapter的clients链表中。i2c-dev.c针对每个I2C适配器生成一个主设备号为89的设备文件,实现了i2c_driver的成员函数以及文件操作接口,所以,i2c-dev.c的主体式“i2c_driver成员函数+字符设备驱动”。

  i2c-dev.c中提供i2cdev_read()、i2cdev_write()函数来对应用户空间要使用的read()和write()文件操作接口,这两个函数分别调用I2C核心的i2c_master_recv()和i2c_master_send()函数来构造一条I2C消息并引发适配器algorithm通信函数的调用,完成消息的传输。i2cdev_read()和i2cdev_write()函数不具备太强的通用性,灭有太大的使用价值,只能适用于非RepStart模式的情况。对于两条以上消息组成的读写,在用户空间需要组织i2c_msg消息数组并调用I2C_RDWR IOCTL命令。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分