Learning Objectives
- 分辨兩種錯誤:儀器放進錯誤佇列的錯誤,與 PyVISA 在電腦端丟出的例外
- 用迴圈讀完錯誤佇列,看懂錯誤代碼的分類
- 用 try/except 接住逾時(VI_ERROR_TMO)與找不到儀器(VI_ERROR_RSRC_NFOUND),找出真正的原因
- 寫出一個檢查錯誤佇列的函數,並且在任何情況下都關閉輸出
自動量測程式一定會遇到錯誤
上一課的程式都是「一切順利」的版本。實際在實驗室跑一整晚的量測,一定會遇到:指令打錯、數值超出範圍、 儀器沒開機、GPIB 線鬆了、某一台儀器回應得比較慢。
這些錯誤分成兩種,處理方式完全不同:
| 儀器端的錯誤 | 電腦端的例外 | |
|---|---|---|
| 例子 | 數值超出範圍、指令拼錯、參數不合法 | 等不到回應(逾時)、找不到儀器 |
| 誰發現 | 儀器解析指令時 | PyVISA 在電腦上 |
| 程式會停嗎 | 不會,程式照樣往下跑 | 會,丟出 VisaIOError |
| 怎麼知道 | 主動用 SYST:ERR? 問 |
用 try/except 接住 |
第一種最危險:程式不會停,你以為設定成功了,其實儀器拒絕了那個指令。第二種會讓程式中止,如果剛好電源輸出還開著,就停在危險的狀態。
這一課把兩種都處理好。
本課的儀器是模擬的教學儀器(Uedu Sim);錯誤代碼採 SCPI 標準的寫法,各儀器實際支援的代碼與訊息以手冊為準。
錯誤佇列:先進先出,讀到 0 為止
儀器把每一個錯誤依發生的順序放進錯誤佇列(error queue)。SYST:ERR? 一次取出最早的一筆(先進先出),
佇列空了之後回傳 0,"No error"。所以要讀完整個佇列,就用迴圈一直問,直到代碼是 0。
回應的格式是「代碼,"說明"」。SCPI 的負數代碼依百位數分類:
| 代碼範圍 | 類別 | 常見例子 |
|---|---|---|
| -100~-199 | 指令錯誤:儀器看不懂 | -113,"Undefined header"(指令拼錯)、-104,"Data type error" |
| -200~-299 | 執行錯誤:看得懂但做不到 | -222,"Data out of range"、-224,"Illegal parameter value" |
| -300~-399 | 儀器本身的錯誤 | 自我測試失敗、佇列滿了 |
| -400~-499 | 查詢錯誤:問答的順序亂了 | -410,"Query INTERRUPTED"(下一課) |
正數代碼是廠商自訂的,查手冊。*CLS 會清空錯誤佇列,常放在程式開頭,讓之後讀到的錯誤都是這次程式造成的。
佇列的長度有限(例如 20 筆),滿了之後新的錯誤會被丟掉,這也是要經常讀它的原因。
逐步看:一次送出三個錯誤,再一筆一筆讀出來
看第 7~9 行:三個有問題的指令送出時,通訊紀錄只有「→」,沒有任何錯誤訊息,程式也照樣往下跑。
直到迴圈裡的 SYST:ERR?,錯誤才依發生順序一筆一筆出現。
逾時與找不到儀器:VisaIOError
query() 是「送出、然後等回答」。如果儀器沒有回答,PyVISA 不會永遠等下去:等了 timeout 毫秒(預設常是 2000)之後,
丟出 pyvisa.errors.VisaIOError,e.abbreviation 是 VI_ERROR_TMO。
儀器不回答,最常見的原因是你送的根本不是查詢:例如 MEAS:VOLT 少了問號。儀器把它當成一個(不存在的)設定指令,
放一筆 -113 到錯誤佇列,然後什麼都不回傳。所以遇到逾時,第一件事是去讀錯誤佇列,找出真正的原因。
其他常見的 VisaIOError:
| abbreviation | 意思 | 常見原因 |
|---|---|---|
VI_ERROR_TMO |
等回應逾時 | 指令不是查詢、終止字元設錯、儀器正在忙、量測時間比 timeout 長 |
VI_ERROR_RSRC_NFOUND |
找不到資源 | 資源字串打錯、GPIB 位址錯、儀器沒開機或沒接線 |
處理逾時的習慣:
- 用
timeout=設定合理的等待時間(毫秒)。太短會誤判,太長會讓程式卡很久。 - 逾時之後呼叫
inst.clear(),清掉介面上殘留的資料,讓下一次問答從乾淨的狀態開始。 - 不論成功或失敗,關閉輸出都放在
finally。
逐步看:少一個問號造成的逾時
看第 9 行:送出之後等了整整 1 秒(timeout=1000 毫秒)才丟出例外,程式跳到 except。通訊紀錄出現「×」那一筆。之後讀錯誤佇列,才知道真正的原因是 -113。
逐步看:把錯誤佇列變成例外
儀器端的錯誤不會讓程式停,這很危險。check_errors() 把它轉成電腦端的例外:讀完佇列,有錯誤就 raise RuntimeError。
每做完一組設定就呼叫一次。範例也示範了資源字串打錯時的 VI_ERROR_RSRC_NFOUND。
小測驗:錯誤佇列
依序送出 VOLT 50(超出範圍)與 VOLTT 5(拼錯)之後,第一次 SYST:ERR? 會讀到哪一筆?
下面的程式會印出什麼?
replies = ['-222,"Data out of range"', '-113,"Undefined header"', '0,"No error"']
codes = []
for r in replies:
code, msg = r.split(",", 1)
if int(code) == 0:
break
codes.append(int(code) // 100)
print(codes)
程式送出 CURR 9(超過 5 A 的上限),下面哪一個描述正確?
小測驗:例外與 finally
psu.query("OUTP ON") 會發生什麼事?
下面的程式會印出什麼?
class VisaIOError(Exception):
pass
log = []
def query(cmd):
if not cmd.endswith("?"):
raise VisaIOError("VI_ERROR_TMO")
return "5.0"
try:
log.append("ON")
v = float(query("MEAS:VOLT"))
log.append(v)
except VisaIOError as e:
log.append(str(e))
finally:
log.append("OFF")
print(log)
程式開啟 GPIB0::23::INSTR 時丟出 VI_ERROR_RSRC_NFOUND。最先該檢查什麼?
重點整理
- 儀器端的錯誤(-100 指令、-200 執行、-300 儀器、-400 查詢)只進錯誤佇列,程式不會停;用
SYST:ERR?讀到0,"No error"為止,*CLS清空。 - 電腦端的錯誤是
pyvisa.errors.VisaIOError:VI_ERROR_TMO(逾時)、VI_ERROR_RSRC_NFOUND(找不到儀器),用e.abbreviation判斷。 - 逾時最常見的原因是送出的不是查詢(少了
?);逾時後clear(),再讀錯誤佇列找原因。 - 把錯誤佇列包成
check_errors(),有錯就raise,讓「儀器拒絕了設定」變成看得見的例外。 - 關閉輸出永遠放在
finally。
下一課處理「等待」:儀器有些動作要花時間(自動設定、長時間量測),程式怎麼知道它做完了,以及問答順序亂掉時的 -410 錯誤。