跳到主要内容

I2C 通信

本节介绍 I2C 通信协议的基本概念,并演示如何使用 MicroPython 的 machine.I2C 类来扫描 I2C 设备以及驱动 OLED 显示屏。

I2C(Inter-Integrated Circuit),也称为 I²CIIC,是一种广泛使用的两线制串行通信协议。I2C 协议允许设备间通过两根信号线进行通信,常用于连接传感器、显示器、存储器等外设。

I2C 具有以下特点:

  • 两线通信:只需要 SDA(数据线)和 SCL(时钟线)两根信号线。

  • 主从架构:支持多个主设备(控制器)和从设备(目标)在同一总线上。

    信息

    I²C 规范的第七次修订 已将传统的 “主/从 (Master/Slave)” 术语更新为 “控制器/目标 (Controller/Target)”。为确保与现有代码和文档的兼容性,本教程可能会根据上下文混用这两种表述。

  • 地址寻址:每个设备都有唯一的 7 位或 10 位地址。

  • 同步通信:通过时钟线进行同步,数据传输更可靠。


I2C 总线包含以下信号线:

  • SDA (Serial Data Line):串行数据线,用于传输数据
  • SCL (Serial Clock Line):串行时钟线,由主设备提供时钟信号

实际硬件连接时所有 I2C 设备还需连接地线(GND),以保证电路共地

I2C 连接

关于上拉电阻

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 引脚图

ESP32-S3-Zero-Pinout

接线图
电路图说明

电路图中的 4.7kΩ 上拉电阻是 I2C 的标准接法。由于本教程使用中的 OLED 模块已内置上拉电阻,不连接这两个电阻,电路依然可以正常工作。


开发板引脚OLED 模块说明
GPIO 1DIN(SDA)I2C 数据线。按需外接 4.7kΩ 上拉电阻至 3.3V
GPIO 2CLK(SCL)I2C 时钟线。按需外接 4.7kΩ 上拉电阻至 3.3V
3.3VVCC电源正极
GNDGND电源负极

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 代码解析

  1. I2C(0, scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=100000):

    • 0: 指定使用 I2C0 硬件控制器。
    • scl, sda: 指定连接的 GPIO 引脚。
    • freq: 设置通信频率,通常为 100000 (100kHz) 或 400000 (400kHz)。
  2. i2c.scan():

    • 扫描 I2C 总线上所有可能的 7 位地址(0x08 到 0x77)。
    • 返回一个包含所有响应设备地址的列表(List)。
  3. 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 项目。

下载链接:micropython-ssd1327-master.zip

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 代码解析

  1. import ssd1327: 导入驱动库。
  2. oled = ssd1327.SSD1327_I2C(128, 128, i2c, 0x3d): 创建 OLED 对象。需要传入屏幕的宽、高、I2C 对象以及 I2C 地址。
  3. oled.fill(0): 清除屏幕缓冲区。0 代表黑色。
  4. oled.text(...): 在缓冲区中写入文本。注意 framebuf 模块内置一种默认字体(8x8 像素)。
    • 第一个参数为要显示的文本内容。
    • 第二个参数为文本的 x 坐标。
    • 第三个参数为文本的 y 坐标。
    • 第四个参数是颜色值。SSD1327 支持 16 级灰度(4-bit),因此可传入 0-15 之间的整数来控制亮度。15 为最亮,0 为全黑。
  5. 关于 framebuf 模块:
    • ssd1327 驱动库基于 MicroPython 内置的 framebuf(Frame Buffer,帧缓冲)模块构建。framebuf 提供了一套标准的图形绘制 API,包括绘制文本、线条、矩形、圆形等基本图形。
    • 在代码中,oled.framebuf.rect()oled.framebuf.ellipse() 等方法直接调用了 framebuf 的绘图功能。
    • framebuf 支持多种颜色格式和绘图操作,是 MicroPython 中进行图形显示的标准工具。更多绘图方法和详细用法,请参考 MicroPython framebuf 官方文档
  6. oled.show(): 将缓冲区的数据发送到 OLED 屏幕,更新显示内容。在调用此方法之前,屏幕内容不会改变。

3.4 运行结果

OLED 屏幕将被点亮,显示 "Hello, MicroPython!" 和 "ESP32",周围有一个矩形边框。

示例 2 运行结果

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.I2Cmachine.SoftI2C 类目前仅支持主机(控制器)模式,不支持从机(目标)模式。

5. 相关链接