import sys
import signal
import os
import time

# 导入i2cdev
from i2cdev import I2C

def signal_handler(signal, frame):
    sys.exit(0)

def i2cdevTest():
    # device, bus = 0x51, 0
    bus = input("Please input I2C BUS num:")
    os.system('i2cdetect -y -r ' + bus)
    device = input("Please input I2C device num(Hex):")
    print("Read data from device %s on I2C bus %s" % (device, bus))

    # 创建I2C设备
    i2c = I2C(eval("0x" + device), int(bus))

    # 读取数据
    value = i2c.read(1)
    i2c.write(value)
    print("read value=", value)
    i2c.close()

if __name__ == '__main__':
    signal.signal(signal.SIGINT, signal_handler)
    print("Starting demo now! Press CTRL+C to exit")
    print("List of enabled I2C controllers:")
    os.system('ls /dev/i2c*')
    while True:
        i2cdevTest()

RDK Python i2cdev I2C 读写测试代码完整解读

一、整体功能概述

该脚本基于地平线封装的 i2cdev 库,实现交互式 I2C 设备单字节读写测试:

  1. 列出系统所有 /dev/i2c-x 总线;
  2. 交互式输入 I2C 总线号、从设备十六进制地址;
  3. 调用 i2cdetect 扫描总线上挂载的 I2C 从机;
  4. 读取从设备 1 个字节数据,再把读到的字节原样写回从设备;
  5. 循环反复执行测试,支持 Ctrl+C 退出。

运行环境:RDK X3/X5;依赖自研 i2cdev 库,需 root/dialout 权限运行。

二、逐段代码分层解析

1. 头部导入与中断信号处理

python

运行

#!/usr/bin/env python3
import sys
import signal
import os
import time
# 地平线专用I2C用户态操作库
from i2cdev import I2C

# 捕获Ctrl+C,直接退出程序
def signal_handler(signal, frame):
    sys.exit(0)
  • signal:注册 SIGINT 键盘中断,按下 Ctrl+C 直接退出死循环;
  • i2cdev.I2C:封装底层 /dev/i2cX 字符设备,提供读写、关闭接口;
  • os:调用系统 shell 工具 i2cdetect 扫描 I2C 设备。

2. 核心业务函数 i2cdevTest ()

python

运行

def i2cdevTest():
    # 1. 输入I2C总线号(数字,如0、1、2)
    bus = input("Please input I2C BUS num:")
    # 调用系统命令扫描该总线所有I2C从设备地址
    os.system('i2cdetect -y -r ' + bus)
    # 输入从设备十六进制地址,例如 51、38
    device = input("Please input I2C device num(Hex):")
    print("Read data from device %s on I2C bus %s" % (device, bus))
    
    # 字符串十六进制转数字:"51" → eval("0x51") = 81
    dev_addr = eval("0x" + device)

    # 实例化I2C对象:从设备地址、总线编号
    i2c = I2C(dev_addr, int(bus))
    
    # 读取1个字节,返回字节数值(0~255)
    value = i2c.read(1)
    # 将读取到的单字节写回从设备
    i2c.write(value)
    print("read value=", value)
    # 关闭I2C设备句柄,释放总线资源
    i2c.close()
关键 API 与逻辑拆解
  1. os.system('i2cdetect -y -r x')
    • -y:自动确认交互,无需手动回车;
    • -r:读扫描,探测总线上在线 I2C 从机;
    • 作用:可视化查看当前总线存在哪些 I2C 设备,防止地址输入错误。
  2. eval("0x" + device) 用户输入字符串如51,拼接成0x51通过 eval 转为十进制整数 81,即 I2C7 位从设备地址。
  3. I2C(dev_addr, bus_num) 打开 /dev/i2c{bus_num} 总线,绑定目标从设备地址。
  4. i2c.read(1) 参数为读取字节长度,此处读取1 个字节,返回 0~255 整型数据。
  5. i2c.write(value) 写入单字节数值到从设备。
  6. i2c.close() 关闭 I2C 文件句柄,释放设备占用。

3. 程序入口主逻辑

python

运行

if __name__ == '__main__':
    signal.signal(signal.SIGINT, signal_handler)
    print("Starting demo now! Press CTRL+C to exit")
    # 打印系统全部I2C总线节点 /dev/i2c-0 /dev/i2c-1...
    os.system('ls /dev/i2c*')
    # 无限循环,反复执行I2C读写测试
    while True:
        i2cdevTest()
  1. 提前注册 Ctrl+C 中断回调;
  2. 启动先打印所有可用 I2C 总线;
  3. while True 死循环:一次测试完成后,再次等待输入总线、地址,重复读写。

三、完整运行流程示例

  1. 运行脚本:sudo python3 i2c_test.py
  2. 打印所有 /dev/i2c-x 总线;
  3. 循环进入测试函数:
    • 输入总线号:如 1
    • 执行 i2cdetect -y -r 1,打印总线挂载的 I2C 地址表格;
    • 输入设备十六进制地址:如 51
    • 打开 I2C 总线 1,从机地址 0x51;
    • 读取 1 字节数据,打印数值;
    • 把读到的字节写回 0x51 设备;
    • 关闭 I2C;
  4. 立刻回到循环开头,等待下一次输入;
  5. 按下 Ctrl+C,触发 signal_handler 直接退出程序。

四、代码存在缺陷与风险

  1. 无任何异常捕获
    • 总线不存在、设备地址无应答、输入非数字、非法十六进制字符,程序直接崩溃;
    • I2C 读写失败无重试、无错误提示。
  2. eval 存在安全风险 用户恶意输入字符会触发代码执行漏洞,生产代码应改用 int(device, 16) 替代 eval:

    python

    运行

    dev_addr = int(device, 16)
    
  3. 死循环无退出机制 只能靠 Ctrl+C 强制终止,无法通过输入命令正常跳出循环。
  4. 缺少资源容错 若 I2C 读写中途异常抛出,i2c.close() 不会执行,会持续占用 I2C 总线句柄。
  5. 仅支持单字节读写,无法指定寄存器地址 常规 I2C 器件需要先发送寄存器地址再读写,本代码仅裸读写从设备,大部分传感器无法正常使用。
  6. 无权限判断 普通用户运行会报打开 /dev/i2c-x 权限失败,必须 sudo。

五、RDK I2C 硬件配套说明

  1. RDK X5 有多路硬件 I2C 控制器,对应节点 /dev/i2c-0/dev/i2c-1/dev/i2c-2
  2. 每路 I2C 信号:SDA(数据线)、SCL(时钟线),3.3V 电平,外部需 4.7K 上拉电阻;
  3. 一条总线可挂载多个不同地址 I2C 从机(EEPROM、OLED、陀螺仪、温感芯片);
  4. 扫描总线命令独立使用:

    bash

    运行

    sudo i2cdetect -y -r 1
    

六、优化修复片段(替换危险 eval、增加异常捕获)

python

运行

def i2cdevTest():
    try:
        bus = input("Please input I2C BUS num:")
        os.system('i2cdetect -y -r ' + bus)
        device = input("Please input I2C device num(Hex):")
        # 安全转换十六进制,替代eval
        dev_addr = int(device, 16)
        bus_num = int(bus)
        print("Read data from device 0x%02X on I2C bus %d" % (dev_addr, bus_num))
        i2c = I2C(dev_addr, bus_num)
        value = i2c.read(1)
        i2c.write(value)
        print("read value=", value)
        i2c.close()
    except ValueError:
        print("输入格式错误,请输入合法数字/十六进制地址")
    except Exception as e:
        print("I2C操作失败:", e)

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐