第九章 I2C协议-K230多设备互联的简洁总线

🎯 本章目标
深入理解 I2C(Inter-Integrated Circuit)通信协议原理,掌握 K230 MicroPython 中 I2C 模块的配置与使用方法,学会驱动 OLED 屏幕、AHT20 温湿度传感器、EEPROM 存储器 等常见外设,构建具备人机交互与数据存储能力的完整嵌入式系统。


1. I2C 协议基础概念

1.1 什么是 I2C?

I2C(Inter-Integrated Circuit) 是由 Philips(现 NXP)开发的一种两线式串行总线,用于连接低速外围设备。

  • 仅需两根线
    • SDA:数据线(Serial Data)
    • SCL:时钟线(Serial Clock)
  • 支持多主多从架构
  • 地址寻址:每个从设备有唯一 7 位或 10 位地址
  • 典型速率:100kHz(标准)、400kHz(快速)、1MHz(高速)

优点

  • 接线简单,节省引脚
  • 支持总线扩展(挂载多个设备)
  • 广泛用于传感器、存储器、显示屏

⚠️ 缺点

  • 速率较低
  • 需要上拉电阻(通常 4.7kΩ)
  • 总线长度受限(建议 < 1m)

2. I2C 通信机制详解

2.1 信号线电气特性

  • 开漏输出:SDA 和 SCL 均为开漏结构
  • 必须外接上拉电阻到 VCC(3.3V)
  • 典型上拉电阻值:4.7kΩ

📌 开发板说明
多数开发板已在 I2C 引脚内置上拉电阻,无需外接。


2.2 数据帧格式

I2C 通信由以下部分组成:

[起始] [从机地址+R/W] [ACK] [数据字节...] [ACK] ... [停止]
  • 起始条件(START):SCL 高电平时,SDA 从高→低
  • 停止条件(STOP):SCL 高电平时,SDA 从低→高
  • ACK:每传输一个字节后,接收方拉低 SDA 表示应答

2.3 7 位地址与读写位

  • 7 位地址 + 1 位读写控制 → 8 位字节
  • 读操作:地址 << 1 | 1
  • 写操作:地址 << 1 | 0

📊 常见设备地址(7 位):

  • OLED(SSD1306):0x3C
  • AHT20:0x38
  • AT24C02(EEPROM):0x50
  • BMP280:0x76

3. K230 I2C 系统架构

3.1 硬件资源

K230 提供多个 I2C 控制器(如 I2C0, I2C1),每个:

  • 支持主/从模式(MicroPython 通常只用主模式)
  • 支持标准/快速模式(100/400kHz)
  • 需通过 FPIOA 映射到物理引脚

3.2 FPIOA 映射

功能名 FPIOA 编号 说明
I2C0_SDA 32 I2C0 数据线
I2C0_SCL 33 I2C0 时钟线
I2C1_SDA 34 I2C1 数据线
I2C1_SCL 35 I2C1 时钟线

📌 建议:使用 I2C1,避免与调试接口冲突


4. MicroPython I2C API 详解

4.1 导入模块

from machine import I2C, Pin, fpioa

或使用 MaixPy 风格:

from fpioa_manager import fm
from machine import I2C

4.2 初始化流程

4.2.1 步骤1:FPIOA 映射
fm.register(sda_pin, fm.fpioa.I2C1_SDA)
fm.register(scl_pin, fm.fpioa.I2C1_SCL)
4.2.2 步骤2:创建 I2C 对象
i2c = I2C(I2C.I2C1, freq=400_000, sda=Pin(sda_pin), scl=Pin(scl_pin))

参数说明:

  • freq:通信频率,通常 100000400000
  • sda, scl:指定引脚对象

4.3 常用方法

方法 说明
i2c.scan() 扫描总线上所有从设备,返回地址列表
i2c.readfrom(addr, nbytes) 从指定地址读取 nbytes 字节
i2c.writeto(addr, buffer) 向指定地址写入数据
i2c.readfrom_mem(addr, memaddr, nbytes) 从设备的内存地址读取
i2c.writeto_mem(addr, memaddr, buffer) 向设备的内存地址写入

5. 实战项目一:I2C 设备扫描

from fpioa_manager import fm
from machine import I2C, Pin

# === 1. FPIOA 映射 ===
sda_pin = 2  # 示例引脚
scl_pin = 3
fm.register(sda_pin, fm.fpioa.I2C1_SDA)
fm.register(scl_pin, fm.fpioa.I2C1_SCL)

# === 2. 初始化 I2C ===
i2c = I2C(I2C.I2C1, freq=400_000,
          sda=Pin(sda_pin),
          scl=Pin(scl_pin))

# === 3. 扫描设备 ===
devices = i2c.scan()
if devices:
    print(f"发现 {len(devices)} 个 I2C 设备:")
    for addr in devices:
        print(f"  地址: 0x{addr:02X}")
else:
    print("❌ 未发现 I2C 设备,请检查接线")

用途:快速确认设备是否连接成功。


6. 实战项目二:驱动 AHT20 温湿度传感器

6.1 AHT20 通信流程

  1. 发送初始化命令 0xBE, 0x08, 0x00
  2. 发送触发测量命令 0xAC, 0x33, 0x00
  3. 等待 80ms
  4. 读取 6 字节数据
  5. 解析温湿度

6.2 完整代码

from fpioa_manager import fm
from machine import I2C, Pin
import utime

# === 1. FPIOA 映射 ===
fm.register(2, fm.fpioa.I2C1_SDA)
fm.register(3, fm.fpioa.I2C1_SCL)

i2c = I2C(I2C.I2C1, freq=400_000,
          sda=Pin(2),
          scl=Pin(3))

AHT20_ADDR = 0x38

def aht20_init():
    """初始化 AHT20"""
    i2c.writeto(AHT20_ADDR, b'\xBE\x08\x00')
    utime.sleep_ms(10)

def aht20_trigger():
    """触发一次测量"""
    i2c.writeto(AHT20_ADDR, b'\xAC\x33\x00')
    utime.sleep_ms(80)  # 等待转换完成

def aht20_read():
    """读取数据并解析"""
    data = i2c.readfrom(AHT20_ADDR, 6)
    if len(data) != 6:
        return None, None

    # 解析
    humidity = ((data[1] << 12) | (data[2] << 4) | (data[3] >> 4)) * 100 / 2**20
    temperature = (((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]) * 200 / 2**20 - 50

    return humidity, temperature

# === 主循环 ===
aht20_init()
print("AHT20 温湿度读取")

try:
    while True:
        aht20_trigger()
        h, t = aht20_read()
        if h is not None and t is not None:
            print(f"湿度: {h:.1f}%  温度: {t:.1f}°C")
        else:
            print("读取失败")
        utime.sleep(2)

except KeyboardInterrupt:
    pass

7. 实战项目三:驱动 SSD1306 OLED 屏幕

7.1 使用 ssd1306 官方库

# 通过 rshell 上传 ssd1306.py
rshell -p /dev/ttyUSB0
cp ssd1306.py /pyboard/

ssd1306.py 可从 MicroPython 官方仓库获取:
https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py


7.2 完整代码

from fpioa_manager import fm
from machine import I2C, Pin
import ssd1306
import utime

# === 1. I2C 配置 ===
fm.register(2, fm.fpioa.I2C1_SDA)
fm.register(3, fm.fpioa.I2C1_SCL)

i2c = I2C(I2C.I2C1, freq=400_000, sda=Pin(2), scl=Pin(3))
oled = ssd1306.SSD1306_I2C(128, 64, i2c, addr=0x3C)

# === 2. 清屏 ===
oled.fill(0)

# === 3. 显示文本 ===
oled.text("Hello K230", 0, 0)
oled.text("I2C OLED", 0, 10)
oled.show()

# === 4. 动态显示时间 ===
counter = 0
while True:
    oled.fill(0)
    oled.text("Counter:", 0, 20)
    oled.text(str(counter), 70, 20)
    oled.text("Time:", 0, 40)
    oled.text(utime.localtime(), 40, 40)
    oled.show()
    counter += 1
    utime.sleep(1)

8. 实战项目四:AT24C02 EEPROM 数据存储

8.1 AT24C02 特性

  • I2C 接口,地址 0x50
  • 容量 256 字节
  • 支持 writeto_mem / readfrom_mem

8.2 完整代码

from fpioa_manager import fm
from machine import I2C, Pin

fm.register(2, fm.fpioa.I2C1_SDA)
fm.register(3, fm.fpioa.I2C1_SCL)

i2c = I2C(I2C.I2C1, freq=100_000, sda=Pin(2), scl=Pin(3))
EEPROM_ADDR = 0x50

def eeprom_write_byte(addr, data):
    i2c.writeto_mem(EEPROM_ADDR, addr, bytes([data]))

def eeprom_read_byte(addr):
    return i2c.readfrom_mem(EEPROM_ADDR, addr, 1)[0]

# === 测试读写 ===
test_addr = 0x00
eeprom_write_byte(test_addr, 0x55)
utime.sleep_ms(10)  # 写入需要时间
value = eeprom_read_byte(test_addr)
print(f"EEPROM 地址 0x{test_addr:02X} 读回: 0x{value:02X}")

9. 高级技巧与优化

9.1 多设备共用总线

  • 多个设备共享 SDA/SCL
  • 通过地址区分
  • 注意地址冲突(如多个 OLED)

9.2 软件模拟 I2C(bit-banging)

当硬件 I2C 不可用时:

from machine import Pin
import utime

def i2c_write_bit(scl, sda, bit):
    scl.value(0)
    sda.value(bit)
    utime.sleep_us(5)
    scl.value(1)
    utime.sleep_us(5)
    scl.value(0)

# 适用于调试、引脚受限场景

10. 常见问题与调试

❌ 问题1:scan() 无设备

排查

  • 接线是否正确(SDA/SCL 是否交叉)
  • 上拉电阻是否缺失
  • 设备地址是否正确
  • 供电是否正常(3.3V)

❌ 问题2:读写超时或异常

解决

  • 降低 freq 到 100kHz
  • 检查设备是否需要初始化
  • 增加 sleep_ms 等待转换完成

❌ 问题3:OLED 无显示

检查

  • 地址是 0x3C 还是 0x3D
  • 是否调用 oled.show()
  • 是否 fill(0) 清屏?

11. 性能与限制

参数 K230 MicroPython 限制
最高速率 400kHz(快速模式)
最大设备数 10+(地址不冲突)
总线电容 建议 < 400pF
支持模式 主模式(Master)

💡 建议

  • 总线设备多时,使用 I2C 集线器(TCA9548A)
  • 关键操作加 try-except 防止程序卡死

本章你已掌握

  • I2C 协议原理与帧结构
  • K230 I2C + FPIOA 配置
  • 设备扫描与地址识别
  • 驱动 AHT20、OLED、EEPROM
  • 常见问题排查

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐