前言
我一直不是特别喜欢读文档,习惯了通过视频学习,因为视频能更直观地展现信息,给人一种更生动、形象的感觉。然而,随着学习的内容逐渐增多,我发现并不是所有的知识点都能在视频中找到,或者视频中展示的内容往往存在不准确或错误的情况。于是,我开始尝试通过阅读文档来学习,认为这是理解开发者思路和协议本质的最直接途径。
学习 I2C 协议是这种尝试的一部分。作为最简单的通信协议之一,I2C协议相较于更复杂的协议(例如 BLE5.3 的 Core 部分就有三千多页文档),无疑是一个更合适的入门选择。在此过程中,除了参考协议的官方文档外,我还阅读了多个设备的技术手册,力图从多个角度全面理解I2C协议的各个方面。通过这一学习过程,我总结了这篇笔记,希望它能对你有所帮助。如果在阅读过程中发现任何错误,敬请指正。
参考文档:
I2C-bus specification and user manual — NXP Semiconductor
A Basic Guide to I2C — Texas Instruments
ADXL345 Data Sheet — ADI
ADS111x Data Sheet — Texas Instruments
Atmel AT24C01C and AT24C02C Data Sheet — MICROCHIP
文中出现的图片来源于网络,如有侵权,请联系我进行删除
文章目录
I²C
简介
MCU 与外围设备之间的通信需要通过某种数字协议,而 I²C 就是这些协议之一
I²C 是一种使用串行数据线SDA
和串行时钟线SCL
的双线串行通信协议,支持在一个总线上连接多个设备,且支持多个控制器在总线上进行数据收发
通信以字节包发送,每个设备在总线上具有唯一地址
发展历史
I²C 通常被称为I two C
,代表内部集成协议,在 1982 年由 Philips Semiconductor (飞利浦半导体,现在的 NXP Semiconductor 恩智浦半导体) 发明,作为一种低速通信协议,它用于将控制器设备(如 MCU 等)和外围设备连接起来
I2C 是一个被广泛运用的协议,原因如下:
- 只需要两条通信线路
SDA
和SCL
即可进行通信 - 以总线的形式连接多个设备,且控制器可以与总线上的任一设备进行通信
- 制造成本低
总线是电子系统中用于传输数据、地址和控制信号的通信路径。它连接着系统中的不同部件,使得数据能够在多个设备之间进行交换和传递。总线通常包括数据线、地址线和控制线,支持多种通信协议,如 I2C、SPI 等。通过总线,多个设备能够共享同一条信号路径,有效地提高了系统的连接效率和扩展性
物理层
双线通信
设备连接方式
I2C 协议使用两条线路来进行所有设备之间的通信:
-
SDA(Serial Data Line)
数据线,用于向目标设备传输数据 -
SCL(Serial Clock Line)
时钟线,主要由控制器控制的串行时钟,用于同步数据的传输
这两条线是共享的,即所有连接到I2C总线的设备都通过这两条线进行通信,同时,这两条线都需要连接上拉电阻
上拉电阻是电子电路中常用的一种电阻配置,它将信号线连接到电源电压(通常是 Vcc),确保信号线在没有驱动信号时能够稳定地保持在高电平状态
在像 I2C 这样的开漏连接系统中,上拉电阻非常重要,因为信号线在某些情况下会处于悬空状态,无法自动拉高。上拉电阻通过提供一定的电流,保证信号线能够从低电平(接地)拉回到高电平,确保数据传输稳定并且符合逻辑要求
通信模式
-
I2C 是一种半双工通信,每次通信只有一个控制器或目标设备在总线上发送数据
-
主设备(Controller)负责启动和停止通信
- 主设备控制通信的开始(发送起始信号)和结束(发送停止信号),从而避免了总线上的总线争用(Bus Contention)问题(在多个设备共享同一总线时,如果没有明确的控制信号,可能会出现多个设备同时试图发送数据的冲突情况)
-
在 I2C 总线中,每个设备都被分配了一个唯一的地址
- 当主设备需要与某个从设备通信时,会通过这个唯一的地址来选定目标设备进行数据传输,这使得 I2C 能够在同一总线中支持多个控制器和多个目标设备,每个设备只会响应自己地址对应的命令或数据传输,避免了设备间的干扰
-
I2C 总线中的数据线(SDA)和时钟线(SCL)采用的是开漏连接
- 这意味着这些信号线通过晶体管与地连接,只有当设备需要传输信号时,才能拉低线路至地电平
- 设备不会主动将信号线拉高,信号线处于悬空状态时需要外部电路来将其拉高(上拉电阻的作用)
半双工通信(Half-Duplex)
在半双工模式下,数据传输是双向的,但不能同时进行,也就是说,设备在同一时刻只能发送或接收数据,而不能同时进行这两项操作
- 比如两个人传纸条,只有一张纸,一个人写完以后另一个人才能写
全双工通信(Full-Duplex)
在全双工模式下,数据可以同时双向传输,即设备可以在同一时间同时发送和接收数据,通信效率更高
- 电话通话就是全双工通信,双方可以同时说话和听对方说话
速度模式
I2C 有多种速度模式:
-
Standard-mode 标准模式 — 100kbps
-
Fast-mode 快速模式 — 400kbps
- 要求:总线电容和驱动能力允许使用快速模式
-
Fast-mode Plus 快速模式+ — 1Mbps
- 为了实现这一速度,设备中的驱动器需要提供更强的驱动能力,以确保信号的上升时间和下降时间(即信号从低电平到高电平或从高电平到低电平的变化速度)足够快
以上三种模式使用了相同的通信结构,但时间规格不同,硬件实现也有区别;I2C 还有另外两种支持更高速率的模式:
-
High-speed mode 高速模式 — 3.4 Mbps
- 为了启用这种高速度传输,控制器设备必须首先使用控制代码来激活目标设备的高速度模式
- 可能需要使用主动拉高电阻(一个具有更强驱动能力的电阻),它能帮助提升信号线的电压,从而确保信号能在较高的数据速率下稳定传输
- 较高的传输速率要求信号的上升和下降时间更短,因此需要更强的电流来驱动信号线
-
Ultra-fast mode 超快模式 — 5Mbps
- 该模式为只写模式,无法从设备接收数据
- 了实现更高的传输速度,Ultra-Fast 模式省略了 I2C 协议中的一些功能,可能包括某些校验、错误处理或其他通信协议的细节,这会提高通信效率,但也可能会降低通信的可靠性,特别是在较长距离或噪声较多的环境中
开漏连接
在 I2C 总线的 SDA 和 SCL 信号线中,采用的是开漏连接,这意味着信号线通过一个 NMOS 晶体管连接到地
-
当 NMOS 晶体管导通时,信号线被拉低(连接到地),此时为低电平
- 它将信号线连接到地,从而将 SDA 或 SCL 信号线拉低到逻辑“0”电平,这个过程非常快速,因为 NMOS 直接将信号线拉低,过渡从高电平到低电平的速度很快
- 从高到低的过渡速度主要由 NMOS 的驱动强度和总线电容(即 SDA 和 SCL 线的电气特性)共同决定,总线电容越大,信号的过渡时间就越长
-
当 NMOS 晶体管关闭时,信号线处于悬空状态,此时信号线被上拉电阻上拉至高电平
- 当 NMOS 关闭时,上拉电阻将信号线拉回到高电平(VDD),这时,信号线的过渡会受总线电容影响,较慢
- 选择较小的上拉电阻可以实现更快的上升时间,从而提高通信速度,但这会导致更高的功耗
- 较大的上拉电阻会使信号的上升时间变慢,从而降低通信速度,但相对较低的功耗
- 当 NMOS 关闭时,上拉电阻将信号线拉回到高电平(VDD),这时,信号线的过渡会受总线电容影响,较慢
NMOS(N型金属氧化物半导体)是一种场效应晶体管,它通过电场控制源极和漏极之间的导电通道,NMOS 具有低导通电阻和快速开关特性,广泛应用于数字电路和开关电路中
总线电容是指 PCB 上信号线与地、相邻导线或其他电路之间形成的电容。它影响信号的传播速度,尤其在 I2C 等总线通信中,当信号从低电平过渡到高电平时,较大的总线电容会使过渡速度变慢,影响数据传输的效率和稳定性。设计时通常会尽量减少总线电容,以提高信号传输的速度和质量
非破坏性总线争用
-
开漏输出的优势
- 总线争用不会导致破坏性状态:当0多个设备连接到同一条信号线时,如果其中任何一个设备将信号线拉低,信号线会被拉到低电平。这种
有线与(Wired-AND)
连接的方式保证了信号线的电平是多个设备输出的逻辑与(AND),不会导致破坏性争用,因为信号线的电平是由多个设备共同控制的 - 即使多个设备同时拉低信号线,信号线的状态仍然是有效的,且不会导致设备损坏或数据丢失
- 总线争用不会导致破坏性状态:当0多个设备连接到同一条信号线时,如果其中任何一个设备将信号线拉低,信号线会被拉到低电平。这种
-
推挽输出的风险
- 推挽输出(如SPI通信中常见的输出方式)使用了互补的 NMOS 和 PMOS 晶体管来分别驱动信号线的高电平和低电平。在多个设备使用推挽输出并连接在一起时,如果两个设备同时试图驱动信号线到不同的电平(例如,一个设备试图拉高,另一个设备试图拉低),就会发生冲突,导致破坏性状态,可能损坏设备或导致数据丢失
- 推挽输出(如SPI通信中常见的输出方式)使用了互补的 NMOS 和 PMOS 晶体管来分别驱动信号线的高电平和低电平。在多个设备使用推挽输出并连接在一起时,如果两个设备同时试图驱动信号线到不同的电平(例如,一个设备试图拉高,另一个设备试图拉低),就会发生冲突,导致破坏性状态,可能损坏设备或导致数据丢失
推挽输出(Push-Pull Output)是一种常见的信号输出方式,它使用互补的 NMOS 和 PMOS 晶体管来主动驱动信号线的高电平和低电平。与开漏输出不同,推挽输出可以快速且准确地切换信号的电平,不需要外部上拉电阻,适用于需要高速和较强电流驱动的应用。然而,多个推挽输出设备不能同时连接到同一信号线上,因为不同电平的输出会导致总线冲突,从而损坏设备或产生错误信号
开漏输出相当于所有设备共用一个 VDD,而推挽输出每个设备都有一个 VDD,当一个设备输出高电平,另一个设备输出低电平,相当于输出低电平的设备直接对 VDD 短路,可能会造成设备烧毁
I2C 通信协议
通信开始和停止
-
I2C 通信的启动(START Condition)
- I2C 通信由主设备(Controller Device)发起,使用 START 条件来开始总线上的通信。
- 首先将 SDA 拉低,然后将 SCL 拉低,这个顺序的变化表示主设备想要占用 I2C 总线进行通信,并且强制其他设备保持通信
-
I2C 通信的结束(STOP Condition)
- 当主设备完成通信时,它会通过发送 STOP 条件来释放总线,允许其他设备进行通信
- 首先将 SCL 拉高,然后将 SDA 拉高,表示通信结束,总线被释放
- 这个操作之后,总线被释放,允许其他控制器进行通信,或是同一控制器与不同的设备进行通信
逻辑 1/0
I2C 使用串行通信来传输数据。每个数据位(0或1)都通过SDA线(数据线)传输,而 SCL 线(时钟线)则控制数据位的传输时序,每一位数据的传输是同步的,SCL时钟在每个时钟周期中标记数据的读取和传输
-
逻辑 1 的传输
- 逻辑 1 是通过 SDA 释放线来实现的:当 SDA 线没有被设备拉低时,外部的上拉电阻将 SDA 线拉高,形成高电平,从而表示逻辑 1
-
逻辑 0 的传输
- 逻辑 0 是通过 SDA 拉低线来实现的:当 SDA 被设备拉低时,信号线的电压会降到接近地电平,表示逻辑 0
-
时序同步
- 当 SCL 产生脉冲时,逻辑 1 和逻辑 0 被接收。对于有效的位,SDA 在该位的 SCL 上升沿和下降沿之间保持不变。如果在 SCL 的上升沿和下降沿之间 SDA 发生变化,则可以将其解释为 I2C 总线上的 START 或 STOP 条件
- 当 SCL 产生脉冲时,逻辑 1 和逻辑 0 被接收。对于有效的位,SDA 在该位的 SCL 上升沿和下降沿之间保持不变。如果在 SCL 的上升沿和下降沿之间 SDA 发生变化,则可以将其解释为 I2C 总线上的 START 或 STOP 条件
数据帧
I2C 协议被分为多个帧
通信开始时,控制器设备在发送 START 信号后发送地址帧;地址帧后跟着一个或多个数据帧,每个数据帧由一个字节组成;每个帧还包含一个应答位,用来通知控制器目标设备或控制器设备已经接收到通信
-
在地址帧的开始,控制器设备发起一个 START 信号
- 控制器设备首先将 SDA 拉低,然后将 SCL 拉低来生成 START 信号
- 这允许控制器设备在总线上占用总线,而不受其他控制器设备的争用
-
每个 I2C 目标设备都有一个唯一的 I2C 地址,当与某个特定目标设备开始通信时,控制器使用目标设备的地址来发送或接收数据,在随后的 I2C 帧中进行传输,I2C 地址一般由 7 位组成,总线上的设备每个都有一个唯一的地址
- 7 位地址意味着可以有128(2^7) 个唯一的地址
- I2C 协议有一些保留地址,进一步限制了可用设备的数量
-
该帧中的第 8 位是读写位(R/W)
- 如果该位为 1,表示控制器要求从目标设备读取数据
- 如果该位为 0,表示控制器要求向目标设备写入数据
-
在每个通信字节后,会使用额外的第 9 位来验证通信是否成功。在地址字节通信结束时,目标设备在 SCL 脉冲期间将 SDA 拉低,表示目标设备已接收到地址,称为确认(ACK)位
- 如果该位为高电平,表示没有目标设备接收到地址,通信失败
- 如果该位为低电平,表示ACK(确认),即接收方已成功接收数据
-
地址帧之后会跟随一个或多个数据帧。每个数据帧一次发送一个字节。在每个数据字节传输后,会进行一个 ACK 确认
- 如果数据字节是写操作,则目标设备将 SDA 拉低以确认传输
- 如果数据字节是读取操作,则控制器将 SDA 拉低以确认数据已被接收
-
通信完成后,控制器发出 STOP 条件。首先释放 SCL,然后释放 SDA
- 控制器使用 STOP 信号表示通信已完成,并释放 I2C 总线
写操作
-
以 START 信号开始
-
写入目标设备地址
- 如果目标设备的地址匹配,该设备会发送一个 ACK,表示已接收到有效地址,并且设备准备好接收数据
-
当控制器发送带有写命令的地址后,控制器接着告诉设备正在写入哪个寄存器
- 第二个发送的字节是指向数据寄存器的寄存器指针
-
作为响应,目标设备通过拉低 SDA 发送一个 ACK ,表示目标设备向控制器确认已接收到地址指针数据,并且已知道正在写入哪个寄存器
-
接下来,寄存器的值被逐字节发送到目标设备
- 目标设备拉低 SDA 表示收到数据
-
传输完成后,此控制器通过发出 STOP 条件释放总线
有关连续读取/写入,需要根据目标设备的技术手册,查看是否支持单个或多个字节的读取/写入
从指定寄存器读取数据
先写入后读取
-
以 START 信号开始
-
写入目标设备地址
- 在此时,读/写位(R/W) 被设置为低,表示通信以向设备写入开始
- 目标设备通过拉低 SDA 发送一个 ACK
-
当控制器发送带有写命令的地址后,控制器接着告诉设备要读取哪个寄存器
- 目标设备通过拉低 SDA 发送一个 ACK ,表示目标设备向控制器确认已接收到地址指针数据
-
控制器发送 STOP 信号
-
再次发送 START 信号
-
控制器再次写入 I2C 目标设备地址
- 控制器已经指示了要读取哪个寄存器,现在控制器向设备发送读取命令,以便读取数据
- 此时,R/W 位被设置为高,表示读取
-
在地址帧完成后,目标设备发送 ACK 确认地址
-
目标设备向控制器发送数据
- 单字节读取:控制器应在接收到数据后发送 NACK,然后发送 STOP
- 多字节读取:控制器应在每个中间字节发送 ACK,最后一个字节发送 NACK,然后发送 STOP
-
控制器发送 STOP 停止读取
保留地址
在I2C协议中,向目标设备写入和读取数据需要使用I2C地址,用于识别控制器希望与之通信的设备
通常,这个地址通过一个字节传输,其中地址本身占用7位,第8位用于指示是从设备读取数据还是向设备写入数据
并非所有7位地址都可以用于目标设备,一些地址被保留用于其他用途:I2C拥有多个保留地址集,这些地址的使用基于特定的应用场景受到限制。使用这些保留地址调用的功能是设备的可选项,并不一定在所有I2C设备中都可用
X = don’t care; 1 = HIGH; 0 = LOW
目标地址 | R/W | 功能描述 |
---|---|---|
000 0000 | 0 | 通用调用地址 |
000 0000 | 1 | START 字节 |
000 0001 | X | C-Bus 总线地址 |
000 0010 | X | 保留用于不同的总线格式 |
000 0011 | X | 保留用于未来用途 |
000 01XX | X | Hs 模式控制器代码 |
111 11XX | 1 | 设备 ID |
111 10XX | X | 10 位目标地址 |
通用调用地址
向通用调用地址进行写操作用于同时向连接到 I2C 总线的所有设备发送命令,并非所有设备都设计为响应通用调用地址,如果有设备响应,那么目标设备可以处理通用调用后的第二个字节及后续字节
设备的响应取决于紧随通用调用之后的字节的最低有效位,这决定了两种不同的情况
-
第二个字节(B)的最低有效位为 0
- 第二个字节可用于向接收通用调用的设备发送命令
- 例如,通过 I2C 发送的常见命令之一是通用调用复位(General Call Reset):如果第二个字节是06h,表示控制器设备正在向I2C总线上的所有设备发送通用调用复位命令
-
第二个字节(B)的最低有效位为 1
- 控制器正在以硬件通用调用(Hardware General Call)的形式发送两字节序列
- 第二个字节用于发送硬件控制器的地址,以便在系统中标识自身1011。 其他控制器设备可以识别该地址,并将信息定向到充当目标设备的硬件控制器
START 字节
在I2C通信中,数据传输通常以一个“START”条件开始,并非所有微处理器都内置有I2C控制器。这意味着这些微处理器无法自动处理I2C通信(比如 STC89C52RC),需要通过软件来手动管理数据传输
此外,这些微处理器可能没有专门的中断机制来检测I2C总线上的通信活动,因此无法依赖硬件中断来通知处理器有新的数据传输
为了监控I2C总线上的通信活动,缺乏内置控制器和中断机制的微处理器必须通过不断地检查(轮询)SDA和SCL线路的状态来检测是否有通信发生
- 这种方法需要处理器以较高的频率轮询,以确保不会错过任何通信活动。这会消耗大量的处理能力,降低系统的整体效率
为了减少处理器的负担,可以在通信开始前发送一个特殊的START字节。具体方法是,控制器在START字节中使用7个零作为地址(即地址为00)
-
目标设备(接收方)在检测到这个特殊的START字节时,可以暂时降低轮询SDA和SCL线路的频率。这意味着在未检测到通信开始前,设备不需要频繁检查线路状态,从而节省处理能力
-
当目标设备检测到在START字节期间SDA线路发送了零(即检测到START信号),它会切换到更高的轮询速率,以便迅速检测接下来的I2C传输地址和数据
-
一旦I2C传输以STOP条件结束,目标设备会恢复到较低的轮询速率,继续节省处理能力
普通的 START 是将两根线拉低,告诉总线上的所有设备要开始通信了
这个特殊的 START 是将两根线拉低,发送 7 个 Byte 后拉高 1 Byte 再拉低
C-Bus 地址、不同的总线格式、未来用途
-
C-Bus 地址
- 用于允许 C-Bus 协议的设备连接到I2C总线
- 主要用于家庭和建筑自动化,在部分地区广泛使用
- 大多数 I2C 设备不会响应此地址,因而忽略该地址
-
不同的总线格式
- 用于支持不同协议之间的通信,促进多协议环境中的设备互操作性
- 仅限那些设计用于跨协议通信的 I2C 设备
-
未来用途
- 预留给未来可能的功能扩展或新协议
- 当前尚未定义具体用途,保留以供未来使用
跨协议通信的 I2C 设备指的是能够在不同通信协议之间进行数据交换和互操作的设备,这类设备通常充当桥梁或转换器,将 I2C 协议与其他协议(如SPI、UART、CAN等)连接起来,从而实现不同系统或组件之间的无缝通信
C-Bus(Control Bus)是一种用于家庭和建筑自动化系统的通信协议和网络技术。由施耐德电气(Schneider Electric)开发,C-Bus 旨在实现多设备之间的可靠、灵活和高效的控制与通信,广泛应用于智能家居、办公楼、商场、酒店等各种建筑环境中
Hs 模式控制器代码
高速控制器代码(High-Speed Controller Code)代码范围从 04 到 07(通常用于读写指示的第 8 位被用作高速控制器代码的一部分),这些高速控制器代码是保留的 8 位代码,不用于目标地址或其他用途
每个高速控制器都有一个唯一的控制器代码,I2C 总线上最多有八个高速控制器,高速模式控制器设备的控制器代码是软件可编程的,由系统设计师选择
高速控制器代码与标准设备地址分离,避免干扰正常的设备通信
因此每个设备和它对应的高速控制器其实相当于是“两个设备”
支持高速模式的设备在标准模式或快速模式下开始运行,控制器代码启用高速模式,高速控制器代码允许高速控制器之间的仲裁,并指示高速模式传输的开始。该代码启用内部电流源,使 I2C 通信总线比仅使用上拉电阻更快
启用后,高速数据传输在数据传输过程中持续进行,重复的 START 条件继续高速模式的数据传输(可以在不释放总线控制权的情况下,继续进行数据的传输,能够在一次通信会话中完成多步操作,如先设置寄存器指针再读取数据,从而优化整体的通信流程),而 STOP 条件将 I2C 总线返回到快速或标准模式
启用高速模式的通信过程
-
通信开始
- 设备以标准模式或快速模式启动
- 控制器发送 START 条件
-
发送保留地址
- 第一个字节使用为高速控制器代码保留的地址发送
- 控制器代码启用所有支持高速模式的设备的高速模式,并启用控制器内部用于高速模式的电路
-
发送目标地址和读写位
- 控制器随后发送高速模式设备的目标地址,并跟随一个读/写位以进行通信
- 数据由控制器或目标设备传输,每个数据字节后都有 ACK 确认,类似于标准的 I2C 通信
-
持续通信
- 目标设备继续通信,直到接收到 STOP 条件或接收到用于新目标地址的重复 START
设备 ID
保留地址7C到7F的用途
- 地址7C到7F专门用于设备ID的识别和读取
- 这些地址不是用于常规的读写操作,而是用于获取设备的制造商ID、部件识别码和芯片版本信息
- 允许控制器识别多个设备,并通过设备ID来区分和验证每个设备的具体信息
注意:这里的
7C到7F
是 7 位二进制数字,前三位和后四位的值,不含读写位
通信过程
-
发送 START 条件
-
发送保留的设备 ID 地址(7C 到 7F)并写入
- 控制器发送一个字节,其中前 7 位是保留的设备 ID 地址(例如,7C),第 8 位(R/W位)设置为 0,表示写操作
- 多个设备响应:保留地址可能被多个设备响应,多个设备会在收到这个地址后发送 ACK 信号,表示它们准备好接收后续数据
-
发送目标设备地址
- 控制器发送目标设备的具体地址,用于标识需要读取设备ID的特定设备
- 只有一个设备会对这个具体地址发送 ACK,确认它是目标设备
-
控制器发送重复 START 条件,保持总线控制权,继续进行下一步操作,而不释放总线
- 这里的开头必须是重复的 START,STOP 后跟 START 条件,或 STOP 后跟重复的 START 条件,然后访问不同的目标设备,将重置目标设备状态机,无法执行设备 ID 读取
-
发送保留的设备 ID 地址并读取
- 控制器再次发送保留的设备 ID 地址(7C 到 7F),这次第 8 位(R/W位)设置为 1,表示读操作
- 目标设备确认接收到读取命令,发送 ACK 信号
-
设备发送设备 ID 数据
-
数据帧结构
- 制造商 ID:12 位,用于标识设备的制造商
- 部件识别码:9 位,用于标识设备的具体部件或型号
- 芯片版本:3 位,用于标识设备的芯片版本或修订号
-
数据传输:设备通过三个 I2C 数据帧依次发送上述数据
-
-
-
-
发送 NACK 并结束通信
- 控制器 NACK 最后一个字节并以 STOP 结束设备 ID 读取
- 控制器可以随时通过发送 NACK 停止读取设备 ID
- 如果控制器在第三个字节之后继续确认字节,则目标将回滚到第一个字节并继续发送设备 ID 序列,直到检测到 NACK
控制器发送保留地址,相当于发送一条需要读取制造商信息的命令给设备,设备在接收到这个消息后,如果有相应的处理方式,则回应控制器
部分制造商对应ID
11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Company |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | NXP Semiconductors |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | NXP Semiconductors (reserved) |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | NXP Semiconductors (reserved) |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | NXP Semiconductors (reserved) |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | Ramtron International |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | Analog Devices |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | STMicroelectronics |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | ON Semiconductor |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | Sprintek Corporation |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | ESPROS Photonics AG |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | Fujitsu Semiconductor |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | Flir |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | O₂Micro |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | Atmel |
尝试对 ADXL345 和 AT24C02 使用这种方式读取制造商 ID,但似乎这两个设备都不支持该种方式
虽然 ADXL345 的数据手册中提到了:
“ADXL345符合《UM10204 I2C总线规范和用户手册》03版(2007年6月19日,NXP Semiconductors提供)”
10 位目标地址
在使用 7 位目标设备地址时,能够挂载在总线上的设备有限,为了扩展设备数量,保留设备地址可以用于将目标设备地址扩展至 10 位
在保留地址 111 10XX 中,最后两位地址作为 10 位地址的开头两位,最后一位依旧为读/写位,第二个字节则为完整的 8 位地址,总体通信过程类似于使用 7 位地址时
在写入时,控制器发送使用 10 位地址需要的两个字节,第一个字节后为 0(写),随后直接发送需要写入的数据
在读取时,控制器在发送使用 10 位地址需要的两个字节,第一个字节后为 0(写),重新发送 START 后再次发送使用 10 位地址的第一个字节,后跟 1(读),之后再进行数据读取
高级特性
时钟同步和仲裁
在 I2C 通信中,同一条总线上可以有多个控制器,因此可能有两个或多个设备试图同时占用总线进行通信,这需要多个活动控制器来解决哪个设备控制总线
SCL 同步
-
I²C 使用开漏(open-drain)连接方式将 SCL 和 SDA 线路与多个控制器连接,形成有线与(wired-AND)结构
- 任何一个控制器将 SCL 拉低,整个 SCL 线路即被拉低;只有所有控制器都释放 SCL 时,线路才能被拉高
当两个控制器均将 SCL 拉低时,每个 SCL 脉冲的低电平由拉低 SCL 时间最长的控制器决定,高电平由拉高时间最短的控制器决定,只有在所有控制器都释放 SCL 之后,线路才能被释放为高电平以进行串行时钟脉冲(当一个控制器释放 SCL 使其拉高时,由于另一控制器仍然将 SCL 拉低,此时 SCL 仍处于低电平状态,同时控制器 1 仍在监控 SCL,此时控制器 1 不能推进 SCL 脉冲,必须等待其他控制器释放时钟),这同步了所有控制器的串行时钟的开始
总线仲裁
串行时钟同步后
- 仲裁过程在 SDA 上进行,采用有线与(wired-AND)连接方式
- 当多个控制器同时传输数据时,SDA 线的实际状态决定最终的通信结果
在总线仲裁中,通信会持续进行,直到发送的数据出现差异。两个设备同时发送 I²C START,在传输的数据相同时,两个设备共同发送,当数据不同时,发送高电平的设备释放总线,成功赢得仲裁的控制器将继续完成数据传输,而失败的控制器则会停止传输并可能重试通信
-
检测差异
- 在每个数据位传输时,若出现不同控制器发送不同数据位(如一个发送0,另一个发送1),SDA 线路的实际状态将反映有线与的结果(例如0)
-
冲突检测与处理
- 发送1的控制器会检测到 SDA 实际为0,从而识别到冲突
- 检测到冲突的控制器(发送1的控制器)会立即释放总线,停止进一步传输
- 保持SDA为0的控制器(发送0的控制器)继续保持总线控制权,完成通信
仲裁不仅在发送 START 条件时发生,而是在整个数据传输过程中,每个数据位传输时都可能发生仲裁,这意味着即使在传输过程中后续的数据位也可能导致设备释放总线
在图中,两个设备同时发送 I²C START。在传输的前两位数据相同。到第三位时,出现了差异,因为控制器 1 发送了 0,而控制器 2 发送了 1;由于两个控制器都监控 SDA 和 SCL 线路,争用被发现。控制器 2 发现 SDA 是低电平,尽管它发送的是 1。为了保持总线上通信的正确有线与结果,控制器 2 释放总线,而控制器 1 赢得了仲裁
总线仲裁通过有线与结构,天然实现冲突检测与解决,无需额外的硬件或复杂协议,保证只有一个控制器能够控制总线,避免了数据冲突和通信错误,保证了通信的稳定性和可靠性
时钟拉伸
时钟拉伸(Clock Stretching)是 I²C 协议 中的一种可选功能,允许从设备(Slave)在需要时延长 SCL 时钟线的低电平时间,从而暂停数据传输,主要用于以下情况:
-
从设备需要更多时间来处理数据或准备数据
- 当从设备接收到一个字节并确认(ACK)后,可以将 SCL 线拉低,迫使主设备(Master)进入等待状态,直到从设备准备好进行下一个字节传输
-
从设备减慢总线时钟速度
- 具有有限硬件(比如处理器速度慢、操作耗时等)的微控制器可以通过延长每个时钟周期的低电平时间,来减慢总线时钟
工作原理
-
主设备控制时钟线
- 在正常情况下,主设备 负责控制 SCL 时钟线
-
从设备触发时钟拉伸
- 当从设备需要时钟拉伸时,会在确认(ACK)期间将 SCL 线拉低
-
SCL 保持低电平
- 由于 I²C 总线的 SCL 线采用 线与(Wired-AND)连接,SCL 线将保持低电平,直到从设备释放
-
主设备等待 SCL 高电平
- 主设备会监视 SCL 线,并等待其变为 高电平。在从设备释放 SCL 线之前,主设备无法继续传输
-
恢复通信
- 一旦从设备准备好,它会释放 SCL 线,允许其变为 高电平,从而恢复正常通信
主要还是与 I2C 的开漏连接的特点有关
特点与注意事项
-
可选功能
- 并非所有从设备都支持时钟拉伸
-
无限时钟拉伸
- 根据 I²C 规范,从设备可以无限期地保持 SCL 线为低电平
-
单主设备系统
- 如果总线上没有支持时钟拉伸的设备,则主设备可以忽略时钟拉伸处理
-
唯一控制 SCL 的场景
- 时钟拉伸是从设备唯一可以控制 SCL 线的情况
-
UFm 模式不支持
- 在 超快速模式(Ultra Fast-mode, UFm)中,不支持时钟拉伸
电气规格
I²C(Inter-Integrated Circuit)总线的电气规格主要涉及电压、电流、时序和上拉电阻等方面,这些参数对于确保总线正常运行至关重要
电压和逻辑电平
I²C 总线使用开漏输出,这意味着设备只能将线路拉低,而不能主动拉高,线路的拉高是通过上拉电阻实现的
-
逻辑低电平 (VIL) 和逻辑高电平 (VIH) 的阈值不是固定的,而是取决于供电电压 (VDD)
-
输入参考电平通常设置为 VDD 的 30% 和 70%
- VIL (逻辑低电平输入电压) 最大值为 0.3 × VCC
- VIH (逻辑高电平输入电压) 最小值为 0.7 × VCC
-
一些旧设备可能具有固定的输入电平,如 VIL = 1.5V 和 VIH = 3.0V,但新的设备需要符合 30%/70% 的规范
-
-
输出低电平电压 (VOL)
- 在标准模式和快速模式下,VOL 通常为 0.4V
- 在快速模式增强 (Fm+) 下,VOL 可能为 0.4V 或 0.2 × VCC (当 VCC ≤ 2V 时)
电压电平转换
I²C 总线的电压电平转换主要涉及在不同供电电压下工作的 I²C 设备之间进行通信时,如何匹配逻辑电平的问题
逻辑低电平 (VIL) 和 逻辑高电平 (VIH) 的阈值不是固定的,而是取决于供电电压 (VDD),不同 VDD 的设备之间的电压不匹配可能导致通信问题,因此当总线上存在具有不同 VDD 的设备时,需要进行电压电平转换:
- 如果一个 3.3V 的控制器尝试与一个 5V 的目标设备通信,控制器可能无法将总线拉到足够高的电压,以被目标设备识别为逻辑高电平
- 反之,如果一个 5V 的控制器与一个 3.3V 的目标设备通信,控制器可能会将总线电压拉得过高,超出目标设备的最大输入电压范围,从而损坏设备
电平转换方法
-
使用电平转换器:最常见的做法是使用专门的 I²C 电平转换器 IC
- 这些器件可以双向转换电压电平,使得不同电压的设备能够正确通信
- 例如,PCA9306 等器件可以用于电平转换
电压不匹配时可能存在问题
- 通信失败:设备无法正确识别逻辑高电平或低电平,导致数据传输错误
- 设备损坏:过高的输入电压可能会损坏设备的 I/O 引脚
- 系统不稳定:不稳定的信号电平可能导致系统出现意想不到的行为
某些 I²C 设备,例如 ADS1115,其 SDA 和 SCL 引脚具有特殊的电压耐受能力,即使上拉电阻连接到比设备自身供电电压更高的电源电压,这些引脚也能正常工作
ADS1115 的数据手册中明确指出,其数字输入引脚(包括 SDA 和 SCL)的最大输入电压可以达到 5.5V,而不受其自身供电电压的影响
电流
-
低电平输出电流 (IOL):指设备在输出低电平时能够吸收的最大电流。
- 标准模式和快速模式下,IOL 通常为 3mA
- 快速模式增强 (Fm+) 下,IOL 可能为 20mA 或 6mA
-
输入电流 (Ii):指输入引脚的电流,通常很小,例如 ±10μA
时序
I²C 的时序参数定义了 SCL 和 SDA 信号的上升和下降时间、建立时间和保持时间,这些参数对于确保数据传输的正确性和可靠性至关重要,不同的 I²C 速度模式具有不同的时序要求
-
标准模式 (Sm)
- 时钟频率 (fSCL) 最高为 100 kHz
- 上升时间 (tr) 最大为 1000 ns
- 下降时间 (tf) 最大为 300 ns
- 数据建立时间 (tSU;DAT) 最小为 250 ns
-
快速模式 (Fm)
- 时钟频率 (fSCL) 最高为 400 kHz
- 上升时间 (tr) 最大为 300 ns
- 数据建立时间 (tSU;DAT) 最小为 100 ns
-
快速模式+ (Fm+)
- 时钟频率 (fSCL) 最高为 1 MHz
- 上升时间 (tr) 最大为 120 ns
-
高速模式 (Hs-mode)
- 时钟频率 (fSCLH) 最高为 3.4 MHz
- 在总线电容为 100pF 时,上升和下降时间都很快,具体参数可参考表12
- 当总线电容为 400pF 时,时钟频率最大为 1.7 MHz,具体的时序参数需要进行线性插值计算
-
超快速模式 (UFm)
- 时钟频率 (fUSCL) 最高为 5 MHz
- 上升时间和下降时间需要根据器件的电气特性和总线负载来评估
上拉电阻
-
SDA 和 SCL 线上需要 上拉电阻,以确保总线空闲时线路为高电平。
-
电阻值的选择 会影响总线速度和功耗。
- 过大的电阻值 会导致上升时间过长,限制通信速度。
- 过小的电阻值 会增加功耗。
-
上拉电阻的最小值取决于设备的拉低电流和 VOL,最大值取决于上升时间和总线电容。
上拉电阻阻值
正常的上拉电阻建议为 1 kΩ 至 10 kΩ。电阻越高,I2C 通信越慢
电阻越低,I2C 通信需要更多功率。基于几个不同的参数,可以计算出 I2C 总线速度的最小和最大电阻
最小上拉电阻阻值
最小上拉电阻值 (Rp(min)) 的计算基于以下三个关键因素:
- 电源电压 (VCC):这是上拉电阻连接到的电压源
- 器件的输出低电平电压 (VOL):当器件将线路拉低时,线路的电压会下降到接近地电平,但会有一个最小的输出低电平电压 (VOL)
- 器件的下拉电流 (IOL):当器件将线路拉低时,会有一个下拉电流 (IOL) 流过器件
R P ( min ) = VCC − V O L ( max ) I O L R_{P(\text{min})} = \frac{\text{VCC} - V_{OL(\text{max})}}{I_{OL}} RP(min)=IOLVCC−VOL(max)
为了确保设备能够有效地将总线拉低到可被识别为逻辑低的电平 (低于VOL),上拉电阻的值不能太小,如果电阻太小,设备下拉时的电流会过大,导致电压降不够低,无法被接收端识别为低电平
最大上拉电阻
在开漏连接释放输出电流后,上拉电阻通过对总线电容( C B C_B CB)充电,将总线电压拉高至逻辑高电平,由于电容的充电特性,总线上的电压随时间呈现指数上升波形,其上升时间 t RISE t_{\text{RISE}} tRISE 由上拉电阻 R p R_p Rp 和总线电容 C B C_B CB 决定,具体表现为:
由于 I2C 标准规定了最大上升时间,因此最大上拉电阻受到总线电容的限制,电阻越大,上拉输出上升越慢,不能足够快地达到逻辑高电平
上升时间定义为从数字输入低电平电压 (VIL) 的 0.3 倍电源电压过渡到数字输入高电平电压 (VIH) 的 0.7 倍电源电压的时间
随时间变化的指数稳定方程式:
V ( t ) = ( 1 − e − t R C ) × V CC V(t) = \left(1 - e^{-\frac{t}{RC}}\right) \times V_{\text{CC}} V(t)=(1−e−RCt)×VCC
对于上升时间和下降时间,有公式:
V I L = 0.3 × V CC = ( 1 − e − t 1 R p C b ) × V CC V_{IL} = 0.3 \times V_{\text{CC}} = \left(1 - e^{-\frac{t_1}{R_p C_b}}\right) \times V_{\text{CC}} VIL=0.3×VCC=(1−e−RpCbt1)×VCC
V I H = 0.7 × V CC = ( 1 − e − t 2 R p C b ) × V CC V_{IH} = 0.7 \times V_{\text{CC}} = \left(1 - e^{-\frac{t_2}{R_p C_b}}\right) \times V_{\text{CC}} VIH=0.7×VCC=(1−e−RpCbt2)×VCC
由上面两个公式可以得到:
t 1 = − R p C b ⋅ ln ( 0.7 ) t_1 = -R_p C_b \cdot \ln(0.7) t1=−RpCb⋅ln(0.7)
t 2 = − R p C b ⋅ ln ( 0.3 ) t_2 = -R_p C_b \cdot \ln(0.3) t2=−RpCb⋅ln(0.3)
t RISE = t 2 − t 1 = 0.8473 × R p × C b t_{\text{RISE}} = t_2 - t_1 = 0.8473 \times R_p \times C_b tRISE=t2−t1=0.8473×Rp×Cb
R p ( max ) = t RISE 0.8473 × C b R_p(\text{max}) = \frac{t_{\text{RISE}}}{0.8473 \times C_b} Rp(max)=0.8473×CbtRISE
- 当通信速率提高(如进入快速模式或高速模式)时,需要减小 R p R_p Rp 以保证信号满足上升时间的要求
推导过程
-
V ( t ) = V CC ⋅ ( 1 − e − t R p C b ) V(t) = V_{\text{CC}} \cdot \left(1 - e^{-\frac{t}{R_p C_b}}\right) V(t)=VCC⋅(1−e−RpCbt) (电容充电的基本公式,描述随时间 ( t ) 的电压变化)
-
V I L = 0.3 × V CC , V I H = 0.7 × V CC V_{IL} = 0.3 \times V_{\text{CC}}, \quad V_{IH} = 0.7 \times V_{\text{CC}} VIL=0.3×VCC,VIH=0.7×VCC (定义低电平 ( V_IL ) 和高电平 ( V_IH}),分别为 ( V_CC ) 的 30% 和 70%)
-
V I L = V CC ⋅ ( 1 − e − t 1 R p C b ) V_{IL} = V_{\text{CC}} \cdot \left(1 - e^{-\frac{t_1}{R_p C_b}}\right) VIL=VCC⋅(1−e−RpCbt1) (在时间 ( t_1 ) 时,电容电压达到 ( V_IL ))
-
0.3 = 1 − e − t 1 R p C b 0.3 = 1 - e^{-\frac{t_1}{R_p C_b}} 0.3=1−e−RpCbt1 (将 ( V_IL= 0.3 cdot V_textCC ) 代入公式)
-
e − t 1 R p C b = 0.7 e^{-\frac{t_1}{R_p C_b}} = 0.7 e−RpCbt1=0.7 (整理得出指数部分)
-
t 1 = − R p C b ⋅ ln ( 0.7 ) t_1 = -R_p C_b \cdot \ln(0.7) t1=−RpCb⋅ln(0.7) (解出时间 ( t_1 ))
-
V I H = V CC ⋅ ( 1 − e − t 2 R p C b ) V_{IH} = V_{\text{CC}} \cdot \left(1 - e^{-\frac{t_2}{R_p C_b}}\right) VIH=VCC⋅(1−e−RpCbt2) (在时间 ( t_2 ) 时,电容电压达到 ( V_IH ))
-
0.7 = 1 − e − t 2 R p C b 0.7 = 1 - e^{-\frac{t_2}{R_p C_b}} 0.7=1−e−RpCbt2 (将 ( V_IH = 0.7 cdot V_CC ) 代入公式)
-
e − t 2 R p C b = 0.3 e^{-\frac{t_2}{R_p C_b}} = 0.3 e−RpCbt2=0.3 (整理得出指数部分)
-
t 2 = − R p C b ⋅ ln ( 0.3 ) t_2 = -R_p C_b \cdot \ln(0.3) t2=−RpCb⋅ln(0.3) (解出时间 ( t_2 ))
-
t RISE = t 2 − t 1 t_{\text{RISE}} = t_2 - t_1 tRISE=t2−t1 (上升时间定义为从 ( V_{IL} ) 到 ( V_{IH} ) 的时间差)
-
t RISE = R p C b ⋅ ln ( 0.7 0.3 ) t_{\text{RISE}} = R_p C_b \cdot \ln\left(\frac{0.7}{0.3}\right) tRISE=RpCb⋅ln(0.30.7) (通过整理 ( t_2 - t_1 ) 推导得到)
-
t RISE = 0.8473 ⋅ R p ⋅ C b t_{\text{RISE}} = 0.8473 \cdot R_p \cdot C_b tRISE=0.8473⋅Rp⋅Cb (计算 ( ln(2.3333)) 得到最终公式)
上升时间取决于 I2C 模式
即使 V C C V_{CC} VCC 发生变化,只要门限比例( 0.3 × V C C 0.3 \times V_{CC} 0.3×VCC 和 0.7 × V C C 0.7 \times V_{CC} 0.7×VCC)不变,公式依然成立,因为充电时间由 R p R_p Rp 和 C b C_b Cb 决定,而不是 V C C V_{CC} VCC 的绝对值
如果 V I L V_{IL} VIL 和 V I H V_{IH} VIH 的门限比例改变,公式中的 0.8473 0.8473 0.8473 系数需要重新计算(可能会有这种情况,但是我没找到)
当使用 Vcc = 3.3V 标准模式时,将 1000 纳秒的上升时间除以 0.8473 × 400 pF 0.8473 \times 400 \, \text{pF} 0.8473×400pF,得出最大上拉电阻为 2.95 kΩ,最小电阻 967 Ω
R p ( max ) = 1000 ns 0.8473 ⋅ 400 pF ≈ 2.95 k Ω R_p(\text{max}) = \frac{1000 \, \text{ns}}{0.8473 \cdot 400 \, \text{pF}} \approx 2.95 \, \text{k}\Omega Rp(max)=0.8473⋅400pF1000ns≈2.95kΩ
R P ( min ) = VCC − V O L ( max ) I O L = 3.3 V − 0.4 V 3 mA = 2.9 V 3 mA = 967 Ω R_{P(\text{min})} = \frac{\text{VCC} - V_{OL(\text{max})}}{I_{OL}} = \frac{3.3 \, \text{V} - 0.4 \, \text{V}}{3 \, \text{mA}} = \frac{2.9 \, \text{V}}{3 \, \text{mA}} = 967 \, \Omega RP(min)=IOLVCC−VOL(max)=3mA3.3V−0.4V=3mA2.9V=967Ω
对于最小电阻 967 Ω 和最大电阻 2.95 kΩ,这些值看起来为电阻提供了一个较窄的范围。然而,这个小范围是因为上拉电阻的大小是基于最大标准模式总线电容 400 pF 计算的。而 400 pF 的总线电容实际上非常大,尤其对于板上的寄生电容来说。如果设计中总线电容较低(这更常见),则最大电阻可以增加,从而减少 I2C 总线上消耗的功率。
对于标准模式,上升时间为 1000 纳秒,使用 400 pF 的最大总线电容,最大电阻计算结果约为 2.95 kΩ
实际应用中,总线电容通常小于最大值,因此可以使用更大的电阻以降低功耗
总线电容
-
I²C 总线上的 总电容 限制了可以连接到总线的设备数量
- 总线电容 (Cb) 指的是 SDA 和 SCL 线上所有连接设备的电容之和,以及 PCB 走线和其他寄生电容
-
不同的 I²C 速度模式有不同的最大总线电容限制
- 标准模式和快速模式下,最大总线电容为 400 pF
- 快速模式增强 (Fm+) 下,最大总线电容为 550 pF
- 高速模式下,最大总线电容可达 400 pF,但时序参数会受到影响。
其他电气特性
-
输入电容 (Ci): 每个 I/O 引脚的电容,通常为 10pF
-
噪声容限: 总线上噪声的容限范围,分为低电平噪声容限 (VnL) 和高电平噪声容限 (VnH)
- VnL 为 0.1 × VDD
- VnH 为 0.2 × VDD
不同的设备对电气特性的要求可能不同,具体需要查阅设备的技术手册