示例:创建总线与器件
示例代码与配置¶
此示例演示
- 如何在 Protocol Exerciser 上创建总线与器件接口。
- 如何与控制器交互,并从目标器件获取信息。
这里以 I3C 为例,但相同原则也适用于其他总线类型。
创建名为 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()
在终端执行下列命令:
输出应类似:
[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Ω 上拉电阻。
典型的总线句柄会包含通道配置;I3C 总线以 scl 与 sda 参数指定。
构造 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 构造函数传入不同的 scl 与 sda 来使用其他引脚。
总线句柄引脚冲突
你可以创建多个总线句柄,但必须确保引脚定义互不冲突。否则部分句柄会创建失败。
创建器件¶
接着创建器件并挂载到总线。此示例创建一个控制器与一个目标器件。
下列片段来自上方示例,用来创建 PxI3CBaseController 与 PxI3C32BitRegisterTarget:
# 创建控制器器件
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 总线上的器件都应可用,并可获取控制器分配的动态地址。
# 获取目标器件已分配的动态地址
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]}")