详解I2C

I2C(也常写作 I I C IIC IIC I 2 C I^2C I2C),全称为Inter-Integrated Circuit(“互连集成电路”),用于在集成电路之间进行短距离数据传输。它由Philips(现在的NXP半导体)公司于1980年代初开发,并成为一种广泛应用于电子设备之间通信的标准。I2C协议简单、灵活且广泛支持,常用于连接传感器、存储器、显示屏和其他外设到微控制器、微处理器或其他集成电路上。这是一种简单的双向双线总线,非常适合用于微控制器与外设之间,或者多个微控制器之间的高效互连控制。

其名称反映了最初的设计目的:它专为“集成电路”间的通信设计。I2C的两条线包括SDA(串行数据线),用于传输信息;和SCL(串行时钟线),负责同步。I2C的一个优点是它只需要这两根线进行通信,这可以在复杂系统中有效地利用资源。

它也是一种在许多产品中广泛使用的标准,扩展和集成相对简单。

I2C的主要特点包括:

  • 通信方式:I2C采用主从式通信方式。一个主设备(通常是微控制器或处理器)控制总线并发起通信,而一个或多个从设备被动地响应主设备的命令或请求。
  • 总线结构:I2C使用两根线路进行通信:
    • SCL(Serial Clock):时钟线由主设备提供,用于同步通信速度。
    • SDA(Serial Data):数据线用于传输数据和控制信号。
  • 信号电平:I2C使用双向开漏(open-drain)输出,这意味着信号线可以被拉低(逻辑0)但不能被主动拉高,只能通过外部上拉电阻回到高电平(逻辑1)。
  • 地址和帧格式:I2C使用7位或10位的设备地址来唯一标识每个从设备。通信帧包括设备地址、读/写位、数据字节和应答位(ACK)。
  • 速率和模式:I2C支持不同的通信速率,通常有标准模式(100 kbit/s)和快速模式(400 kbit/s)。还有更高速的模式如高速模式(3.4 Mbit/s)和超高速模式(5 Mbit/s)。
  • 启动和停止条件:I2C通信的开始由主设备发送启动信号(SDA从高电平切换到低电平,同时SCL保持高电平)表示。通信结束时,主设备发送停止信号(SDA从低电平切换到高电平,同时SCL保持高电平)表示。
  • 多主设备支持:I2C协议允许多个主设备共享同一条总线,通过仲裁机制来解决总线竞争问题。
  • 应用领域:I2C广泛应用于各种设备和应用领域,例如传感器、存储器(如EEPROM)、显示屏、温度传感器、实时时钟(RTC)、扩展IO芯片等。

I2C是一种简单、灵活且广泛支持的串行通信协议,适用于在电子设备之间进行短距离数据传输。它具有较低的硬件复杂性和通信开销,因此在许多嵌入式系统和电子设备中被广泛采用。

I2C协议还有一些其他的特性和扩展,如高速模式、高容量模式、地址扩展等,以满足更复杂的应用需求。虽然I2C并不适合所有情况(例如,它并不适用于长距离或高速应用),但它是一种常见的、实用的选择,适用于简单的内部通信。当希望许多从设备共享通信线并由一个(或多个)主设备管理时,它特别有用。

I2C总线通用连接框图:

I2C总线互联系统示意图

在单主机系统中,可以抽象为:

单主机I2C总线

典型的多主机系统则为:

多主机I2C总线

电气特性

I2C使用开漏/开集电极结构,同时在同一数据线上使用输入缓冲器,这允许单个数据线用于双向数据流。

双向通信的开漏结构

开漏指的是一种输出类型,可以将总线拉低到某个电压(通常是地线),或者"释放"总线,让其被上拉电阻拉高。当总线被主设备或从设备释放时,上拉电阻(RPU)负责将总线电压拉高到电源电平。由于没有设备可以强制将线路拉高,这意味着总线永远不会遇到通信问题,其中一个设备尝试传输高电平,而另一个设备传输低电平,导致短路(电源电平到地)。I2C要求在多主设备环境中,如果主设备发送高电平,但检测到线路低电平(另一个设备将其拉低),则停止通信,因为另一个设备正在使用总线。推挽接口不允许此类自由,这是I2C的一个优点。

SDA/SCL内部基本结构

上图显示了在SDA/SCL线上的从设备或主设备的内部结构的简化视图,包括用于读取输入数据的缓冲器和用于传输数据的下拉场效应晶体管(FET)。设备只能将总线线路拉低(与地短接)或释放总线线路(对地高阻抗),并允许上拉电阻将电压拉高。这是在处理I2C设备时需要了解的重要概念,因为没有设备可以将总线保持高电平。这个特性是实现双向通信的关键。

拉低总线

如前所述,开漏结构只能将总线拉低,或者"释放"总线并由电阻将其拉高。下图展示了将总线拉低时的电流流动。希望传输低电平的逻辑会激活下拉场效应晶体管,提供对地的短路,将线路拉低。

拉低总线

释放总线

当从设备或主设备希望传输逻辑高时,它只能通过关闭下拉场效应晶体管来释放总线。这将使总线悬空,而上拉电阻将把电压拉高到电源电平,被解释为高电平。下图展示了通过上拉电阻的电流流动,将总线拉高。

释放总线

通信速率模式

速率模式

常规模式

高速模式

超快模式

接口规范

一般操作

I2C总线是一种标准的双向接口,使用控制器(即主设备)与从设备进行通信。除非被主设备寻址,否则从设备不能传输数据。I2C总线上的每个设备都有一个特定的设备地址,以区分其他在同一I2C总线上的设备。许多从设备在启动时需要进行配置,以设置设备的行为。这通常在主设备访问从设备的内部寄存器映射时完成,这些寄存器具有唯一的寄存器地址。一个设备可以有一个或多个寄存器,用于存储、写入或读取数据。

物理的I2C接口由串行时钟(SCL)线和串行数据(SDA)线组成。SDA和SCL线都必须通过上拉电阻连接到VCC电源。上拉电阻的大小取决于I2C线路上的电容量。只有当总线处于空闲状态时,才能启动数据传输。如果在停止条件之后,SDA和SCL线都保持高电平,总线将被认为是空闲的。

主设备访问从设备的一般过程如下:

  1. 如果主设备想要向从设备发送数据:

    • 主设备-发送器发送起始条件并寻址从设备-接收器
    • 主设备-发送器向从设备-接收器发送数据
    • 主设备-发送器通过停止条件终止传输
  2. 如果主设备想要接收/读取从设备的数据:

    • 主设备-接收器发送起始条件并寻址从设备-发送器
    • 主设备-接收器向从设备-发送器发送要读取的寄存器
    • 主设备-接收器从从设备-发送器接收数据
    • 主设备-接收器通过停止条件终止传输
起始和停止条件

I2C通信通过主设备发送起始条件(START condition)来初始化,并通过主设备发送停止条件(STOP condition)来终止。在SCL线为高电平时,SDA线上的高至低的跳变定义了起始条件。在SCL线为高电平时,SDA线上的低至高的跳变定义了停止条件。

起始和停止条件示例

重复起始条件

重复的起始条件(Repeated START condition)类似于起始条件(START condition),用作连续的停止条件和起始条件的替代。它在外观上与起始条件相同,但与起始条件不同,因为它发生在停止条件之前(总线不处于空闲状态)。这在主设备希望启动新的通信,但不希望通过停止条件使总线空闲,从而可能失去对总线的控制权(在多主设备环境中)时非常有用。

数据有效性和字节格式

在每个SCL时钟脉冲期间传输一个数据位。一个字节由SDA线上的八个位组成。一个字节可以是设备地址、寄存器地址或从从设备写入或读取的数据。数据以最高有效位(MSB)优先的顺序传输。在START和STOP条件之间,可以从主设备向从设备传输任意数量的数据字节。在时钟周期的高电平期间,SDA线上的数据必须保持稳定,因为当SCL为高电平时,数据线的变化被解释为控制命令(START或STOP)。

单字节传输示例

应答(ACK)和非应答(NACK)

接收器在每个数据字节(包括地址字节)后发送一个ACK位。ACK位允许接收器向发送器传达字节已成功接收,并且可以发送另一个字节。

在接收器发送ACK之前,发送器必须释放SDA线。为了发送ACK位,接收器在ACK/NACK相关的时钟周期(周期9)的低电平期间拉低SDA线,以便在ACK/NACK相关的时钟周期的高电平期间,SDA线保持稳定低电平。必须考虑到设置时间和保持时间。

当SDA线在ACK/NACK相关的时钟周期中保持高电平时,这被解释为非应答(NACK)。有几种情况会导致产生NACK:

  1. 接收器由于执行某些实时功能并且尚未准备好与主设备开始通信,因此无法接收或发送。
  2. 在传输过程中,接收器接收到无法理解的数据或命令。
  3. 在传输过程中,接收器无法接收更多的数据字节。
  4. 主设备接收器完成数据读取,并通过NACK向从设备表示这一点。

NACK波形示例

I2C数据

必须通过读取或写入从设备的寄存器来发送和接收数据。寄存器是从设备内存中包含信息的位置,无论是配置信息还是一些采样数据用于发送回主设备。主设备必须向这些寄存器中写入信息,以指示从设备执行任务。

虽然在I2C从设备中常见的是寄存器,但请注意,并非所有的从设备都会有寄存器。一些设备非常简单,只包含一个寄存器,可以通过在从设备地址之后立即发送寄存器数据来直接写入该寄存器,而无需寻址寄存器。单寄存器设备的一个例子是8位I2C开关,它通过I2C命令进行控制。由于它只有一个位来启用或禁用通道,因此只需要一个寄存器,主设备只需在从设备地址之后写入寄存器数据,跳过寄存器编号。

向I2C总线上的从设备写入数据

要在I2C总线上写入数据,主设备将在总线上发送一个带有从设备地址的起始条件,并将最后一位(R/W位)设置为0,表示写入操作。在从设备发送应答位后,主设备将发送要写入的寄存器地址。从设备再次发送应答位,通知主设备它已准备好。然后,主设备将开始向从设备发送寄存器数据,直到主设备发送完所有需要的数据(有时仅为一个字节),然后通过发送停止条件终止传输。

下图显示了向从设备寄存器写入单个字节的示例:

向从设备寄存器写数据示例

从I2C总线上的从设备读取数据

从从设备读取数据与写入数据非常相似,但有一些额外的步骤。为了从从设备读取数据,主设备首先必须告知从设备它希望从哪个寄存器读取数据。主设备通过类似写入操作的方式开始传输,发送带有R/W位为0(表示写入)的地址,然后是要从中读取数据的寄存器地址。一旦从设备确认了寄存器地址,主设备将再次发送起始条件,然后发送带有R/W位为1(表示读取)的从设备地址。这次,从设备将确认读取请求,主设备释放SDA总线,但仍向从设备提供时钟。在此事务的这个阶段,主设备将变为主设备接收器,而从设备将变为从设备发送器。

主设备将继续发送时钟脉冲,但会释放SDA线,以便从设备可以传输数据。在每个数据字节结束时,主设备将向从设备发送一个ACK,表示主设备准备好接收更多数据。一旦主设备接收到了所期望的字节数,它将发送一个NACK,告知从设备停止通信并释放总线。主设备随后发送停止条件。

下图显示了从从设备寄存器读取单个字节的示例:

从从设备寄存器读数据示例

实例

这两天调试 【开源】调试利器:高速 USB 转 UART、SPI、I2C、JTAG 这个模块时捕捉了一些波形,可以结合前文一起理解:

实例1

对应代码:

result = ch347_driver.i2c_set(device_index, 1)
if result:
    print("Success to set I2C speed.")
else:
    print("Failed to set I2C speed.")

result = ch347_driver.i2c_set_delay_ms(device_index, 1)
if result:
    print("Success to set I2C delay.")
else:
    print("Failed to set I2C delay.")


result = ch347_driver.stream_i2c(device_index, b'\x13', 2)
if result:
    print("Success!")
else:
    print("Failed!")

实例2

对应代码:

result = ch347_driver.i2c_set(device_index, 1)
if result:
    print("Success to set I2C speed.")
else:
    print("Failed to set I2C speed.")

result = ch347_driver.i2c_set_delay_ms(device_index, 1)
if result:
    print("Success to set I2C delay.")
else:
    print("Failed to set I2C delay.")


result = ch347_driver.stream_i2c(device_index, b'\x12\x13\x14', 8)
if result:
    print("Success!")
else:
    print("Failed!")

总结

I2C总结

公众号 | FunIO
微信搜一搜 “funio”,发现更多精彩内容。
个人博客 | blog.boringhex.top

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
i2c-tools是一个Linux下的I2C总线工具,它提供了一组用户空间的程序,可以用来与I2C设备进行通信和调试。i2c-tools包含了以下工具: - i2cdetect:扫描I2C总线,查找I2C设备的地址; - i2cdump:读取I2C设备的寄存器值; - i2cget:读取I2C设备的一个字节或多个字节; - i2cset:向I2C设备的寄存器写入一个字节或多个字节; - i2cprobe:测试I2C设备是否存在。 其中,i2cdetect是最常用的工具,可以帮助我们快速地扫描I2C总线上的设备,找到它们的地址。i2cdump和i2cget可以读取设备的寄存器值,i2cset可以向设备的寄存器写入值。 使用i2c-tools之前,需要先确保I2C总线的驱动程序已经加载,可以使用lsmod命令查看是否已经加载了i2c-dev和i2c-bcm2835模块(如果是树莓派系统,则是i2c-bcm2708)。如果没有加载,可以使用modprobe命令加载。 接下来,我们以i2cdetect为例,介绍一下i2c-tools的使用方法。 i2cdetect命令语法: ``` i2cdetect [-y] [-a] [-q|-r] i2cbus [first last] ``` 参数说明: - -y:不需要确认,直接扫描设备地址; - -a:扫描所有地址,包括保留地址; - -q:以静默模式运行,只输出找到的地址; - -r:以详细模式运行,输出找到的地址及设备型号; - i2cbus:I2C总线编号,例如0、1等; - first和last:设备地址范围,可以省略,如果省略,则默认扫描0x03~0x77之间的地址。 使用示例: 1. 扫描I2C总线0上的设备地址: ``` sudo i2cdetect -y 0 ``` 2. 扫描I2C总线1上的设备地址: ``` sudo i2cdetect -y 1 ``` 3. 扫描所有地址: ``` sudo i2cdetect -y -a 1 ``` 4. 扫描指定地址范围: ``` sudo i2cdetect -y 1 0x20 0x27 ``` 注意:使用i2cdetect命令时,需要使用sudo权限。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值