範例:事件註冊與處理函式設定
除了上述基本操作,AQPXLIB 也支援訂閱 Protocol Exerciser 送出的事件。
範例程式與設定¶
範例如下。
run_i3c_event_example.py
import logging
import threading
from aqpxlib import AqProtocolExerciser
from aqpxlib.i3c import (
ACUTE_DEFAULT_PID,
PxI3CBus,
PxI3CBaseController,
PxI3C32BitRegisterTarget,
)
from aqpxlib.event import PxEventMsg
from aqpxlib.exceptions import PxAPIError
logger = logging.getLogger(__name__)
def exec_i3c_event_example():
"""I3C 事件註冊與處理函式設定的基本範例.
步驟依下列順序執行:
1. 建立並開啟與 Acute Protocol Exerciser 的連線.
2. 設定匯流排上正確的電壓準位與上拉電阻.
3. 建立 I3C 匯流排控制代碼介面.
4. 將 I3C 控制器與目標裝置掛載到匯流排.
5. 訂閱目標裝置的事件.
6. 執行 Dynamic Address Assignment(此時會收到已指派位址事件)
"""
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 right now, 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}")
# 訂閱目標裝置的已指派位址事件
event = threading.Event()
@i3c_target.on_event("i3_c_target_assigned_address")
def i3c_target_assigned_address_handler(event_msg: PxEventMsg):
"""目標裝置已指派位址事件的處理函式."""
assigned_address = event_msg.device_event.i3_c_target_assigned_address.assigned_address
logger.info(f"I3C target assigned address: 0x{assigned_address:02x}")
event.set()
# 這裡直接執行 DAA,會為目標裝置指派動態位址,
# 因此已訂閱的處理函式應被呼叫.
i3c_controller.do_daa()
logger.info("Controller performed DAA")
# 確認我們已收到事件
assert event.wait(timeout=3), "Event has never been called"
# 將裝置從匯流排卸離
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_event_example()
在終端機執行下列指令:
事件註冊裝飾器¶
匯流排與裝置的設定與前一個範例相同,此處不再重複。主要差異是現在訂閱目標裝置的已指派位址事件。
以下說明如何用裝飾器註冊事件。
# 訂閱目標裝置的已指派位址事件
event = threading.Event()
@i3c_target.on_event("i3_c_target_assigned_address")
def i3c_target_assigned_address_handler(event_msg: pxmsg.PxEventMsg):
"""目標裝置已指派位址事件的處理函式."""
assigned_address = event_msg.device_event.i3_c_target_assigned_address.assigned_address
logger.info(f"I3C target assigned address: 0x{assigned_address:02x}")
event.set()
# 這裡直接執行 DAA,會為目標裝置指派動態位址,
# 因此已訂閱的處理函式應被呼叫.
i3c_controller.do_daa()
logger.info("Controller performed DAA")
# 確認我們已收到事件
assert event.wait(timeout=3), "Event has never been called"
執行 DAA 時,目標裝置會收到控制器指派的位址。此時 Protocol Exerciser 會立即通知訂閱者位址已指派。
上方範例以裝飾器風格撰寫,你也可以用目標類別的 add_event_handler 以宣告方式註冊。
範例最終輸出應類似:
[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] Controller performed DAA
[INFO] I3C target assigned address: 0x08
[INFO] Detached the controller and target from the bus
這裡可以看到目標裝置已被指派位址 0x08.
全域事件(GPIO 觸發)¶
上方裝置範例使用裝置範圍事件(例如 @i3c_target.on_event(...))。有些通知是全域的: 它們不綁定匯流排裝置 ID.Exerciser 以單一邏輯名稱發布這些事件,你把它傳給 AqProtocolExerciser.add_event_handler。
當你用 set_gpio_trigger 設定的 GPIO 脈衝寬度觸發符合條件時,會發出 gpio_trigger 全域事件。請在 AqProtocolExerciser 實例上註冊處理函式,而不是在裝置上:
from aqpxlib.event import PxEventMsg
def on_gpio_trigger(event_msg: PxEventMsg) -> None:
info = event_msg.global_event.gpio_trigger
# info.pin_idx, info.pulse_duration, info.pulse_polarity
...
px_session.add_event_handler("gpio_trigger", on_gpio_trigger)
一般 GPIO 讀取、驅動與脈衝 API 請見 GPIO 讀取、驅動與脈衝。接腳限制、持續時間範圍、觸發模式與完整走查(含獨立脈衝範例)請見 GPIO 脈衝寬度觸發。