跳轉到

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

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

在終端機執行下列指令:

$ python run_i3c_event_example.py

事件註冊裝飾器

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

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

# 訂閱目標裝置的已指派位址事件
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 脈衝寬度觸發