Skip to Content
Plugin 開發Plugin API 地圖

Plugin API 地圖

Plugin 的整個對外介面都在一個標頭檔裡:nexus_plugin_api.h。它主要是兩張表:

  • IcPlugin:你填、主程式讀的表。裡面有你的名字、位址、指令,以及一堆 callback(主程式在某件事發生時回頭呼叫你的函式)。
  • NxHostApi:主程式填、你讀的表。裡面是主程式提供給你的服務,例如讀寫硬體、寫 Log、更新面板。你在 init() 拿到它的指標。

這一頁把它們按用途分組,每個成員一行:它做什麼、什麼時候用、可以在哪條執行緒上呼叫。想知道精確的參數、回傳值與錯誤碼,點成員名稱進參考文件。

怎麼讀「執行緒」欄

  • UI Thread:主程式的畫面執行緒。在這裡不能碰硬體、不能睡覺。
  • I2C Worker:該掛載所綁定的後端的 I2C 工作執行緒。硬體存取在這裡做。
  • 不限:標頭明文寫任何執行緒都可以。
  • —:標頭沒有另外限定。

不確定自己的 callback 跑在哪條執行緒時,先看生命週期與執行緒;規則的完整版在開發指南第 4 章。

結構裡名叫 reserved* 的欄位都是對齊用的保留欄位,一律填 0,這一頁不逐個列。

宣告與生命週期

先決定你是什麼:名字、綁哪些位址、有哪些指令,然後由主程式在適當的時機呼叫你的 callback。

你要填的資料欄位

成員做什麼、什麼時候用執行緒
IcPluginPlugin 的總表,NexusPluginGetInterface() 回傳一個指向它的指標—
IcPlugin::namePlugin 的唯一名稱,plugin list 與 Log 前綴都用它。名稱撞名時先註冊的贏—
IcPlugin::description一行說明,顯示在 plugin list;不需要填 NULL—
IcPlugin::panel_mode有沒有自己的面板:無(headless)、Widget 模式、Canvas 模式。新手選 Widget—
IcPlugin::slaves你要綁的 I2C 位址表(NxSlaveDecl 陣列),第一個是面板預設顯示的對象—
IcPlugin::slave_countslaves 有幾個元素;0 或超過上限會拒絕載入—
IcPlugin::slave_decl_size填 sizeof(NxSlaveDecl),讓主程式確認你的位址表與它認得的是同一種尺寸—
IcPlugin::commands你的自訂指令表(NxCommandDef 陣列);沒有填 NULL—
IcPlugin::command_countcommands 有幾個元素—
IcPlugin::reg_semantics選用:暫存器語意表(NxRegSemantic 陣列),讓畫面顯示「這個位址是什麼」—
IcPlugin::reg_semantic_countreg_semantics 有幾個元素—
NxSlaveDecl一個位址的宣告:7-bit 位址、8/16 位元定址、有沒有 bank 暫存器、顯示名稱—
NxCommandDef一筆自訂指令:名稱、說明、用法字串、處理函式—
NxCommandFn自訂指令處理函式的型別,argv[0] 是指令名本身,只在這次呼叫期間有效I2C Worker
NxRegSemantic一筆暫存器語意:哪個 bank/位移的哪幾個位元叫什麼名字—

生命週期 callback

一顆 Plugin 的一生:載入 → init → probe → 反覆的 poll_fn/on_tick/事件 → cleanup。整個流程見生命週期與執行緒。

成員做什麼、什麼時候用執行緒
IcPlugin::init掛載時呼叫一次,交給你 ctx 與 host。存起來,初始化自己的變數。可為 NULL;不要在這裡碰硬體UI Thread
IcPlugin::cleanup卸載時呼叫,釋放你自己的資源。強制實作,不能是 NULLI2C Worker
IcPlugin::probe確認你的 IC 真的在匯流排上,回 ERR_OK 表示找到。探測 dongle 的 GPIO 能力也寫在這裡,不能寫在 initI2C Worker
IcPlugin::poll_fn背景輪詢,週期由 SetPollingInterval 決定。poll off 時不會被呼叫I2C Worker
IcPlugin::interrupt_handler硬體中斷腳或使用者指令觸發;廣播給同一條匯流排上所有實作它的掛載。不是你的中斷就回 ERR_OKI2C Worker
IcPlugin::on_tick純軟體的定時脈動,poll off 時仍會呼叫(但此時 SubmitJob 的低優先級會被拒)I2C Worker
IcPlugin::on_tab_activate你所在的 Tab 被切成作用中UI Thread
IcPlugin::on_tab_deactivate你所在的 Tab 離開作用中UI Thread
NxPluginGetInterfaceFnDLL 唯一要匯出的進入點 NexusPluginGetInterface 的型別:回傳你的 IcPlugin,不相容回 NULLUI Thread

讀寫硬體

三種方式怎麼選見讀寫硬體的三種方式。一句話:要立刻拿到結果用 ExecSync,背景丟出去用 SubmitJob,只想看目前快取用 GetRegSnapshot。

成員做什麼、什麼時候用執行緒
NxHostApi::ExecSync同步執行一筆讀/寫/讀改寫,等硬體回應才返回。在 probe、指令處理函式、on_widget_event 裡讀暫存器就用它只能在 I2C Worker(UI Thread 上回 ERR_WRONG_THREAD)
NxHostApi::SubmitJob非同步送一筆 Job 進佇列,立刻返回,結果由 on_complete 通知。佇列滿會立即拒絕,不會卡住UI Thread 與 I2C Worker
NxHostApi::GetRegSnapshot取得主程式目前記得的暫存器快照,附一份「哪些位元組讀過」的遮罩。不碰硬體,畫面更新前取一份最適合—
NxHostApi::SetPollingInterval設定你的 poll_fn 週期(毫秒),0 是停用。這是請求,不是保證—
NxHostApi::ShouldAbort問「主程式要我快點結束嗎」。跑很久的迴圈(例如燒錄)每一圈都要問:不問的話,關機時 Core 只等 2 秒就放棄你的執行緒,行程關不乾淨不限
NxHostApi::GetBackendName取得這個掛載用的 dongle 後端名稱(使用者設定的字串)不限
NxHostApi::GetResolvedBackendName取得別名解析後的正式後端名稱,要「記住是哪一顆實體」時用它I2C Worker
NxJobRequest一筆硬體存取的請求:操作種類、位址、bank、位移、長度、資料、on_complete。memset 成 0 就是「全部用我這個掛載的預設」。ABI 4.1 起多一種 NX_JOB_VENDOR(用名稱呼叫 dongle 的 vendor op,只能走 ExecSync)—
NxJobCompleteFnSubmitJob 完成時的回呼型別,data 只在這次呼叫期間有效,要保留請自己複製I2C Worker
IcPlugin::on_watch_triggered使用者設的監看條件成立時通知你,只送給該監看所屬掛載上的 Plugin。poll off 時不會發生I2C Worker

面板與 Widget

Widget 模式:你用 .nxlay 版面檔描述要哪些控制項,主程式負責排版與畫面,使用者操作時再通知你。教學見 Widget 模式深入與 Widget 型別詳解。

版面與屬性

成員做什麼、什麼時候用執行緒
NxHostApi::LoadLayout以資源(RCDATA)或開發用檔案(NX_LAYOUT_F_FILE)載入 .nxlay,取代整個面板的控制項;NULL 清空面板。內容相同是 no-op—
NxHostApi::LoadLayoutData同上,但版面文字在記憶體裡(靜態登錄的 Plugin 或執行期組出來的版面)—
NxHostApi::CreateWidget執行期新增一個控制項(版面無法事先決定時用)—
NxHostApi::DestroyWidget移除一個控制項;該面板在途的事件作廢—
NxHostApi::SetWidgetProp寫一個屬性:值、標籤、選項、表格列、圖表樣本、啟用/隱藏…。不會回頭觸發你自己的 on_widget_event—
NxHostApi::GetWidgetProp讀回一個屬性(Core 影子表的已承諾值)—
NxHostApi::GetHostCaps問主程式支援哪些能力(Canvas、圖片、檔案對話框),依此決定要不要用對應功能—
NxHostApi::AddTab執行期多開一個屬於你的 Tab(不會存進設定,重開要自己重建)。滿了回 ERR_FULL,別重試不限
NxHostApi::RemoveTab移除用 AddTab 開的 Tab;不會自動卸載上面的 Plugin不限

事件

成員做什麼、什麼時候用執行緒
IcPlugin::on_widget_event使用者改了某個 widget 或按了按鈕。按鈕的典型反應是讀寫 I2C,所以在 Worker 上送達I2C Worker
IcPlugin::on_layout_changed你的面板可用尺寸(抽象格)改變。快取起來供 Canvas 繪圖用;只在尺寸有變時送UI Thread
IcPlugin::on_key_press攔截按鍵(只送給目前 Tab 的作用中 Plugin),回 NX_EVT_CONSUMED 吞掉。ESC 不能攔UI Thread
IcPlugin::on_mouse_event滑鼠事件,座標是面板內容區的抽象格。只送給 Canvas 面板UI Thread
IcPlugin::on_command_pre_execute使用者指令執行前的竊聽與改寫;改寫的字串寫進主程式給的緩衝區UI Thread

資料型別

成員做什麼、什麼時候用執行緒
NxWidgetEventon_widget_event 收到的事件:種類(CHANGED/ACTIVATED)、屬性、修飾鍵、user_data、新值或點擊位置—
NxPropVal屬性值容器(恆為 24 位元組):SetWidgetProp/GetWidgetProp 的參數,也是事件裡的 val—

Canvas 繪製

Canvas 模式:面板是一塊字元格畫布,你自己畫。只在 panel_mode 是 Canvas 時才用。教學見 Canvas 模式。

成員做什麼、什麼時候用執行緒
IcPlugin::ui_render_fnHost 要畫你的 Canvas 面板時呼叫。裡面不能碰硬體、不能睡覺、不能鎖共享資料;超過 10 ms 會被下線UI Thread
NxHostApi::DrawText在面板相對座標畫一段文字,用語意色不用 RGB。只能在 ui_render_fn 內呼叫UI Thread(ui_render_fn 內)
NxHostApi::DrawBox畫矩形框,邊框字元由主程式依主題決定UI Thread(ui_render_fn 內)
NxHostApi::DrawRegion一次提交一整塊字元格。要畫很多格時用它,比逐格呼叫快得多UI Thread(ui_render_fn 內)
NxCanvasCell一個字元格:字元+前景色+背景色,DrawRegion 的元素—
IcPlugin::on_mouse_eventCanvas 的滑鼠輸入;按下後拖出面板也會持續收到,直到放開UI Thread
NxHostApi::NotifyUiRefresh資料變了,請主程式重畫。Worker 上通知畫面的唯一合法方式;同一週期多次呼叫會合併—

Log 與通知

成員做什麼、什麼時候用執行緒
NxHostApi::LogWrite寫一筆 Log,前綴 [你的名稱] 由主程式加。等級只能用 NX_LOG_IC/INFO/WARN/ERROR/DEBUG/TRACE,其他會被改成 IC不限
NxHostApi::LogWriteStyled同上,另外可把訊息中的幾段上色或加粗(純 ASCII 訊息)。spans 傳 NULL 就等於 LogWrite不限
NxLogSpan一段上色區間:起點、長度、前景/背景語意色、樣式位元—
IcPlugin::on_log_generated系統產生任何 Log 都通知你(廣播、不可攔截)。裡面呼叫 LogWrite 會被丟掉,防止無限遞迴Core 的 Log 廣播執行緒
NxHostApi::NotifyUiRefresh請求重畫,見「Canvas 繪製」—

設定

讓使用者的設定在關掉再開之後還在。教學見設定持久化。

成員做什麼、什麼時候用執行緒
IcPlugin::on_config_load掛載(init 成功)後、probe 之前,把你在 config.ini 的 [Plugin.<名稱>] 段逐筆交給你。存起來,含不認得的鍵UI Thread
IcPlugin::on_config_save主程式要存檔時呼叫,你把整段設定以 key=value 多行寫進它給的緩衝區。整段取代;要快,不能碰硬體UI Thread

UART 與 GPIO

UART(你自己的序列埠)

Plugin 可以開一個自己私有的 COM 埠,和面板上的 UART 面板互不相干。接收是 pull 模型:主程式不會推資料給你,你要在 on_tick 之類的週期 callback 裡自己讀。

成員做什麼、什麼時候用執行緒
NxHostApi::Uart_Open開你私有的 UART 埠(埠名、鮑率、資料位元、同位、停止位元、流量控制)。會阻塞最多 1.5 秒,別在 init 這類 UI Thread callback 裡開不限(避開 UI Thread)
NxHostApi::Uart_Close關閉私有埠;本來就沒開也回成功。同樣會等執行緒結束不限(避開 UI Thread)
NxHostApi::Uart_Read取走目前已收到、還沒讀的位元組。沒資料不是錯誤(out_got 是 0);不做行切分—
NxHostApi::Uart_Write原樣送出位元組,不附加換行;TX 佇列滿會立即回 ERR_QUEUE_FULL—
NxHostApi::Uart_SetParams不重開埠,直接改鮑率等參數—
IcPlugin::on_uart_rx_ex面板上的 UART 埠收到資料時廣播給你(附來源 Tab 與面板)。你自己 Uart_Open 的私有埠不會觸發它該埠的 UART Worker
IcPlugin::on_uart_rx已停用:Core 不會呼叫,欄位只為 ABI 相容而留。請用 on_uart_rx_ex—(不會被呼叫)

GPIO(dongle 上的腳位)

GPIO 腳位由 dongle 提供,不是每顆 dongle 都有。下面全部只能在 I2C Worker 的 callback 內呼叫(UI Thread 上回 ERR_WRONG_THREAD),所以能力探測要寫在 probe,不能寫在 init。

成員做什麼、什麼時候用執行緒
NxHostApi::GpioGetCaps問這顆 dongle 有哪些腳可以當 GPIO、哪些支援中斷。回報的是硬體能力,不是你板子的接線I2C Worker
NxHostApi::GpioConfigure宣告一支腳並設定功能(輸入、輸出、中斷)與輸出初值。先宣告者持有,別的掛載會被拒I2C Worker
NxHostApi::GpioWrite寫自己持有的輸出腳(非零為高電位)I2C Worker
NxHostApi::GpioRead讀自己持有的腳位電位I2C Worker
NxHostApi::GpioRelease用完提早還腳,還原成高阻抗輸入。選用:cleanup 之後 Core 會強制回收I2C Worker
NxGpioCapsGpioGetCaps 的結果:可當 GPIO 的腳遮罩與支援中斷的腳遮罩。呼叫前要先填 size_of_struct—

Dongle 專屬服務(ABI 4.1)

INT 邊緣事件、匯流排恢復、精確脈衝與 vendor op 都需要 dongle 支援(STM32 dongle 有,其他不一定)。五支服務與 on_int_event 是 ABI 4.1 追加的,舊主程式沒有:用 NX_ABI_HAS_FIELD 確認再呼叫。教學見 Dongle 專屬功能。

成員做什麼、什麼時候用執行緒
NxHostApi::IntConfigure設定 INT 腳偵測的邊緣與內部上下拉;NX_INT_EDGE_NONE 只釋放自己的請求I2C Worker
NxHostApi::IntGetStatus讀 INT 腳目前的設定、電位與 dongle 端累計計數。呼叫前要先填 SizeOfStructI2C Worker
NxHostApi::IntClear清除邊緣計數與尚未取走的事件(不檢查擁有權,會影響同後端的其他 Plugin)I2C Worker
NxHostApi::BusRecover嘗試解開被卡住的 I2C 匯流排。ERR_OK 不代表已釋放,要看 out_pinsI2C Worker
NxHostApi::GpioPulse在自己持有的輸出腳輸出定寬脈衝,期間佔住本匯流排的 WorkerI2C Worker
IcPlugin::on_int_event一批逐邊緣事件(廣播給同後端所有實作者)。dropped 是增量;有 INT 時 poll_fn 不會自動停I2C Worker
NxIntEvent一筆邊緣事件:t_us(dongle 本地微秒,會繞回)、count、rx_tick、level—
NxIntStatusIntGetStatus 的結果:edge、pull、level、count、dropped、last_t_us—

版本與 ABI

Plugin 與主程式各自編譯,靠這幾個欄位確認彼此的結構尺寸對得上。教學見版本號與 ABI 相容。

成員做什麼、什麼時候用執行緒
IcPlugin::abi_major你編譯時的 ABI 主版本,填標頭的巨集。與主程式不符就拒絕載入—
IcPlugin::abi_minor你編譯時的 ABI 次版本,填標頭的巨集。落後或超前都接受—
IcPlugin::size_of_struct填 sizeof(IcPlugin)。主程式依它判斷你的表有多長,是降級的唯一依據—
IcPlugin::version_major你這顆 Plugin 自己的版本號(主)。與 ABI 無關,Plugin Manager 用它比對更新—
IcPlugin::version_minor你的版本號(次)—
IcPlugin::version_patch你的版本號(修訂)。三段全為 0 代表「未宣告」,請從 1.0.0 起算—
IcPlugin::version_rsv保留,填 0—
NxHostApi主程式給你的服務函式表,在 init 拿到指標,可長期保存,但不能修改、不能釋放—
NxHostApi::abi_major主程式實作的 ABI 主版本—
NxHostApi::abi_minor主程式實作的 ABI 次版本。不要拿它比大小判斷功能有沒有,見 size_of_struct—
NxHostApi::size_of_struct主程式實際填了多長。要用較新的服務時,用 NX_ABI_HAS_FIELD(host->size_of_struct, NxHostApi, 欄位名) 確認它存在,樣板見開發指南第 10 章—

基本型別

標頭自己定義一組型別別名,不用 stdint.h(VC6 沒有)也不用 windows.h(會破壞 PDK 的可攜性)。

成員做什麼、什麼時候用執行緒
NxByte無號 8 位元—
NxWord無號 16 位元—
NxDword無號 32 位元—
NxLong有號 32 位元,也是全部錯誤碼回傳值的型別—
NxBool布林值,真假用 NX_TRUE/NX_FALSE—
NxPluginCtx不透明的上下文控制代碼,init 交給你,每個 HostAPI 的第一個參數。它代表「你在這個 Tab 上的掛載」,cleanup 返回後就失效,不要自己記 Tab 編號—

Port Shim(移植既有韌體)

把既有的 8051 風格 I2C 韌體搬進來,原始碼幾乎不用改:韌體 #include "nexus_port.h",Plugin 這一側 #define NX_PORT_IMPL 後 include nexus_port_plugin.h 提供實作。教學見移植既有韌體。這一組不在 nexus_plugin_api.h,而在 nexus_port.h/nexus_port_plugin.h。

I2C 存取類函式都以 NxHostApi::ExecSync 為底,所以只能在 I2C Worker 的 callback 內呼叫。addr8 是 7-bit 位址左移一位的 8-bit 寫法(與韌體慣例一致)。

成員做什麼、什麼時候用執行緒
nx_port_bind在 init 呼叫,記下 ctx 與 host。一顆移植 DLL 同時只能服務一個掛載,回傳值要原樣當 init 的回傳值UI Thread
nx_port_unbind在 cleanup 呼叫,清算沒 free 的記憶體(會記 WARN 提醒洩漏)I2C Worker
nx_i2c_reg_read用 8-bit 暫存器位移讀多個位元組I2C Worker
nx_i2c_reg_write用 8-bit 暫存器位移寫多個位元組I2C Worker
nx_i2c_reg16_read用 16-bit 暫存器位移讀。要掛載設成 16 位元定址才會真的在線路上送 16-bitI2C Worker
nx_i2c_reg16_write用 16-bit 暫存器位移寫,同上I2C Worker
nx_i2c_reg_set只改一個暫存器的幾個位元(讀改寫),以單一原子 Job 完成I2C Worker
nx_i2c_write_raw不帶暫存器位移,直接寫一串原始位元組I2C Worker
nx_i2c_write_read先寫後讀(中間不釋放匯流排),送命令碼後立刻讀回結果的 IC 用I2C Worker
nx_i2c_set_bus指定後續存取的具名匯流排。只是記下名字,能不能解析到要等下一次存取才知道I2C Worker
i2c_readnx_i2c_reg_read 的短別名,給用這個名字的韌體I2C Worker
i2c_writenx_i2c_reg_write 的短別名I2C Worker
delay_ms韌體常見的毫秒延遲。只能在 Worker 上用,不能在 UI Thread 等超過 1 msI2C Worker
delay_us微秒延遲;不承諾實際精度,只保證「至少這麼久」I2C Worker
nx_port_tick_ms取得相對毫秒計時,量經過時間用(會繞回,比較請用差值)UI Thread 與 I2C Worker
nx_port_should_abort韌體版的 ShouldAbort:長迴圈每圈問一次,非零就盡快返回;便宜到可以在緊迴圈裡頻繁呼叫—
nx_port_printf韌體的 printf,改寫成一筆 INFO 等級的 Log—
nx_port_malloc韌體的 malloc(追蹤表會在 nx_port_unbind 時檢查洩漏)。不能在 ui_render_fn 內用—
nx_port_free對應的 free,NULL 是合法的無操作—
nx_port_gpio_write寫本掛載持有的 GPIO 輸出腳I2C Worker
nx_port_gpio_read讀本掛載持有的 GPIO 腳位電位I2C Worker
nx_port_gpio_pulse對輸出腳打一個脈波:拉到指定電位、保持一段時間、再寫回相反電位I2C Worker
NX_PORT_PIN腳位編號的恆等轉換巨集,讓移植碼統一寫法,不做重新映射—
Port Shim 相容別名巨集printf/malloc/free、mymalloc/osDelay、看門狗與 _nop_() 等韌體慣用名稱,自動改寫到上面的函式或空操作—