錄製 I2C 操作
前面幾頁都是自己一行一行寫腳本。但很多時候你手邊只有一個「已經在畫面上手動調對了」的
設定流程——先設位址、寫幾個暫存器、讀一下確認——要再照著寫成腳本很煩,也容易抄錯。
record 就是幫你做這件事:開始錄製之後,你照平常那樣在指令列(或畫面上)操作,
程式把每一個實際完成的 I2C 操作翻成 Lua,一條一條寫進一個檔案。停止錄製後,
這個檔案就是一支可以直接重播的腳本。
三個指令
| 指令 | 作用 |
|---|---|
record start [<檔名>] | 開始錄製 |
record stop | 停止錄製,把檔案收尾 |
record status | 看現在有沒有在錄、存在哪、已經錄了幾條 |
檔案存到哪裡
- 不給檔名:存成
logs/record_日期_時間.lua(例如logs/record_20260929_141203.lua)。 - 只給檔名或相對路徑(
my_setup.lua、bringup/step1.lua):一律放在目前資料夾(執行程式時的工作目錄)底下的logs/裡(跟寫檔案說的一樣,取決於你從哪裡啟動程式),需要的子資料夾會自動建立。相對路徑裡不能有..(會被拒絕), 避免檔案被寫到logs/以外的地方。 - 完整路徑(
C:\lua\my_setup.lua):照你寫的位置存。 - 檔案已經存在就拒絕:不會覆寫,也不會接在後面續寫。要重錄請換一個檔名或先自己刪掉舊檔。
- 已經在錄製中再下一次
record start也會被拒絕;沒在錄製時下record stop則會回報錯誤。
record start my_setup.lua
addr 58
wr 10 01
rd 10
record stoprecord start 成功時會印 REC: recording to <路徑>,record stop 會印
REC: stopped, <N> command(s) saved to <路徑>,同時各記一則 Log。<路徑> 是實際使用的那個,
照你給的樣子印:省略檔名或給相對路徑時是 logs/my_setup.lua 這種相對於目前資料夾的寫法,
給完整路徑就印完整路徑。
視窗版:File → Record I2C…
視窗版也可以不打指令:選單 File → Record I2C… 會開一個存檔對話框(預設在 logs/),
選好檔案就開始錄製,效果等同 record start <路徑>。錄製中這個選項會變成
Stop recording,選它就停止。細節見選單說明:錄製 I2C 操作。
怎麼知道現在有沒有在錄
錄製中,畫面最下面的狀態列會有一個紅色的標示:終端機版是 REC 檔名,視窗版是 ● REC 檔名。
沒有看到它就是沒在錄。直接關閉程式時,如果還在錄製,程式會先自動幫你收尾,已經錄下的部分不會遺失
(每一條都是錄到就立刻寫進檔案)。
什麼會被錄、翻成什麼
只有真的做了事、而且成功了的 I2C 操作會被錄。單純查看的指令(dump、mount、
不帶參數的 watch 之類)和畫面設定(layout、theme…)都不錄。
| 你下的指令 | 錄成的 Lua |
|---|---|
wr 10 01 | i2c.write(0x10, 0x01) |
wr 10 01 02 03(連續多個值) | i2c.write(0x10, {0x01, 0x02, 0x03}) |
set 11 0C 0C(要設的值等於整個遮罩) | i2c.set(0x11, 0x0C) |
set 11 0C 00(要設的值是 0) | i2c.clr(0x11, 0x0C) |
set 11 0C 04(其他情形) | 一行註解寫著原指令,接著一行「讀出來、清掉遮罩位元、換成新值、再寫回」的 i2c.write(...) |
clr 11 04 | i2c.clr(0x11, 0x04) |
rd 10 | local v = i2c.read(0x10) -- recorded: 0x01(註解是當時讀到的值)。只有第一條 rd 會寫 local v = ...,之後的 rd 寫成 v = ...(重新賦值)——Lua 一個函式裡最多只能宣告 200 個 local,錄很長的操作時每條都宣告會撞到上限 |
rd 10 4 | v = i2c.read(0x10, 4)(不是第一條 rd 時),註解列出讀到的 4 個位元組 |
bank 2 | i2c.bank(2) |
addr 58 | i2c.addr(0x58) |
bank reg F0 FF | i2c.bank_reg(0xF0, 0xFF) |
poll on / poll off | poll.on() / poll.off()(poll toggle 依切換之後的狀態錄成其中一個) |
watch 12 | watch.add(0x12) |
unwatch 12 | watch.remove(0x12) |
unwatch all | watch.clear() |
幾件事先講清楚:
- 數字一律寫成
0x開頭的十六進位,因為 Lua 裡的數字預設是十進位 (見I2C 讀寫最前面的提醒)。錄下來的檔案不會有「58變成十進位 58」這種陷阱。 rd只是把讀到的值記在註解裡,不做任何判定。重播時它只是再讀一次、什麼都不比。 想讓它變成「值不對就失敗」,見下面「把rd改成check」。- 位址只在變了才寫:每條指令實際送去的位址如果跟前一條不同(包括你用
[<位址>]臨時指定的),錄製器會先補一行i2c.addr(...);分頁裡有多個掛載時會帶上第幾個掛載的slot參數。你不用自己想「這條要不要重設位址」。 - 頁(Bank)會盡量還原:如果這個位址設定過頁碼暫存器,錄製器在你用
[<位址>]臨時改用別的位址、下一條又切回來時,會補回i2c.bank_reg(...)和i2c.bank(<頁碼>)(掛載本身沒換位址,頁碼設定原封不動),讓重播從全新狀態開始時每一條都落在 你當時所在的那一頁(見Bank 切換)。但你用addr指令真的換掉掛載位址就不同了: 程式會把該掛載的頁碼設定整個清掉,換回原位址也不會還原,錄製器因此也不會補回舊的頁碼設定。 位移超過0xFF的存取(例如wr 0110 01代表第 1 頁的0x10)會被拆成i2c.bank(1)、以0x10存取、再切回原本那一頁。
錄不進去的,會變成註解
錄製器不會悄悄丟掉任何一條它看到、卻翻不出來的指令——它們會以註解留在檔案裡,
你重播時不會執行,但讀檔案時一眼看得出「這裡少做了一件事」。其中開頭是 not recorded 的註解
(下表第二列起,FAILED 與 still pending 不算)第一次出現時,整次錄製會在 Log 裡記一則
WARN 提醒你,之後不再重複。
| 註解開頭 | 意思 |
|---|---|
-- FAILED (<原因>): <指令> | 指令送出了但失敗(例如裝置沒回應)。失敗的操作不會被當成成功錄下來 |
-- not recorded (no Lua equivalent): trigger ... | trigger、password <IC>、rd16/wr16、指定面板的 poll ... @p<N> 沒有對應的 Lua 寫法。password 只會寫 IC 名稱,絕不寫出密碼內容 |
-- not recorded (other tab): <指令> | 這條是在別的分頁下的。一支腳本只綁一個分頁(開始錄製時所在的那個),混進別的分頁的操作會讓重播作用在錯的匯流排上。例外:poll on / poll off 是全域的,從任何分頁下都照錄 |
-- not recorded (no Lua equivalent on 16-bit mount): <指令> | 掛載使用 16-bit 位址,手動指令與 Lua 的寫法在這種掛載上行為不同,照錄會悄悄改變硬體行為 |
-- not recorded (bank state unknown for offset above 0xFF): <指令> | 位移超過 0xFF 但程式不知道你原本在哪一頁,沒辦法保證錄出來的腳本會把頁切回去 |
-- not recorded (recorder queue full, order may differ): <指令> | 你連續送出的長時間指令太多,錄製器排不下;為了不寫出順序可能錯亂的腳本而放棄這一條(極少見) |
-- still pending at stop: <指令> | 你按 record stop 的時候,這條指令還在執行、沒有結果,所以不知道成敗 |
一支腳本裡有這些註解,代表重播出來的結果跟你當時手動做的不完全一樣。 重播前先搜尋一下
FAILED 與 not recorded,決定要不要手動補上。
檔頭與時間註解
錄出來的檔案最前面是三行說明:錄製時間、用哪個 dongle 與哪個分頁、以及重播用的指令樣板。
如果開始錄製的當下這個分頁已經設好位址,接下來會有 i2c.addr(...)(有頁碼暫存器的話還有
i2c.bank_reg(...)、i2c.bank(...)),把當時的狀態「拍一張快照」放在腳本開頭;這樣不論
你後面有沒有再下 addr,重播都是從你開始錄的那個狀態出發。
每條指令前面都有一行 -- +12.4s,是距離開始錄製過了幾秒。錄製器不會幫你插
delay_ms():你思考、打字的時間不是硬體需要的等待,把它照抄進去只會讓腳本白白慢下來。
如果某一步之間硬體真的需要等(例如寫了電源設定要等它穩定),自己在那裡加一行
delay_ms(...)(見等待與中止)。
檔尾是 -- End of recording: N commands;N 包含 FAILED、not recorded 這些註解條目。
所有註解都是英文 ASCII,不管你的系統語言為何。
範例:錄出來的樣子
下面是一次實際錄製的結果。當時下的指令是:
nexus_tui.exe --dongle mock --batch --cmd "record start C:\lua\record_demo.lua" --cmd "addr 58" --cmd "wr 10 01" --cmd "set 11 0C 0C" --cmd "clr 11 04" --cmd "rd 10" --cmd "record stop"(先說一件事:下面範例的 +2.1s 之類的時間是為了好讀手動改過的——用 --cmd 批次下指令時,
每條之間幾乎沒有間隔,時間幾乎都是 +0.0s。你手動輸入時看到的就是像這樣有間隔的數字。)
-- Recorded by Nexus 'record' on 2026-09-29 12:13:56
-- Dongle: mock Tab: 1
-- Run: nexus_tui.exe --dongle mock --batch --run <this file>
-- +2.1s
i2c.addr(0x58)
-- +5.8s
i2c.write(0x10, 0x01)
-- +9.4s
i2c.set(0x11, 0x0C)
-- +14.0s
i2c.clr(0x11, 0x04)
-- +17.6s
local v = i2c.read(0x10) -- recorded: 0x01
-- End of recording: 5 commands(這支腳本不印任何東西)
重播它:
nexus_tui.exe --dongle mock --batch --run C:\lua\record_demo.lua路徑一樣要用完整路徑,理由見「第一次跑腳本」。重播沒有任何輸出——錄下來的腳本只做動作,
不印東西,所以上面會註明「這支腳本不印任何東西」,只要 Exit Code 是 0 就代表整支腳本跑完、沒有出錯。
(腳本檔頭那行 -- Run: ... 是提示你怎麼重播;接真實硬體時,記得把 --dongle mock 換成真正的
dongle 名稱或整個拿掉,理由見整合進批次流程。)
把 rd 改成 check
rd 被錄成「讀一次、把當時的值寫在註解裡」,並沒有判定。錄完之後,你很可能想把其中幾個讀取
變成驗證:重播時值不對就標成失敗。這是一個手動的小改動:把註解裡的值抄成
check()(見判定與結束摘要):
local v = i2c.read(0x10) -- recorded: 0x01改成
local v = i2c.read(0x10)
check(v == 0x01, "register 0x10 reads back 0x01")改完的整支腳本:
-- Edited from record_demo.lua: the recorded 'rd' became a check().
i2c.addr(0x58)
i2c.write(0x10, 0x01)
i2c.set(0x11, 0x0C)
i2c.clr(0x11, 0x04)
local v = i2c.read(0x10)
check(v == 0x01, "register 0x10 reads back 0x01")CHECK OK: register 0x10 reads back 0x01這樣重播時會多出一行 CHECK OK:,數值不對則是 CHECK FAIL:,批次模式的 Exit Code 也會跟著變
(對照表見判定與結束摘要)。
小提醒
- 錄製只記你下的指令。監看(
watch)觸發時自動執行的動作、Plugin 管理器自己的安裝/移除, 都不會被錄進去;run執行腳本也不錄(腳本裡做的事你已經有腳本了)。 - 每一條指令要等完成才寫入檔案,所以檔案裡的順序就是你送出指令的順序; 一起送出多條耗時的指令時,寫檔仍然照你送出的先後。
- 錄好的檔案是普通的 Lua 檔,可以用任何編輯器打開、刪掉不要的行、加上
check()與delay_ms(), 再把它當成正式的測試腳本使用。
接下來
下一頁介紹 gpio.* 與 uart.*。