I2C 通信
本节介绍 I2C 通信协议的基本概念,并演示如何使用 MicroPython 的
machine.I2C类来扫描 I2C 设备以及驱动 OLED 显示屏。
I2C(Inter-Integrated Circuit),也称为 I²C 或 IIC,是一种广泛使用的两线制串行通信协议。I2C 协议允许设备间通过两根信号线进行通信,常用于连接传感器、显示器、存储器等外设。
I2C 具有以下特点:
-
两线通信:只需要 SDA(数据线)和 SCL(时钟线)两根信号线。
-
主从架构:支持多个主设备(控制器)和从设备(目标)在同一总线上。
信息I²C 规范的第七次修订 已将传统的 “主/从 (Master/Slave)” 术语更新为 “控制器/目标 (Controller/Target)”。为确保与现有代码和文档的兼容性,本教程可能会根据上下文混用这两种表述。
-
地址寻址:每个设备都有唯一的 7 位或 10 位地址。
-
同步通信:通过时钟线进行同步,数据传输更可靠。
I2C 总线包含以下信号线:
- SDA (Serial Data Line):串行数据线,用于传输数据
- SCL (Serial Clock Line):串行时钟线,由主设备提供时钟信号
实际硬件连接时所有 I2C 设备还需连接地线(GND),以保证电路共地。
I2C 协议规范要求 SDA 和 SCL 两条线路上必须有上拉电阻。这是因为 I2C 总线采用开漏(Open-Drain)电路结构,设备只能将信号线拉到低电平,而不能主动输出高电平。上拉电阻的作用就是在总线空闲时,将信号线拉回到高电平,确保通信正常。
添加外部上拉电阻的情况:
- 实际连线时(特别是外接模块或多板通信),建议在 SDA 和 SCL 各连接一个 4.7kΩ 上拉电阻至 3.3V,提升通信可靠性。
- 总线较长、设备较多或通信不稳定时,必须使用外部上拉电阻。
可以省略外部上拉电阻的情况:
- 许多 I2C 模块(如本教程使用的 微雪 1.5 寸 OLED 模块)内部已集成上拉电阻。使用此类模块时通常可直接连接,无需额外添加上拉电阻。
- ESP32 GPIO 支持内部弱上拉,简单应用中可能够用。但最佳做法仍是添加外部上拉电阻以确保通信稳定。
如不确定模块是否包含上拉电阻,建议查看模块原理图或数据手册。
1. ESP32 上的 I2C
在 MicroPython 中,I2C 功能通过 machine 模块中的 I2C 类(硬件 I2C)和 SoftI2C 类(软件 I2C)来实现。
- Hardware I2C (
machine.I2C): 使用 ESP32 芯片内部专用的 I2C 硬件控制器。它的速度更快,CPU 占用率更低。ESP32 的硬件 I2C 支持映射到任意 GPIO 引脚。 - Software I2C (
machine.SoftI2C): 通过软件模拟 I2C 时序(Bit-banging)。通常仅在硬件 I2C 资源不足时使用。
建议优先使用 Hardware I2C。由于 ESP32 支持引脚矩阵映射,通常不需要使用 Software I2C。
2. 示例 1:I2C 扫描器 (I2C Scanner)
连接新 I2C 模块时,获取其地址是首要步骤。很多模块并不标明地址,或地址可通过跳线更改。I2C 扫描仪程序可以快速检测并报告总线上所有设备的地址,是进行 I2C 开发与调试的重要工具。
2.1 搭建电路
需要使用的器件有:
- 微雪 1.5 寸 OLED 模块 * 1(也可以替换为其他 I2C 模块)
- 4.7kΩ 电阻 * 2(可选,若 I2C 模块内置上拉电阻可省略)
- 面包板 * 1
- 导线
- ESP32 开发板
按照下面接线图连接电路:
ESP32-S3-Zero 引脚图


电路图中的 4.7kΩ 上拉电阻是 I2C 的标准接法。由于本教程使用中的 OLED 模块已内置上拉电阻,不连接这两个电阻,电路依然可以正常工作。
| 开发板引脚 | OLED 模块 | 说明 |
|---|---|---|
| GPIO 1 | DIN(SDA) | I2C 数据线。按需外接 4.7kΩ 上拉电阻至 3.3V |
| GPIO 2 | CLK(SCL) | I2C 时钟线。按需外接 4.7kΩ 上拉电阻至 3.3V |
| 3.3V | VCC | 电源正极 |
| GND | GND | 电源负极 |
2.2 代码
from machine import Pin, I2C
# I2C 引脚定义
SDA_PIN = 2
SCL_PIN = 1
# 初始化 I2C
# id=0 使用第一个硬件 I2C 控制器
# scl=Pin(SCL_PIN), sda=Pin(SDA_PIN) 指定引脚
# freq=100000 设置 I2C 频率为 100kHz (标准模式)
i2c = I2C(0, scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=100000)
print("Scanning I2C bus...")
devices = i2c.scan() # 扫描总线上的设备
if len(devices) == 0:
print("No I2C devices found")
else:
print("I2C devices found:", len(devices))
for device in devices:
# 打印十进制和十六进制地址
print("Decimal address: ", device, " | Hex address: ", hex(device))
2.3 代码解析
-
I2C(0, scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=100000):0: 指定使用 I2C0 硬件控制器。scl,sda: 指定连接的 GPIO 引脚。freq: 设置通信频率,通常为 100000 (100kHz) 或 400000 (400kHz)。
-
i2c.scan():- 扫描 I2C 总线上所有可能的 7 位地址(0x08 到 0x77)。
- 返回一个包含所有响应设备地址的列表(List)。
-
hex(device):- 将十进制地址转换为十六进制字符串(例如
0x3d),这是 I2C 地址最常用的表示方式。
- 将十进制地址转换为十六进制字符串(例如
2.4 运行结果
运行代码后,REPL 将输出扫描到的设备地址。例如,对于微雪 1.5 寸 OLED 模块,通常会看到地址 0x3d。
Scanning I2C bus...
I2C devices found: 1
Decimal address: 61 | Hex address: 0x3d
3. 示例 2:驱动 OLED 显示屏 (SSD1327)
实际应用中,通常无需自行编写底层的 I2C 数据收发代码,而是直接使用针对特定硬件的库(由社区或者制造商提供)。
此代码示例依赖 ssd1327.py 驱动库。该库基于社区开发者 mcauser 的 micropython-ssd1327 项目。
3.1 准备驱动文件
MicroPython 固件通常不内置特定显示屏的驱动库。为了驱动使用 SSD1327 芯片的 OLED 屏幕,我们需要手动添加驱动文件。
将下载的库文件中的 ssd1327.py 上传到 ESP 设备中。
注意:这个文件必须保存在 ESP32 的文件系统中的根目录下。
3.2 代码
确保 ssd1327.py 已上传到开发板后,运行以下代码:
from machine import I2C, Pin
import ssd1327
# 引脚定义
SDA_PIN = 2
SCL_PIN = 1
I2C_ADDR = 0x3d
# 初始化 I2C
i2c = I2C(0, scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=400000)
# 初始化 OLED
oled = ssd1327.SSD1327_I2C(128, 128, i2c, I2C_ADDR)
# 清屏 (填充黑色)
oled.fill(0)
# 显示文字
# framebuf.text(s, x, y, c)
# c 是颜色值,对于 4-bit 灰度,范围是 0-15。15 为最亮,0 为全黑。
oled.text("Hello,", 10, 10, 15)
oled.text("MicroPython!", 10, 25, 8)
oled.text("ESP32", 10, 40, 1) # 亮度较低的文字
# 使用 framebuf 绘制图像
# 绘制矩形框
oled.framebuf.rect(0, 0, 128, 128, 15)
# 绘制圆或椭圆
oled.framebuf.ellipse(0, 0, 128, 128, 15)
# 刷新显示
oled.show()
3.3 代码解析
import ssd1327: 导入驱动库。oled = ssd1327.SSD1327_I2C(128, 128, i2c, 0x3d): 创建 OLED 对象。需要传入屏幕的宽、高、I2C 对象以及 I2C 地址。oled.fill(0): 清除屏幕缓冲区。0代表黑色。oled.text(...): 在缓冲区中写入文本。注意framebuf模块内置一种默认字体(8x8 像素)。- 第一个参数为要显示的文本内容。
- 第二个参数为文本的 x 坐标。
- 第三个参数为文本的 y 坐标。
- 第四个参数是颜色值。SSD1327 支持 16 级灰度(4-bit),因此可传入 0-15 之间的整数来控制亮度。15 为最亮,0 为全黑。
- 关于
framebuf模块:ssd1327驱动库基于 MicroPython 内置的framebuf(Frame Buffer,帧缓冲)模块构建。framebuf提供了一套标准的图形绘制 API,包括绘制文本、线条、矩形、圆形等基本图形。- 在代码中,
oled.framebuf.rect()和oled.framebuf.ellipse()等方法直接调用了framebuf的绘图功能。 framebuf支持多种颜色格式和绘图操作,是 MicroPython 中进行图形显示的标准工具。更多绘图方法和详细用法,请参考 MicroPython framebuf 官方文档。
oled.show(): 将缓冲区的数据发送到 OLED 屏幕,更新显示内容。在调用此方法之前,屏幕内容不会改变。
3.4 运行结果
OLED 屏幕将被点亮,显示 "Hello, MicroPython!" 和 "ESP32",周围有一个矩形边框。

4. 常见问题与注意事项
4.1 Hardware I2C vs Software I2C
注意: ESP32 的 I2C 引脚支持通过 GPIO 矩阵路由到任意可用引脚,因此通常情况下不需要使用 SoftI2C,直接使用 I2C (硬件 I2C) 即可。
from machine import Pin, SoftI2C
# SoftI2C 由 CPU 模拟
i2c = SoftI2C(scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=100000)
SoftI2C 的用法与 I2C 完全相同,但它是由 CPU 模拟的,因此在高速通信或高负载下可能不如硬件 I2C 稳定。
4.2 关于 I2C 从机模式
MicroPython 的标准 machine.I2C 和 machine.SoftI2C 类目前仅支持主机(控制器)模式,不支持从机(目标)模式。