Notes on I2C

Notes on I2C

Updated: May 14, 2026 ✱ Created: May 7, 2026 ✱ Embedded 更新日期:2026年5月14日 ✱ 创建日期:2026年5月7日 ✱ 嵌入式

I rewrote this like 3 times over the course of 7 days and spent way too long on these diagrams so hopefully this is up to snuff. I2C Stands for Inter-Integrated Circuit and abbreviated as I2C or just I2C. It is a synchronous, two-wire serial communication bus used to connect low-speed peripherals over short distances. 我在7天内重写了这篇文章大约3次,并且在这些图表上花费了太多时间,希望这份内容足够出色。I2C 代表“集成电路间通信”(Inter-Integrated Circuit),简称为 I2C。它是一种同步、双线串行通信总线,用于在短距离内连接低速外设。

SDA Stands for Serial DAta. Transfers data bidirectionally between devices. SDA 代表串行数据(Serial DAta)。在设备之间双向传输数据。

SCL Stands for Serial CLock. Carries the clock signal to synchronize all data transfers. SCL 代表串行时钟(Serial CLock)。承载时钟信号以同步所有数据传输。

Topology There is always at least one controller and one target. Commonly there is a single controller and one or more targets. Uncommonly, there can be multiple controllers (multi-controller). The targets can be connected at any point on the lines. Each line is connected to its own pull up resistor which connects to the same positive voltage. 拓扑结构 总线中始终至少有一个控制器(Controller)和一个目标设备(Target)。通常是一个控制器连接一个或多个目标设备。在较少见的情况下,可以存在多个控制器(多控制器模式)。目标设备可以连接在总线的任何位置。每条线路都连接有各自的上拉电阻,这些电阻连接到相同的正电压。

Open-Drain In I2C, SDA and SCL are idle-HIGH, meaning they are HIGH by default. As a result, I2C is considered an “Open-Drain” system. When the drain is closed, SDA and SCL are pulled LOW (connected to ground). When the drain is open, SDA and SCL return to HIGH (connected to positive voltage). 开漏(Open-Drain) 在 I2C 中,SDA 和 SCL 在空闲时为高电平(HIGH),这意味着它们默认处于高电平状态。因此,I2C 被视为一种“开漏”系统。当漏极闭合时,SDA 和 SCL 被拉低(连接到地);当漏极断开时,SDA 和 SCL 回升至高电平(连接到正电压)。

Bus Arbitration I2C can support multiple controllers on the same bus. The way to resolve this falls out naturally from the open-drain design. 总线仲裁 I2C 支持在同一总线上存在多个控制器。解决冲突的方式自然地源于其开漏设计。

Wired-AND Logic On an open-drain bus, driving LOW is an active action (pulling the line to ground), while driving HIGH is a passive release (letting the pull-up resistor return the line to positive voltage). If one device pulls LOW while another releases to HIGH, the line goes LOW. That is: Pulling always overrides releasing. The bus is therefore logically equivalent to AND-ing every device’s output. This property is called “Wired-AND Logic”, and it is the electrical foundation that makes arbitration possible. 线与逻辑(Wired-AND Logic) 在开漏总线上,驱动低电平是一个主动动作(将线路拉至地),而驱动高电平是一个被动释放(让上拉电阻将线路恢复至正电压)。如果一个设备拉低电平,而另一个设备释放至高电平,线路最终会变为低电平。也就是说:拉低总是覆盖释放。因此,总线在逻辑上等同于对每个设备的输出进行“与”运算。这种特性被称为“线与逻辑”,它是实现总线仲裁的电气基础。

Resolution Every transmitting controller is required to monitor SDA while it transmits. On each bit, the controller compares what it tried to send against what actually appears on the SDA line. If they match then there is no conflict and the controller continues to transmit. If the controller tried to send HIGH but reads back LOW, another controller is pulling LOW, meaning this controller has lost arbitration and stops driving the bus, waiting for the next “Stop Condition” before attempting to claim the bus again. 仲裁解析 每个发送数据的控制器在传输时都必须监控 SDA 线。在每一位(bit)传输时,控制器会将它试图发送的数据与 SDA 线上实际出现的数据进行比较。如果两者匹配,则没有冲突,控制器继续传输。如果控制器试图发送高电平但读回的是低电平,说明另一个控制器正在拉低总线,这意味着该控制器仲裁失败,它将停止驱动总线,并等待下一个“停止条件”(Stop Condition)出现,然后再尝试重新获取总线控制权。

The winning controller never knows a conflict occurred. It sees its own bit on the line and continues transmitting its transaction uncorrupted. In conclusion, open-drain makes arbitration a passive consequence of the electrical design rather than an active protocol feature. 获胜的控制器永远不会知道冲突曾经发生。它在总线上看到的是自己发送的位,并继续完整地传输其事务。总之,开漏设计使得仲裁成为电气设计的一种被动结果,而不是协议层面的主动功能。

Both controllers successfully claim the bus by pulling SDA LOW during the “Start Condition”. The conflict is resolved during the address transmission that immediately follows. As both controllers clock out their target addresses bit-by-bit, the first bit where they disagree is where arbitration is decided: The controller that tries to send HIGH while the other sends LOW detects the mismatch and backs off. The winning controller continues transmitting, unaware that arbitration even happened. 两个控制器在“起始条件”(Start Condition)期间通过将 SDA 拉低,都能成功声称获取总线。冲突在随后的地址传输阶段得到解决。当两个控制器逐位输出目标地址时,第一个出现分歧的位即决定了仲裁结果:试图发送高电平而对方发送低电平的控制器会检测到不匹配并退出。获胜的控制器继续传输,完全不知道仲裁曾经发生。

And what if the controllers transmit identical bits throughout the entire transaction? Neither one detects a conflict! Both see the bus matching what they sent on every bit, both believe they own the bus, and both complete their transactions successfully! The spec actually calls this out: Two controllers can actually complete an entire transaction without error, as long as the transmissions are identical. This isn’t a bug, it’s a deliberate feature of the design. 如果控制器在整个事务中发送完全相同的位会怎样?双方都不会检测到冲突!双方都看到总线上的数据与自己发送的每一位匹配,双方都认为自己拥有总线,并且双方都能成功完成事务!规范中明确指出了这一点:只要传输内容完全相同,两个控制器确实可以无错误地完成整个事务。这不是 Bug,而是设计中有意为之的特性。

The target still receives one coherent transaction, the fact that two controllers were driving the bus is invisible and harmless because their signals to the target were identical. For other reasons I won’t get into, the spec instructs that multi-controller systems should design their software protocol to avoid identical-transmission scenarios where possible, or accept that identical transactions are harmless duplicates. This becomes more clear as we explore these concepts later, so if this doesn’t make sense yet, come back and re-read this after you finish up! 目标设备仍然接收到一个连贯的事务,两个控制器同时驱动总线的事实对目标设备来说是不可见且无害的,因为它们发送给目标的信号完全相同。出于其他我不打算深入探讨的原因,规范建议多控制器系统应在软件协议设计中尽可能避免这种“相同传输”场景,或者接受这种相同的事务只是无害的重复。随着我们后续对这些概念的深入探讨,这一点会变得更加清晰,所以如果现在还没理解,请在读完后再回来重读一遍!

Pull-Up Pulling down a line is usually much faster than pulling up a line. Pull up time is a function of bus capacitance and values of the pull up resistors. Typical values for these resistors is 1-10 kΩ. Pull up resistors are a compromise: High resistances increase the time needed to pull up the line and thus limits max bus speed. Lower resistances allow faster communications but require higher power. 上拉(Pull-Up) 拉低线路通常比拉高线路快得多。上拉时间取决于总线电容和上拉电阻的阻值。这些电阻的典型值为 1-10 kΩ。上拉电阻是一种折中方案:高电阻会增加拉高线路所需的时间,从而限制总线的最大速度;低电阻允许更快的通信,但需要更高的功耗。

Speed Mode

ModeSpeed
Standard100 kbps
Fast400 kbps
Fast Plus1 Mbps
High Speed3.4 Mbps
Ultra Fast5 Mbps

速度模式

模式速度
标准 (Standard)100 kbps
快速 (Fast)400 kbps
快速增强 (Fast Plus)1 Mbps
高速 (High Speed)3.4 Mbps
超高速 (Ultra Fast)5 Mbps

I2C can operate at different bus speeds, often referred to as modes. Hardware is specified as compliant to the max mode it can theoretically achieve. Ultra fast mode is write-only and makes some modifications to the protocol. High speed mode devices are backwards compatible to lower speeds. I2C 可以在不同的总线速度下运行,通常称为模式。硬件规格通常标注其理论上能达到的最高模式。超高速模式仅支持写入,并对协议进行了一些修改。高速模式设备向下兼容较低的速度。

Roles In I2C there are roles that don’t change that I’m referring to as “Fixed Roles”. Fixed roles describe who controls the bus. There are also roles that do change depending on the type of transaction (Read/Write) which I’m referring to as “Transactional Roles”. Transactional roles describe who is talking and who is listening during a transaction. A controller is always a controller and a target is always a target, but they may be a sender or a receiver depending on the direction of the current transaction (Read/Write). 角色 在 I2C 中,有些角色是不会改变的,我称之为“固定角色”(Fixed Roles)。固定角色描述了谁控制总线。还有一些角色会根据事务类型(读/写)而改变,我称之为“事务角色”(Transactional Roles)。事务角色描述了在事务期间谁在发送数据,谁在接收数据。控制器永远是控制器,目标设备永远是目标设备,但根据当前事务的方向(读/写),它们可能是发送者或接收者。

Fixed Roles These are determined by the hardware/software design and don’t change during a transaction. 固定角色 这些角色由硬件/软件设计决定,在事务期间不会改变。

Controller The controller is the device that “owns” the bus during an I2C transaction. Controls when a transaction starts (START Condition) and stops (STOP Condition). Decides whether a transaction is a “Read” or “Write”. Drives the clock line SCL. Chooses which target will participate in the transaction. Note that this was originally called “Master” and is still referred to as such in many explanations and… 控制器(Controller) 控制器是在 I2C 事务期间“拥有”总线的设备。它控制事务何时开始(起始条件)和停止(停止条件),决定事务是“读”还是“写”,驱动时钟线 SCL,并选择参与事务的目标设备。请注意,它最初被称为“主设备”(Master),在许多解释中仍然沿用此称呼……