跳轉到

範例:事件註冊與處理函式設定

除了上述基本操作,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 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()

在終端機執行下列指令:

$ python run_i3c_event_example.py

事件註冊裝飾器

匯流排與裝置的設定與前一個範例相同,此處不再重複。主要差異是現在訂閱目標裝置的已指派位址事件。

以下說明如何用裝飾器註冊事件。

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 脈衝寬度觸發。