class I2CTarget —— 一个 I2C 目标设备

I2C 目标(target)是一种连接到 I2C 总线并受 I2C 控制器控制的设备。I2C 目标可以有多种形式。machine.I2CTarget 类实现了一个 I2C 目标,它可以配置为内存/寄存器设备,或者通过回调(如果移植版本支持)配置为任意 I2C 设备。

适用于 OpenMV Cam M4 / M7 / H7 / H7 Plus / Pure Thermal / RT1062 / AE3。

内存设备情形的用法示例:

from machine import I2CTarget

# Create the backing memory for the I2C target.
mem = bytearray(8)

# Create an I2C target.  Depending on the port, extra parameters
# may be required to select the peripheral and/or pins to use.
i2c = I2CTarget(addr=67, mem=mem)

# At this point an I2C controller can read and write `mem`.
...

# Deinitialise the I2C target.
i2c.deinit()

请注意,某些移植版本要求向 I2CTarget 构造函数传入 id,以及可能还有 sclsda 引脚,以选择其所连接的硬件 I2C 实例和引脚。

当配置为内存设备时,还可以注册以接收事件。例如,在内存被读取/写入时收到通知:

from machine import I2CTarget

# Define an IRQ handler, for I2C events.
def irq_handler(i2c_target):
    flags = i2c_target.irq().flags()
    if flags & I2CTarget.IRQ_END_READ:
        print("controller read target at addr", i2c_target.memaddr)
    if flags & I2CTarget.IRQ_END_WRITE:
        print("controller wrote target at addr", i2c_target.memaddr)

# Create the I2C target and register to receive default events.
mem = bytearray(8)
i2c = I2CTarget(addr=67, mem=mem)
i2c.irq(irq_handler)

更复杂的 I2C 设备可以使用完整的事件集来实现。例如,查看原始事件在被触发时的情形:

from machine import I2CTarget

# Define an IRQ handler that prints the event id and responds to reads/writes.
def irq_handler(i2c_target, buf=bytearray(1)):
    flags = i2c_target.irq().flags()
    print(flags)
    if flags & I2CTarget.IRQ_READ_REQ:
        i2c_target.write(buf)
    if flags & I2CTarget.IRQ_WRITE_REQ:
        i2c_target.readinto(buf)

# Create the I2C target and register to receive all events.
i2c = I2CTarget(addr=67)
all_triggers = (
    I2CTarget.IRQ_ADDR_MATCH_READ
    | I2CTarget.IRQ_ADDR_MATCH_WRITE
    | I2CTarget.IRQ_READ_REQ
    | I2CTarget.IRQ_WRITE_REQ
    | I2CTarget.IRQ_END_READ
    | I2CTarget.IRQ_END_WRITE
)
i2c.irq(irq_handler, trigger=all_triggers, hard=True)

构造函数

class machine.I2CTarget(id: int, addr: int, *, addrsize: int = 7, mem: bytearray | None = None, mem_addrsize: int = 8, scl: Pin | None = None, sda: Pin | None = None)

使用以下参数构造并返回一个新的 I2CTarget 对象:

  • id 标识特定的 I2C 外设。允许的取值取决于具体的移植版本/开发板。某些移植版本有默认值,在这种情况下可省略此参数。

  • addr 是目标的 I2C 地址。

  • addrsize 是 I2C 目标地址的位数。有效值为 7 和 10。

  • mem 是一个支持缓冲区协议且可写的对象。如果未指定,则没有后备内存,数据必须通过 I2CTarget.readinto()I2CTarget.write() 方法来读写。

  • mem_addrsize 是内存地址的位数。有效值为 0、8、16、24 和 32。

  • scl 是指定用于 SCL 的引脚对象。

  • sda 是指定用于 SDA 的引脚对象。

请注意,某些移植版本/开发板会为 sclsda 提供默认值,可在此构造函数中更改。另一些则会为 sclsda 提供固定值,无法更改。

通用方法

deinit() None

反初始化 I2C 目标。调用此方法后,硬件将不再响应 I2C 总线上的请求,且不能再调用任何其他方法。

readinto(buf: bytearray) int

将 I2C 控制器写入的任何待处理字节读入给定的缓冲区。返回读取的字节数。

write(buf: bytes) int

写出给定缓冲区中的字节,在 I2C 控制器发送读请求后将其传递给控制器。返回写入的字节数。大多数移植版本每次只接受向此方法传入一个字节。

irq(handler: Callable[[I2CTarget], None] | None = None, trigger: int = IRQ_END_READ | IRQ_END_WRITE, hard: bool = False) None

安装一个 IRQ handler,当 trigger 所选事件之一触发时调用它。trigger 是若干 IRQ_* 常量按位或(OR)组合而成的位掩码;默认在每次控制器侧读或写结束时触发。

hard=True 会注册一个硬中断处理程序(回调中不进行堆分配)。默认为调度回调。传入 handler=None 可禁用该中断。

备注

IRQ_ADDR_MATCH_READIRQ_ADDR_MATCH_WRITEIRQ_READ_REQIRQ_WRITE_REQ 必须由硬 IRQ 回调(hard=True)处理,因为这些事件必须与硬件同步地予以确认。IRQ_END_READIRQ_END_WRITE 既可由软回调也可由硬回调处理;所有事件共用单个处理程序,因此只要有任一事件需要硬回调,则全部都需要。

当向构造函数提供内存缓冲区时,驱动会抑制仅用于写入内存地址的那次事务的 IRQ_END_WRITE。即使软 IRQ 调度器延迟了回调,这也能让事务结束事件保持有意义。

memaddr

I2C 控制器最近所选内存地址的整数值(仅在构造函数中指定了 mem 时有效)。

常量

每个 IRQ_* 常量都是一个标志位。将它们按位或(OR)组合可构建用于 irq()trigger 掩码。在处理程序内部,可通过将 self.irq().flags() 与各常量按位与(AND)来恢复已触发的事件集合。

IRQ_ADDR_MATCH_READ: int

当控制器为读事务寻址此目标时触发(地址字节已接收,且读/写位被置为 1)。仅限硬 IRQ。

IRQ_ADDR_MATCH_WRITE: int

当控制器为写事务寻址此目标时触发(地址字节已接收,且读/写位被置为 0)。仅限硬 IRQ。

IRQ_READ_REQ: int

当控制器向目标请求一个字节时触发。处理程序必须在控制器的时钟周期完成之前调用 write() 来提供该字节。仅限硬 IRQ。

IRQ_WRITE_REQ: int

当控制器已向目标时钟输入一个字节时触发。处理程序必须在控制器发送下一个字节之前调用 readinto() 来取回该字节。仅限硬 IRQ。

IRQ_END_READ: int

当控制器完成一次读事务时触发(收到 STOP / 重复 START)。既可由硬 IRQ 回调也可由软 IRQ 回调处理。

IRQ_END_WRITE: int

当控制器完成一次写事务时触发(收到 STOP / 重复 START)。既可由硬 IRQ 回调也可由软 IRQ 回调处理。当向构造函数提供了 mem 缓冲区时,写入内存地址的那次事务会抑制此事件。