範例:事件註冊與處理函式設定
除了上述基本操作,AQPXLIB 也支援訂閱 Protocol Exerciser 送出的事件。
範例程式與設定¶
範例如下。
import logging
import threading
from aqpxlib import AqProtocolExerciser
from aqpxlib.i3c import (
ACUTE_DEFAULT_PID,
PxI3CBus,
PxI3CBaseController,
PxI3C32BitRegisterTarget,
)
from aqpxlib.event import PxI3CTargetAssignedAddressEvent
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(PxI3CTargetAssignedAddressEvent)
def i3c_target_assigned_address_handler(assigned: PxI3CTargetAssignedAddressEvent):
"""目標裝置已指派位址事件的處理函式."""
assigned_address = assigned.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()
在終端機執行下列指令:
事件註冊裝飾器¶
匯流排與裝置的設定與前一個範例相同,此處不再重複。主要差異是現在訂閱目標裝置的已指派位址事件。
以下說明如何用裝飾器註冊事件。
from aqpxlib.event import PxI3CTargetAssignedAddressEvent
# 訂閱目標裝置的已指派位址事件
event = threading.Event()
@i3c_target.on_event(PxI3CTargetAssignedAddressEvent)
def i3c_target_assigned_address_handler(assigned: PxI3CTargetAssignedAddressEvent):
"""目標裝置已指派位址事件的處理函式."""
assigned_address = assigned.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.
事件型別¶
使用 aqpxlib.event 中的事件類別註冊處理函式,處理函式會收到該類別的物件。裝置事件另帶有觸發事件之裝置的 device_id。第二欄的事件名稱仍可代替事件類別使用。
| 事件類別 | 事件名稱 | 欄位 |
|---|---|---|
PxI3CTargetAssignedAddressEvent |
i3_c_target_assigned_address |
assigned_address |
PxI3CTargetUpdatedEventStateEvent |
i3_c_target_updated_event_state |
event_state |
PxI3CTargetAssignedGroupAddressEvent |
i3_c_target_assigned_group_address |
assigned_group_address |
PxI3CControllerRecvIbiDataEvent |
i3_c_controller_recv_ibi_data |
address, is_ack, data |
PxMemoryTargetReadEvent |
memory_target_read_event |
addr, count |
PxMemoryTargetWriteEvent |
memory_target_write_event |
addr, data |
PxFifoTargetRecvEvent |
fifo_target_recv |
data, last |
PxFifoTargetSentEvent |
fifo_target_sent |
count, last |
PxUartRxDataEvent |
uart_rx_data |
data, has_parity_error |
PxGpioTriggerEvent |
gpio_trigger |
pin_idx, pulse_duration, pulse_polarity |
PxUpdatedTopologyEvent |
updated_topology |
handle_idx, revision, config |
PxUpdatedTopologyEvent.config 為該 handle 的匯流排設定;handle 槽位被清除時為 None。
在 attach 之前註冊處理函式¶
裝置事件處理函式可以在 attach_to_bus() 之前註冊。pxlib 會將其保存在邏輯註冊表中,僅在裝置取得匯流排 device_id 後才向 Protocol Exerciser 訂閱。
from aqpxlib.event import PxI3CTargetAssignedAddressEvent
i3c_target = PxI3C32BitRegisterTarget(px_session, name="i3c_target", ...)
i3c_target.add_event_handler(PxI3CTargetAssignedAddressEvent, handler)
i3c_target.attach_to_bus(i3c_bus) # 在此完成訂閱
呼叫 detach_from_bus() 會註銷目前 device_id 下的 exerciser 訂閱。再次呼叫 attach_to_bus() 會在新指派的 ID 下恢復訂閱。remove_event_handler() 接受與 add_event_handler() 相同的事件類別(或事件名稱)。
全域事件(GPIO 觸發)¶
上方裝置範例使用裝置範圍事件(例如 @i3c_target.on_event(...))。有些通知是全域的: 它們不綁定匯流排裝置 ID.把事件類別傳給 AqProtocolExerciser.add_event_handler 來註冊它們。
當你用 set_gpio_trigger 設定的 GPIO 脈衝寬度觸發符合條件時,會發出 gpio_trigger 全域事件。請在 AqProtocolExerciser 實例上註冊處理函式,而不是在裝置上:
from aqpxlib.event import PxGpioTriggerEvent
def on_gpio_trigger(info: PxGpioTriggerEvent) -> None:
# info.pin_idx, info.pulse_duration, info.pulse_polarity
...
px_session.add_event_handler(PxGpioTriggerEvent, on_gpio_trigger)
一般 GPIO 讀取、驅動與脈衝 API 請見 GPIO 讀取、驅動與脈衝。接腳限制、持續時間範圍、觸發模式與完整走查(含獨立脈衝範例)請見 GPIO 脈衝寬度觸發。