4. Python的smbus2库入门:安装、扫描与单字节通信
好,咱们正式开始动手了。前面几章讲了I2C协议的理论,说实话,光看时序图确实有点枯燥。这一章我们直接上Python,用smbus2这个库来操作I2C设备。你想想看,能通过几行代码就点亮LCD背光、控制字符显示,是不是比干看波形图有意思多了?
4.1 为什么选smbus2?
Python里操作I2C的库有好几个,比如smbus、smbus2、pyftdi等等。我个人习惯用smbus2,原因很简单:
- 兼容性好:它完全兼容
smbus的API,老代码基本不用改。 - 性能更优:底层用C实现,读写速度比纯Python实现快不少。
- 功能完整:支持SMBus协议的所有操作,包括块读写、I2C读写等。
- 文档清晰:GitHub上的README写得明明白白,遇到问题好查。
我在一个工业项目里用过smbus,当时遇到一个奇怪的问题——连续读写时偶尔会丢数据。换成smbus2之后,问题就再没出现过。后来看了源码才发现,smbus2在锁机制上做了优化。嗯,细节决定成败。
4.2 安装smbus2库
安装过程非常简单,一行命令搞定。前提是你的树莓派(或其它Linux开发板)已经连上了网络。
pip install smbus2
如果你用的是Python 3(现在基本都是了),可能需要用pip3:
pip3 install smbus2
sudo raspi-config,在"Interface Options"里启用I2C。不然后面扫描设备会什么都扫不到,别问我怎么知道的……我曾经在这上面浪费了半小时。
安装完成后,验证一下:
python3 -c "import smbus2; print(smbus2.__version__)"
如果能正常输出版本号,比如0.4.2,那就说明安装成功了。
4.3 扫描I2C设备地址
拿到一个新的I2C模块,第一件事是什么?我个人习惯先扫一下它的地址。你想想看,如果地址都搞错了,后面所有的读写操作都是白费功夫。
下面这个脚本会扫描0x03到0x77之间的所有地址,看看哪些设备在线:
import smbus2
# 创建SMBus实例,参数1表示I2C总线编号(树莓派通常是1)
bus = smbus2.SMBus(1)
print("正在扫描I2C设备...")
print(" 0 1 2 3 4 5 6 7 8 9 a b c d e f")
for address in range(0x03, 0x78):
try:
# 尝试读取一个字节,看设备是否有响应
bus.read_byte(address)
# 格式化输出地址
row = address // 16 * 16
col = address % 16
if col == 0:
print(f"{row:02x}: ", end="")
print(f"{address:02x} ", end="")
except OSError:
# 设备无响应,打印"--"
row = address // 16 * 16
col = address % 16
if col == 0:
print(f"{row:02x}: ", end="")
print("-- ", end="")
if (address + 1) % 16 == 0:
print()
bus.close()
print("\n扫描完成。")
运行结果大概长这样:
正在扫描I2C设备...
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
如果某个地址有设备响应,就会显示对应的十六进制地址。比如常见的LCD1602 I2C转接板,地址通常是0x27或0x3F。
4.4 发送单字节数据
找到设备地址之后,下一步就是跟它通信了。最简单的操作就是发送一个字节。比如你想控制LCD的背光,可能只需要往某个寄存器写一个字节。
发送单字节数据有两种常用方式:
| 方法 | 说明 | 适用场景 |
|---|---|---|
write_byte(addr, val) |
向设备地址直接发送一个字节 | 设备只有一个寄存器,或者不需要指定寄存器地址 |
write_byte_data(addr, cmd, val) |
先发送命令字节(寄存器地址),再发送数据字节 | 设备有多个寄存器,需要指定操作哪个寄存器 |
来看个实际例子。假设你的LCD地址是0x27,你想打开背光(背光控制位通常是第3位,即0x08):
import smbus2
# 设备地址
LCD_ADDR = 0x27
# 背光控制位
LCD_BACKLIGHT = 0x08
# 创建总线实例
bus = smbus2.SMBus(1)
try:
# 发送背光控制字节
bus.write_byte(LCD_ADDR, LCD_BACKLIGHT)
print(f"已向地址 0x{LCD_ADDR:02X} 发送背光控制字节 0x{LCD_BACKLIGHT:02X}")
except OSError as e:
print(f"通信失败:{e}")
finally:
bus.close()
如果LCD支持寄存器操作,你可能需要这样写:
# 假设寄存器地址是0x00,数据是0x08
bus.write_byte_data(LCD_ADDR, 0x00, LCD_BACKLIGHT)
4.5 本章知识结构
下面这张图把本章的核心逻辑串起来了,从安装库到实际通信,每一步都环环相扣:
4.6 常见问题与避坑指南
做I2C通信,有些坑是绕不开的。我把自己踩过的坑整理了一下,你遇到了可以直接对照着查:
- 地址搞错:有些模块的地址是7位,但smbus2要求传入8位地址(左移一位)。不过smbus2内部会自动处理,你直接填数据手册上的地址就行。但要注意,有些库不帮你做这个转换。
- 总线编号不对:树莓派一般是
/dev/i2c-1,对应SMBus(1)。老版本树莓派可能是/dev/i2c-0。用ls /dev/i2c-*确认一下。 - 权限问题:运行脚本时如果报
Permission denied,把用户加到i2c组:sudo usermod -a -G i2c pi,然后重新登录。 - 设备没上电:这个听起来很蠢,但我真犯过。LCD模块需要5V供电,如果只接了I2C线没接电源,扫描结果就是空的。
4.7 小结
这一章我们走通了smbus2库的完整流程:安装、扫描地址、发送单字节数据。说白了,就是让Python和I2C设备能对上话。下一章我们会用这些基础操作,去控制LCD1602的背光和显示。到时候你会发现,今天学的这些就是所有高级功能的地基。
嗯,动手试试吧。把代码敲一遍,看看你的LCD模块能不能被扫到。如果扫到了,恭喜你,硬件连接没问题,可以继续往下走了。