Lua API 參考
本頁列出自動化腳本可用的每一個函式,每個函式一節:語法、參數範圍、回傳值、
會出錯的情況,以及一個最小範例。想看完整、可下載的範例,回頭看前面各頁的
<LuaExample> 區塊。
開放的標準函式庫
除了下列 Nexus 自有函式,腳本內也能使用這幾個 Lua 標準函式庫:
| 函式庫 | 說明 |
|---|---|
| base | 基本函式(type、pcall、tostring、pairs…);print 被覆寫為寫入 Log |
| table | table 操作(table.insert、table.remove…) |
| string | 字串處理(string.format、string.find…) |
| math | 數學函式 |
io、os、package、require 不開放——腳本不能讀寫任意檔案、呼叫系統指令,
或載入外部模組,見「腳本執行的機制」。
i2c
以下七個函式共通的 slot:省略時作用在這個分頁預設(第 0 個)掛載;帶
slot 時作用在同一個分頁裡、第 slot 個掛載(0~31)——這是「一個
分頁同時掛好幾顆 IC」的多掛載設定,跟指令列選分頁用的 @p<N> 是完全不同的
兩個軸:@p<N> 選的是分頁本身,slot 選的是這個分頁裡的第幾顆掛載。
offset 的合法範圍取決於這個掛載的暫存器位移定址寬度是 8-bit 還是
16-bit(設定掛載時決定,見指令參考的 rd16/wr16):8-bit
定址(多數 IC,也是預設值)時 offset 不能超過 0xFF;16-bit 定址時可以到
0xFFFF。這跟 Slave 位址是 7-bit 還是 8-bit 格式是兩件不相干的事(後者
見常見問題),不要搞混。
i2c.read(offset [, len [, slot]])
local v = i2c.read(0x10) -- 讀一個位元組,回傳一個數字
local t = i2c.read(0x10, 4) -- 讀 4 個位元組,回傳 table:t[1]~t[4]offset:0~65535(8-bit 定址的掛載上限是0xFF,見上一段)。len(可省略,預設1):1~256。- 省略
len(或給1)時回傳單一數字;給了其他len時回傳一個1-based 的 table。 - 出錯情況:
offset/len/slot不是整數或超出範圍;讀取本身失敗(裝置 沒回應等)。
i2c.write(offset, value_or_table [, slot])
i2c.write(0x10, 0xAB) -- 寫一個位元組
i2c.write(0x10, {1, 2, 3, 4}) -- 用 table 一次連續寫 4 個位元組offset:同i2c.read。- 單一寫入值:
0~255。 - table 形式:長度
1256,每個元素各自0255——驗證會先掃過整個 table 再開始寫,某個元素超出範圍時完全不會寫入任何位元組,不會有「寫了 一半」的情況。 - 出錯情況:任一數值不是整數或超出範圍;寫入本身失敗。
i2c.set(offset, mask [, slot])
i2c.set(0x10, 0x80) -- 把 0x10 這個位元組的第 7 個位元設為 1,其餘位元不變先讀出目前的值、只把 mask 裡是 1 的位元設成 1、再寫回去(讀改寫)。
mask:0~255。出錯情況同 i2c.write。
i2c.clr(offset, mask [, slot])
i2c.clr(0x10, 0x80) -- 把 0x10 這個位元組的第 7 個位元清為 0,其餘位元不變跟 i2c.set 相反:把 mask 裡是 1 的位元清成 0,其餘位元不變。mask:
0~255。出錯情況同 i2c.write。
i2c.bank(n [, slot])
i2c.bank(2) -- 切到第 2 頁:把 2 寫進頁碼暫存器切換到第 n 頁(Bank),之後同一個位移存取的是該頁的內容;細節見
Bank 切換。n:0~255。這個掛載如果沒有設定過頁碼暫存器
(沒呼叫過 i2c.bank_reg(),也沒下過 bank reg <位移> <遮罩>),呼叫會直接
出錯(bank not supported for this tab),不會嘗試猜一個。
i2c.addr(addr [, slot])
i2c.addr(0x58) -- 把這個掛載接下來要對話的位址設成 0x58
i2c.addr(0x58, 1) -- 只改第 1 個掛載槽的位址,其餘掛載槽不受影響addr:0x01~0x7F(0保留給「未設定」的哨兵值,不能拿來當合法位址)。- 沒有回傳值。
- 只在本次腳本執行期間有效:腳本結束時(不論成功、失敗、
fail()、發生 錯誤或被中止)一律自動改回腳本開始前的位址,不會寫進config.ini,也 不影響你在腳本外用指令列或畫面設定的位址。 - ⚠ 順序有影響:如果同一個掛載槽已經先用
i2c.bank_reg()設定好頁碼 暫存器,之後才呼叫i2c.addr()換位址,頁碼暫存器設定會被清掉(跟畫面上 先bank reg、後addr的行為一樣)——要用 Bank,請先i2c.addr()、再i2c.bank_reg(),見Bank 切換。 - 出錯情況:
addr不是整數、是0、或超過0x7F;slot未設定(見本節 開頭的共通說明);寫入本身失敗。
i2c.bank_reg(reg, mask [, slot])
i2c.bank_reg(0xF0, 0xFF) -- 位移 0xF0 是頁碼暫存器,遮罩 0xFF 代表整個位元組都是頁碼reg:0x000xFF。mask:0x010xFF(0保留給「未設定」的哨兵值, 不能拿來當合法遮罩)。- 沒有回傳值。效果等同
bank reg <reg> <mask>指令,設定好之後i2c.bank()/dump_all()才能用。 - 只在本次腳本執行期間有效,還原規則同上一節的
i2c.addr()。 - ⚠ 這個掛載實際停在哪一頁不會被自動還原:腳本結束只會把「頁碼暫存器的
位移/遮罩設定」還原成腳本開始前的樣子,不會把晶片實際切回原本那一頁。腳本
如果用
i2c.bank()切換過頁,收尾前自己呼叫i2c.bank(0)(或換回原本那 一頁),見Bank 切換。 - 出錯情況:
reg/mask不是整數或超出範圍、mask為0;slot未設定; 寫入本身失敗。
i2c.recover()
local pins = i2c.recover() -- 3 表示 SCL 與 SDA 兩條線都已放開要求裝置嘗試恢復被卡住的 I2C 匯流排,回傳腳位狀態遮罩。回傳不代表線已放開:
只有 pins == 3(兩條線都是高電位)才表示恢復成功,其他值代表仍有線被拉住,
請自己檢查。只作用在這支腳本自己的匯流排;裝置不支援時丟錯。
uart
三個函式共通的 panel:省略時,只有這個分頁剛好開了一個 UART 面板才會自動
選它;開了不只一個或一個都沒開,必須自己指定,否則出錯。panel 範圍
0~7。模擬後端(mock)沒有任何 UART 裝置可以回話,這三個函式實際上
需要真實硬體才能跑,見GPIO 與 UART。
⚠ uart.read/uart.expect 只看得到「這支腳本本次執行、第一次呼叫任何
一個 uart.* 之後」才到達的資料——執行前已經在面板上的舊資料,不算「已經
到的」。逾時都回傳 nil(不算錯誤);但如果該看的那幾行在你來得及讀之前就被
覆寫掉,兩者都會出錯("uart: unread lines on panel <N> were lost (ring overwritten)"),提醒你讀得不夠快、該讀的資料已經真的遺失。
uart.write(str [, panel])
uart.write("PING\n")原樣送出字串(二進位安全,不會自動加換行)。str 長度 1~256 個位元組。
出錯情況:空字串、超過 256 位元組、對應的面板沒開、佇列滿逾時(1 秒)。
uart.read(timeout_ms [, panel])
local line = uart.read(2000) -- 最多等 2000ms;等到就回傳字串,逾時回傳 nil在時限內等待並回傳下一行收到的資料(見上面的游標範圍限制);逾時回傳 nil
(不算錯誤)。timeout_ms 必須是非負數;0 代表「不等待,只看游標範圍內
現在已經到的」。出錯情況:對應的面板沒開;ring overwritten(見上面)。
uart.expect(pattern, timeout_ms [, panel])
local line, capture = uart.expect("^PONG (%d+)", 2000) -- 逾時一樣回傳 nil在時限內等待符合 pattern(Lua pattern,跟 string.find 同一種語法)的一行;
逾時回傳 nil。等到時的回傳值不只整行內容——第一個回傳值永遠是整行,後面
接著的是 pattern 裡每一組括號 (...) 各自比對到的子字串(跟
string.find 的 captures 是同一回事);pattern 沒有括號就只回傳整行。
出錯情況同 uart.read。
gpio
三個函式共通的 pin:031;**模擬後端只會回應腳位 03**,其餘腳位在
mock 上會失敗。真實硬體支援的腳位數依接的裝置而定。
gpio.write(pin, v)
gpio.write(0, true) -- 設為輸出並寫入高電位
gpio.write(0, 0) -- 數字也可以:非 0 是高、0 是低把指定腳位設為輸出並寫入電位;v 可以是布林值,也可以是數字。出錯情況:
pin 超出範圍、腳位已被別的用途(其他掛載/Plugin)佔用、硬體交易本身失敗。
gpio.read(pin)
local v = gpio.read(0) -- 回傳 0 或 1讀回指定腳位目前的電位,回傳 0 或 1。如果這支腳本已經用 gpio.write()
把腳位設為輸出,gpio.read() 讀的是目前輸出的鎖存值,不需要先切回輸入。
gpio.release(pin)
gpio.release(0)釋放腳位的持有權,交還給其他使用者。就算不呼叫,腳本結束時(不論結果如何) 程式都會自動收回這支腳本佔用的全部腳位。
gpio.pulse(pin, level, width_us)
gpio.write(1, 0)
local w = gpio.pulse(1, 1, 2000) -- 在腳位 1 輸出 2000 微秒的高電位脈衝讓裝置自己在指定腳位輸出一個精確寬度的脈衝,回傳實際寬度(微秒)。level 是脈衝
期間的電位(布林或數字,規則同 gpio.write()),width_us 為 1~1000000。
腳位要先設成輸出(例如先 gpio.write());裝置不支援脈衝或寬度超出該裝置的範圍時
丟錯。脈衝進行中腳本無法被中止,中止會在函式返回後生效。
dongle
dongle.op(name[, args])
local r = dongle.op("wave", {scl_hz = 100000, sda_hz = 100000})
print(r.tick_hz)
local p = dongle.op("pin_mode", {2, "gpio_out"}) -- 位置參數執行裝置自己宣告的廠商操作(dongle list 可看到有哪些,dongle help <名稱> 看參數)。
args 是一張表:整數鍵 1、2、… 是依宣告順序的位置參數,字串鍵是具名參數
(位置參數要在前);值可以是數字、布林或字串,省略的參數取宣告的預設值。回傳一張
結果表,鍵是結果欄位名稱:數字型別是數字,bool 是布林,enum 是標籤字串(遇到
目錄沒有的值則是數字),hex 是小寫十六進位字串;沒有結果欄位的操作回傳空表。
出錯情況:裝置沒有目錄、沒有這個操作、參數不合目錄宣告(訊息含參數名稱與原因)、
裝置回傳的位元組與目錄不符、硬體交易本身失敗。只作用在這支腳本自己的匯流排。
int
裝置的 INT 輸入腳位(邊緣偵測)。腳本結束時,這支腳本設定的 INT 會自動還原, 不影響 Plugin 或指令列設定的同一支腳位。
int.config(edge[, pull])
int.config("falling", "up") -- 偵測下降沿,內部上拉
int.config("none") -- 提早釋放這支腳本的設定edge 是 "falling"、"rising"、"both" 或 "none";pull 是 "up"、
"down" 或 "none"(預設)。多個使用者(腳本、Plugin、指令列)同時設定時,
實際套用的是合併結果:邊緣取聯集,上下拉取第一個非 "none" 者。
int.status()
local s = int.status()
print(s.edge, s.pull, s.level, s.count, s.dropped, s.last_t_us)回傳一張表:目前套用的 edge/pull(字串)、腳位電位 level(0/1)、
累計邊緣數 count、累計丟棄數 dropped、最近一次邊緣的裝置時間 last_t_us(微秒,
會繞回)。
int.events([max])
local ev, dropped = int.events()
for _, e in ipairs(ev) do print(e.t_us, e.count, e.level) end取走這支腳本自己的游標之後的事件(從 int.config() 之後發生的邊緣起算),不等待。回傳兩個值:事件陣列(每筆有 t_us、
count、level)與這段期間的丟棄數 dropped。max 預設 64、最多 256。
腳本執行期間背景輪詢不會運作,所以每次呼叫都會先向裝置取一次最新事件。
int.clear()
int.clear()清除裝置與程式端的 INT 計數和事件。
poll
這兩個函式跟畫面上手動下 poll on/poll off 指令是同一個開關——作用是
整個程式,不是只有這支腳本,而且不會隨腳本結束自動復原。⚠ 這兩個函式
在腳本執行期間沒有任何可觀察的效果:背景輪詢本來就不會在腳本執行期間運作,
跟這個開關是開是關無關;它們唯一有意義的效果,是腳本結束後背景輪詢是開是關。
細節見輪詢與監看。
poll.on()
poll.on()恢復背景輪詢。無參數,不會失敗。
poll.off()
poll.off()暫停背景輪詢。無參數,不會失敗。⚠ 沒有自動配對的 poll.on():腳本若在關閉
之後才出錯或被中止,輪詢會維持關閉,要再手動或用下一支腳本開回來。
watch
watch.add(offset [, slot])
watch.add(0x40) -- 加進目前分頁的監看清單
watch.add(0x40, 1) -- 加進「掛載槽 1」自己的監看清單把一個位移加入監看清單。offset:065535;slot(可省略):031。
⚠ 清單已滿、或加入的 (offset, slot) 組合已經存在,都不會讓腳本出錯,
只在 Log 留一筆 WARN,呼叫完了但清單裡好像沒東西,記得去 Log 找原因。
- 出錯情況:
offset/slot不是整數或超出範圍;帶非0的slot時,那個 掛載槽必須已經設定過(掛載或i2c.addr()),否則直接讓腳本出錯並中止。
watch.remove(offset [, slot])
watch.remove(0x40) -- 從目前分頁的監看清單移除
watch.remove(0x40, 1) -- 從「掛載槽 1」自己的監看清單移除把一個位移從監看清單移除。offset:065535;slot(可省略):031,
語意與 watch.add(offset, slot) 對稱——省略 slot 只作用在第 0 個掛載自己的
清單;帶 slot 只移除那個掛載自己的項目。清單裡沒有這一筆(offset/slot
不對,但那個掛載槽本身有設定過)安靜地什麼都不做,不會讓腳本出錯。
- 出錯情況:
offset/slot不是整數或超出範圍;帶非0的slot時,那個 掛載槽必須已經設定過,否則直接讓腳本出錯並中止(與watch.add()同一個 判準——未設定的掛載槽不會被靜默當成「沒有這一筆」放行)。
watch.clear()
watch.clear()清空目前分頁的監看清單(含全部掛載槽的項目),不需要參數,不會失敗。
ic
ic.name()
local name = ic.name() -- 沒有掛載 Plugin 時是空字串 ""回傳目前分頁掛載的 IC/Plugin 名稱(字串);沒有掛載任何東西時是空字串
"",不是特地的一個詞——只有在連掛載設定本身都讀不到(幾乎不會發生)這種
內部異常情況,才會退回固定字串 "none"。判斷「有沒有掛載」請比對空字串,
不要比對 "none"。
ic.addr()
local addr = ic.addr() -- 沒有掛載時是 0回傳目前分頁使用的 7-bit Slave 位址(0~0x7F 之間的數字);沒有掛載時
回傳 0。
file
file.append(name, str)
file.append("result.txt", "run 1: PASS\n")把字串附加寫入目前資料夾(執行時的工作目錄)底下 logs/ 資料夾裡的一個
檔案;檔案不存在時自動建立,logs/ 資料夾不存在也會自動建立。name 限定
英數字、.、_、-,長度 1~64 個字元,不能以 . 開頭或結尾,也不能是
Windows 保留檔名(CON、PRN、COM1…)。str 二進位安全,原樣寫入。
出錯情況:檔名不合法、目錄/檔案無法開啟或寫入失敗。
全域函式
log(level, message)
log("info", "hello from Lua")把一行訊息寫進 Log;level 可為 info、warn、error、debug、trace、
ic,大小寫需完全相符。出錯情況:level 不是這六個字串之一("invalid log level")。
check(cond [, msg])
check(1 + 1 == 2, "1 + 1 == 2")記錄一次判定:cond 成立寫 CHECK OK: <msg>,不成立寫 CHECK FAIL: <msg>
並讓失敗計數加一。不論成功或失敗都會繼續往下執行。msg 省略時固定為
"check"。回傳 cond 本身,可以寫成 if check(x, "...") then。細節見
判定與結束摘要。
fail([msg])
fail("device did not respond")記錄一次判定失敗並立即結束腳本,後面的程式碼不會再執行。msg 省略時固定為
"fail()"。
print(…)
print("value =", 42)把每個引數轉成字串、以定位字元(Tab)相接後寫進 Log(等同 log("info", ...))。
delay_ms(ms)
delay_ms(50) -- 暫停 50 毫秒暫停指定的毫秒數,且能被使用者中止(Esc/Ctrl+C,見等待與中止)。
ms 必須是非負數;負數會出錯。
dump([slot])
local t = dump() -- 印出並回傳目前分頁 256 個位元組的內容讀出並列印 256 個位元組的暫存器表,同時把內容回傳成一個 t[1]t[256] 的
table。slot(可省略):031;帶非 0 的 slot 時,那個掛載槽必須已經
設定過,否則出錯。
dump_all([slot [, count]])
local path = dump_all() -- 依 Bank 逐頁傾印,回傳寫出的檔案路徑
local path2 = dump_all(0, 4) -- 顯式指定傾印 4 頁(遮罩推不出頁數時尤其需要)依 Bank 逐頁傾印暫存器內容,寫成**目前資料夾(執行時的工作目錄)**底下
logs/ 資料夾裡的一個檔案(檔名帶執行當下的時間戳記),回傳寫出的檔案
相對路徑(logs/dump_...txt 這種形式,不是絕對路徑)。slot(可省略,
預設 0):031;count(可省略):整數 1256,顯式指定要傾印
幾頁,蓋過依遮罩推算出的頁數。
- 這個掛載沒設定過頁碼暫存器時(沒呼叫過
i2c.bank_reg(),也沒下過bank reg):省略count(或給1)照樣成功,只當成「這顆 IC 沒有 Bank」傾印目前這一頁(1 頁)。給大於1的count會出錯,不是靜默 只傾印 1 頁卻回報成功——沒有頁碼暫存器就沒有「第 2 頁」可以切換,回報 成功但實際只寫了一部分頁數會讓你誤以為拿到了完整的count頁。 - 設定過頁碼暫存器、遮罩小於
0xFF:頁數由遮罩推算(例如遮罩0x03代表 2 個位元、共 4 頁),省略count就用這個推算值;顯式給count則 蓋過推算值,但不能超過遮罩允許的頁數上限,超過會出錯。 - 設定過頁碼暫存器、但遮罩是
0xFF(等於「整個位元組都是頁碼,範圍 沒收窄」,例如Bank 切換教的i2c.bank_reg(0xF0, 0xFF)): 單看遮罩本身無法推算共有幾頁,這時必須顯式給count(例如dump_all(0, 256)傾印滿 256 頁,或依你實際的硬體規格給一個更小的數字) ——省略count仍然出錯("cannot infer bank count (mask is 0xFF)")。 count本身不是整數、小於1或大於256,一律出錯。- 部分頁面讀取失敗時,已完成的部分仍會被寫進檔案,但這次呼叫本身會回報 錯誤(訊息含檔案路徑)。
細節見記錄與傾印。
timestamp()
local ts = timestamp() -- 例如 "20260928_221030"回傳目前時間,格式為 YYYYMMDD_HHMMSS(固定 15 個字元)。
dofile(path)
dofile("helpers.lua")載入並執行另一個 Lua 檔,回傳該檔的回傳值。路徑可含中文等非 ASCII 字元;檔案不存在或有語法錯誤時丟出錯誤,腳本因此結束。
loadfile(path)
local f, err = loadfile("helpers.lua")
if f then f() end只載入、不執行:成功回傳編譯好的函式,失敗回傳 nil 與錯誤訊息(不丟錯誤)。路徑規則同 dofile。
bit
bit.* 系列函式一律把數字當成剛好 32 個位元處理。
bit.tobit(x)
bit.tobit(0x100000001) -- 1:超過 32 位元的部分被丟掉把數字轉成有號 32-bit 整數(超過 32 位元的高位直接捨棄)。
bit.tohex(x [, n])
bit.tohex(0xAB, -2) -- "AB"把數字轉成十六進位字串;n 為負值時輸出大寫,數字大小代表要印幾位。
bit.bnot(x)
bit.bnot(0) -- -1位元反相(NOT)。結果可能是負數,細節與原因見位元運算的 「為什麼結果是負數」一節。
bit.band(x1 [, x2…])
bit.band(0xFF, 0x0F) -- 0x0F:只留下低 4 位位元 AND(可以傳多個引數,逐一相 AND)。
bit.bor(x1 [, x2…])
bit.bor(0x10, 0x01) -- 0x11位元 OR(可以傳多個引數,逐一相 OR)。
bit.bxor(x1 [, x2…])
bit.bxor(0xFF, 0x0F) -- 0xF0:翻轉低 4 位位元 XOR(可以傳多個引數,逐一相 XOR)。
bit.lshift(x, n)
bit.lshift(1, 4) -- 0x10邏輯左移 n 格,左端溢出的位元直接捨棄,右端補 0。n 通常是 0~31。
bit.rshift(x, n)
bit.rshift(0x80, 4) -- 0x08邏輯右移 n 格,左端補 0。常搭配 bit.band 用來讀出某個欄位,見
位元運算。
bit.arshift(x, n)
bit.arshift(-8, 1) -- -4算術右移,左端補符號位(與 bit.rshift 的差別只在左端補的是 0 還是符號
位)。
bit.rol(x, n)
bit.tohex(bit.rol(0x12345678, 8)) -- "34567812"位元循環左移:從左端溢出的位元繞回右端補上,不會丟失任何位元。
bit.ror(x, n)
bit.tohex(bit.ror(0x12345678, 8)) -- "78123456"位元循環右移:從右端溢出的位元繞回左端補上,不會丟失任何位元。
bit.bswap(x)
bit.tohex(bit.bswap(0x12345678)) -- "78563412"交換 4 個位元組的順序(大小端互換)。