I2C(Inter-Integrated Circuit)是一种两线串行总线协议。
# ============ 接线 ============
# SCL → Pico GP9(I2C0 SCL)
# SDA → Pico GP8(I2C0 SDA)
# =============================
from machine import I2C, Pin
# 创建 I2C 对象
i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=400000) # 400kHz
# 扫描总线上的设备
devices = i2c.scan()
print('I2C 设备地址:', devices) # 打印十进制地址列表
# 写入数据(向设备 0x27 写入数据)
i2c.writeto(0x27, b'\x00\x01\x02') # 设备地址, 数据
# 读取数据(从设备 0x27 读取 5 字节)
data = i2c.readfrom(0x27, 5)
print('读取到:', data)
# 写入后读取
i2c.writeto(0x27, b'\x00') # 先写入寄存器地址
data = i2c.readfrom(0x27, 2) # 再读取数据
# 完整配置示例
i2c = I2C(
0, # I2C 端口号
scl=Pin(9), # 时钟引脚
sda=Pin(8), # 数据引脚
freq=400000, # 时钟频率(Hz)
timeout=500000 # 超时时间(微秒)
)
def scan_i2c():
devices = i2c.scan()
if devices:
print(f'找到 {len(devices)} 个 I2C 设备:')
for addr in devices:
print(f' 0x{addr:02X}')
else:
print('未找到 I2C 设备')
scan_i2c()
# 向设备写入单字节
i2c.writeto(0x27, b'\x00') # 写入一个字节
# 向设备写入多字节
i2c.writeto(0x27, b'\x00\x01\x02') # 写入多个字节
# 写入寄存器
def write_register(device_addr, reg_addr, value):
"""写入单个寄存器"""
i2c.writeto(device_addr, bytes([reg_addr, value]))
# 写入多个寄存器
def write_registers(device_addr, reg_addr, values):
"""写入多个寄存器"""
data = bytes([reg_addr]) + bytes(values)
i2c.writeto(device_addr, data)
# 读取指定长度数据
data = i2c.readfrom(0x27, 4) # 读取 4 字节
# 读取寄存器
def read_register(device_addr, reg_addr, length=1):
"""读取寄存器"""
i2c.writeto(device_addr, bytes([reg_addr]))
return i2c.readfrom(device_addr, length)
# ============ 接线 ============
# LCD VCC → Pico VBUS(5V,背光更亮;接 3V3 也能用但对比度偏弱)
# LCD GND → Pico GND
# LCD SCL → Pico GP9(I2C0 SCL)
# LCD SDA → Pico GP8(I2C0 SDA)
# 运行前要把 lcd_api.py、pico_i2c_lcd.py 两个文件存进 Pico
# =============================
from machine import I2C, Pin
from lcd_api import LcdApi
from pico_i2c_lcd import I2cLcd
# 初始化 I2C0:SCL 接 GP9,SDA 接 GP8,400kHz
i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=400000)
# 先扫一遍总线,确认屏幕在线
print('扫描到的地址:', i2c.scan()) # 屏幕在的话会打印 39(0x27)或 63(0x3F)
# 初始化屏幕:地址 0x27,2 行,每行 16 列
lcd = I2cLcd(i2c, 0x27, 2, 16)
# 第一行从开头写
lcd.putstr('Hello Pico')
# 光标移到第 1 行第 0 列(行号从 0 开始数)
lcd.move_to(0, 1)
# 第二行写
lcd.putstr('I2C OK')
两个库文件的完整代码和存法,见执行器章的 LCD 显示屏。
# ============ 接线 ============
# 接线同示例1:VCC→VBUS,GND→GND,SCL→GP9,SDA→GP8
# 本例不用 lcd_api/pico_i2c_lcd,自己往转接板(PCF8574)写字节,看看库背后发了什么
# =============================
from machine import I2C, Pin
import time
# 初始化 I2C0,用同样的两根线
i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=400000)
# LCD1602 转接板地址:0x27 或 0x3F,扫不到就换另一个
ADDR = 0x27
# 转接板上那颗 PCF8574 有 8 个输出脚:
# 低 4 位是控制线 0x01=RS(0 发指令 / 1 发字符) 0x02=RW 0x04=E(锁存) 0x08=背光
# 高 4 位才是送给 LCD 的数据
def send_byte(byte):
i2c.writeto(ADDR, bytes([byte | 0x04])) # E 拉高
i2c.writeto(ADDR, bytes([byte])) # E 拉低,这一拍数据被 LCD 收下
def send_nibble(nibble, rs=0):
send_byte((nibble << 4) | 0x08 | rs) # 4 位数据挪到高 4 位,0x08 顺手点亮背光
def send_cmd(cmd):
send_nibble(cmd >> 4) # 先送高 4 位
send_nibble(cmd & 0x0F) # 再送低 4 位
time.sleep_ms(2) # 留点时间给 LCD 执行
def send_char(ch):
send_nibble(ord(ch) >> 4, rs=1) # rs=1:告诉 LCD 收到的是字符
send_nibble(ord(ch) & 0x0F, rs=1) # 低 4 位也是字符
time.sleep_ms(2)
# 上电初始化:连发三次 0x03 再发 0x02,LCD 才认定为 4 位模式
time.sleep_ms(50)
send_nibble(0x03)
time.sleep_ms(5)
send_nibble(0x03)
time.sleep_ms(1)
send_nibble(0x03)
time.sleep_ms(1)
send_nibble(0x02) # 切到 4 位模式
send_cmd(0x28) # 两行显示,5x8 点阵
send_cmd(0x0C) # 开显示,不显示光标
send_cmd(0x06) # 每写一个字光标右移一格
send_cmd(0x01) # 清屏
time.sleep_ms(5) # 清屏要等 4 毫秒以上
for ch in 'Hello Pico!': # 一个字符一个字符写上去
send_char(ch)
scan() 打印出来的是十进制 —— 0x27 就是 39,0x3F 就是 63