如何在Python中事先读取I2C消息的长度和头部

编程语言 2026-07-11

我有一个工具,用来与我用Python实现的I2C从设备进行交互。这个从设备不是一台PC,而是一个自定义设备(不过我无法获得编辑该设备的软件的权限)。目前,我被迫在每条I2C消息中读取任意数量的字节。我需要能够最多读取4000字节,因此如果对所有消息都设为4000,就会造成过大的延迟。

我需要能够在读取完整消息之前读取I2C头中的长度字节,以便确定要读取多少字节。问题在于,一旦读取,I2C邮箱就会被清空,因此我不能简单地把读取代码写成两段——一次用于头部,一次用于完整的消息及其载荷。

下面是我的I2C读取函数,在需要时会被调用:

def I2cRead(addr, ReadLength,split=0):
    MsgSplit = int(split)

    readLength = int(ReadLength)
    readSplit = []
    if split == 0:
        ## only need 1 block
        readSplit.append(readLength)
    else:    
        ## number of blocks
        blocks = math.floor(readLength / MsgSplit)
        readSplit += [MsgSplit] * blocks
        ## Add remainder if not zero
        if (readLength % MsgSplit) != 0:
            readSplit.append(readLength % MsgSplit)

    #print(readSplit)

    i2c_msgs = []
    msg_buffers = []

    # Create I2C message
    data = [0x01]
    msg = i2c_msg()
    msg.addr = addr
    msg.flags = I2C_M_WR
    msg.len = 1
    data_bytes = (ctypes.c_ubyte * 1)(*data)
    msg.buf = ctypes.cast(data_bytes, ctypes.c_void_p)

    i2c_msgs.append(msg)

    ## Print out length of msgs
    for i,element in enumerate(readSplit):
        # Create I2C message
        msg = i2c_msg()
        msg.addr = addr
        msg.flags = I2C_M_RD
        msg.len = element ## length of read msg

        ## Create buffer in memory
        data_bytes = (ctypes.c_ubyte * element)()
        msg_buffers.append(data_bytes)

        ## point to buffer
        msg.buf = ctypes.cast(data_bytes, ctypes.c_void_p)

        ## append to i2c msg
        i2c_msgs.append(msg)



    MsgArrayType = i2c_msg * len(i2c_msgs)
    msgs_array = MsgArrayType(*i2c_msgs)

    # Wrap in ioctl_data
    ioctl_data = i2c_rdwr_ioctl_data()
    ioctl_data.msgs = ctypes.cast(msgs_array,ctypes.POINTER(i2c_msg))
    ioctl_data.nmsgs = len(i2c_msgs)

    # Open I2C device and send
    with open("/dev/i2c-1", "rb+", buffering=0) as f:
        fcntl.ioctl(f, I2C_RDWR, ioctl_data)

    CollectedBuffer = []
    for i,element in enumerate(msg_buffers):
        ## loop through msgs
        buffer = []
        for i in range(len(element)):
            buffer.append(element[i])
        #print(buffer)
        CollectedBuffer.append(buffer)

    return CollectedBuffer[0]

解决方案

要点是:使用现有的Python I2C库,例如 smbus2,并查阅设备的文档,了解在单次I2C读取操作允许的32字节之外,如何精确地读取一段数据。


问题在于,一旦I2C邮箱被读取就会被清空,因此我不能简单地把我的读取代码写成两段——一次用于头部,一次用于完整的消息和载荷。

这听起来有点蹊跷。如果你是这么描述,设备要么在读取之前就删除了数据——这显然不是情况——要么它有某种方式来判断你已经处理了哪些数据、哪些没处理——这也是不可能的,因为它所做的只是把数据发送到线上,并没有办法知道接下来会发生什么。

根据 smbus2.SMBus.read_block_data 文档,I2C协议一次读取一个设备寄存器最多只能达到32字节。因此,设备中必然存在某种机制,允许你在不丢弃你将要读取的数据的情况下读取更多字节。

我的猜测是,你可能先读取前面的(最多)32字节,提取长度字段,然后你就知道还需要执行多少次读取。也许你还需要在设备中设置某个其他的“锁定”标志,以表示你正在读取多块数据的过程。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章