跳转至

示例:创建总线与器件

示例代码与配置

此示例演示

  • 如何在 Protocol Exerciser 上创建总线与器件接口。
  • 如何与控制器交互,并从目标器件获取信息。

这里以 I3C 为例,但相同原则也适用于其他总线类型。

创建名为 run_i3c_example.py 的新文件,并将下列代码复制进去:

run_i3c_example.py
import logging

from aqpxlib import AqProtocolExerciser
from aqpxlib.i3c import (
    ACUTE_DEFAULT_PID,
    PxI3CBus,
    PxI3CBaseController,
    PxI3C32BitRegisterTarget,
)
from aqpxlib.exceptions import PxAPIError

logger = logging.getLogger(__name__)

def exec_i3c_example():
    """演示如何与 Acute Protocol Exerciser 交互.

    步骤按下列顺序执行:

    1. 创建并打开与 Acute Protocol Exerciser 的连接.
    2. 配置总线上正确的电压电平与上拉电阻.
    3. 创建 I3C 总线句柄接口.
    4. 将 I3C 控制器与目标器件挂载到总线.
    5. 初始化 I3C 总线以执行 Dynamic Address Assignment.
    6. 对目标器件执行写入/读取.
    7. 卸载器件并关闭与 Protocol Exerciser 的连接.
    """

    with AqProtocolExerciser.connect(port=60600) as px_session:
        logger.info("Created an Exerciser session")

        # 在 SCL 引脚 0、SDA 引脚 1 上配置 I3C 总线
        i3c_bus = PxI3CBus(px_session, scl=0, sda=1)
        logger.info(f"Created {i3c_bus}")

        # 将电压设为 3.3V,上拉电阻设为 1.5kΩ
        i3c_bus.voltage = 3300
        i3c_bus.pullup_resistance = 1500

        # 创建控制器器件
        i3c_controller = PxI3CBaseController(px_session, name="i3c_controller")
        try:
            i3c_controller.attach_to_bus(i3c_bus)
        except PxAPIError:
            logger.warning("Multiple controller is not supported, using the existing controller")
            i3c_controller = i3c_bus.get_current_controller()

        logger.info(f"Added a I3C controller with name {i3c_controller.name}")

        # 创建目标器件
        bcr = 0x00  # Bus Characteristic Register
        dcr = 0x00  # Device Characteristic Register
        pid = ACUTE_DEFAULT_PID | (0x01 << 32) | 0 # Provisioned ID

        i3c_target = PxI3C32BitRegisterTarget(
            px_session,
            name="i3c_target",
            static_addr=0x12,
            sub_address_type=PxI3C32BitRegisterTarget.SubAddressType.NONE,
            bcr=bcr,
            dcr=dcr,
            pid=pid,
        )
        i3c_target.attach_to_bus(i3c_bus)
        logger.info(f"Added a I3C 32-bit register target with name {i3c_target.name}")

        # 初始化 I3C 总线,流程依 I3C 规范
        i3c_controller.init_bus()
        logger.info("I3C bus initialized")

        # 获取目标器件状态以读取已分配的动态地址
        assigned_address = i3c_target.assigned_address
        logger.info(f"Target assigned dynamic address: {assigned_address}")

        # 对目标器件执行写入/读取
        is_ack = i3c_controller.private_write(assigned_address, [0xDE, 0xAD, 0xBE, 0xEF])
        logger.info(f"I3C target" + (" acknowledged the write" if is_ack else " did not acknowledge the write"))

        data = i3c_controller.private_read(assigned_address, 4)
        logger.info(f"I3C target read data: {[f'{byte:02x}' for byte in data]}")

        # 将器件从总线卸载
        i3c_target.detach_from_bus()
        i3c_controller.detach_from_bus()
        logger.info(f"Detached the controller and target from the bus")


if __name__ == "__main__":
    logging.basicConfig(format='[%(levelname)s] %(message)s')
    logger.setLevel(logging.INFO)
    exec_i3c_example()

在终端执行下列命令:

$ python run_i3c_example.py

输出应类似:

[INFO] Created an Exerciser session
[INFO] Created I3C Bus (SCL: 0, SDA: 1)
[INFO] Added a I3C controller with name i3c_controller
[INFO] Added a I3C 32-bit register target with name i3c_target
[INFO] I3C bus initialized
[INFO] Target assigned dynamic address: 0x08
[INFO] I3C target acknowledged the write
[INFO] I3C target read data: ['0xde', '0xad', '0xbe', '0xef']
[INFO] Detached the controller and target from the bus

接下来逐步说明代码。

创建 session

首先要创建连接到 Protocol Exerciser 的 session。我们使用 with 语句,确保离开代码块时自动关闭 session。

with AqProtocolExerciser.connect(port=60600) as px_session:
    # session `px_session` 已打开
    # 在此执行操作
    ...

# session `px_session` 已关闭

若要在 with 块外使用 AqProtocolExerciser,必须自行断开连接。

创建总线句柄

接着要在 Protocol Exerciser 上配置总线句柄。此示例创建 I3C 总线。可配置总线的硬件参数,这必须符合你实际使用的硬件。此示例将 I3C 总线设为 3.3V、1.5kΩ 上拉电阻。

# 在 SCL 引脚 0、SDA 引脚 1 上配置 I3C 总线
i3c_bus = PxI3CBus(px_session, scl=0, sda=1)

典型的总线句柄会包含通道配置;I3C 总线以 sclsda 参数指定。

构造 PxI3CBus 对象时,会自动发出请求,在 Protocol Exerciser 上配置总线。

若配置成功,Protocol Exerciser 上的引脚定义会更新为: | PIN 0 | PIN 1 | PIN 2 | PIN 3 | PIN 4 | PIN 5 | PIN 6 | PIN 7 | | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | | I3C SCL | I3C SDA | (null) | (null) | (null) | (null) | (null) | (null) |

也可在 PxI3CBus 构造函数传入不同的 sclsda 来使用其他引脚。

总线句柄引脚冲突

你可以创建多个总线句柄,但必须确保引脚定义互不冲突。否则部分句柄会创建失败。

创建器件

接着创建器件并挂载到总线。此示例创建一个控制器与一个目标器件。

下列片段来自上方示例,用来创建 PxI3CBaseControllerPxI3C32BitRegisterTarget:

# 创建控制器器件
i3c_controller = PxI3CBaseController(px_session, name="i3c_controller")
try:
    i3c_controller.attach_to_bus(i3c_bus)
except PxAPIError as e:
    i3c_controller = i3c_bus.get_current_controller()

# 创建目标器件
bcr = 0x00  # Bus Characteristic Register
dcr = 0x00  # Device Characteristic Register
pid = ACUTE_DEFAULT_PID | (0x01 << 32) | 0 # Provisioned ID

i3c_target = PxI3C32BitRegisterTarget(
    px_session,
    name="i3c_target",
    static_addr=0x12,
    sub_address_type=PxI3C32BitRegisterTarget.SubAddressType.NONE,
    bcr=bcr,
    dcr=dcr,
    pid=pid,
)
i3c_target.attach_to_bus(i3c_bus)

执行操作

器件都准备好后,即可通过控制器对目标器件操作。

I3C 必须先初始化总线,以处理地址分配程序。

# 初始化 I3C 总线,流程依 I3C 规范
i3c_controller.init_bus()
logger.info("I3C bus initialized")

完成后,I3C 总线上的器件都应可用,并可获取控制器分配的动态地址。

# 获取目标器件已分配的动态地址
assigned_address = i3c_target.assigned_address
logger.info(f"Target assigned dynamic address: {assigned_address}")

接着对目标器件写入数据并读回。

# 对目标器件执行写入/读取
is_ack = i3c_controller.private_write(assigned_address, [0xDE, 0xAD, 0xBE, 0xEF])
logger.info(f"I3C target" + (" acknowledged the write" if is_ack else " did not acknowledge the write"))

data = i3c_controller.private_read(assigned_address, 4)
logger.info(f"I3C target read data: {[f'{byte:02x}' for byte in data]}")