简介:泰克 TSP Toolkit 官方快速入门指南,以 PDF 形式呈现,专为从事电子测试与测量的工程师、技术员和开发者编写。指南详细讲解如何安装和使用基于 Visual Studio Code 的开源扩展,在支持 TSP 技术的泰克仪器上编辑、运行与调试 .tsp 脚本,解决仪器控制与自动化测试中脚本开发效率低、排错难的问题。内容由浅入深:从扩展安装、工作区配置、仪器连接,到项目设置、脚本运行与调试、终端使用,再到自动化脚本生成和下载示例脚本,每一步均有操作指引。工具内建语法检测、代码补全、悬停帮助与命令集文档,可显著降低入门门槛。资源包为单个 PDF 文件,大小约 1.55MB,轻量便于随时查阅。目前已有 260 人学习下载。文档特别强调了仪器连接方式和项目配置方法,并配合 GitHub 示例脚本,可帮助读者快速搭建测试环境、定位脚本问题,加速 IV 测试等常用自动化流程的落地。无论你是初次接触 TSP 脚本,还是希望优化既有测试程序,这份指南都能帮助你快速上手并减少试错成本。
1. 从逐条SCPI到仪器端TSP:这套VS Code脚本工具包改变了什么
做过源表自动化的人,应该都体会过这条链路:上位机循环里拼SCPI命令,逐条发出去,再逐条等回应。测量一万个点,就意味着至少一万次网络往返,调度一忙数据就堵在缓冲区里。基于Visual Studio Code 的 TSP 脚本开发工具包要解决的,正是这个「上位机太累、仪器太闲」的错位。TSP 是仪器内置的脚本处理器,复用 Lua 语法,让循环、判断、数据归档直接在仪表端完成。
这套工具包对三类人最有价值:要跑大批量 IV 扫描或脉冲序列的测试工程师;想把产线测试写成可版本管理的脚本、而不是一长串记事本命令的自动化开发;以及刚拿到支持 TSP 的源表、想用最短路径建立本地开发环境的初学者。它解决的痛点很明确:在 VS Code 里获得语法高亮、自动补全和代码片段,在需要时把脚本完整下发到仪器,再用回传的 print 输出做交互式调试。
别把 TSP 当成新一套 SCPI 替代品,它真正的改变是执行位置。理解这一点以后,环境配置和踩坑排查才有依据。
2. TSP脚本模型先立住:仪器里的Lua解释器与你手里编辑器各管哪一段
2.1 慢网络下的快测量:为什么TSP把循环放进仪器而不是上位机
SCPI 的核心单位是命令,命令之间天然有网络往返。TSP 的核心单位是脚本,脚本可以在仪器内部独立跑完一整段流程。源表类仪器通常内置 Lua 解释器,你下发的不再是零零碎碎的单条指令,而是一个带变量、循环、函数调用的完整程序。这个设计最直接的好处有两个:一是大幅减少上位机与仪器之间的通信次数,二是让时间敏感的操作不再依赖上位机调度精度。
| 维度 | SCPI 逐条下发 | TSP 脚本下放 |
|---|---|---|
| 基本单位 | 单条命令 | 完整 Lua 程序块 |
| 控制流 | 上位机负责 | 仪器端负责 |
| 循环效率 | 每条循环都走一次网络 | 循环在仪器内执行 |
| 数据回传 | 每次查询返回一次结果 | 脚本内 print 统一回传 |
| 适用场景 | 少量手动验证 | 批量扫描、产线自动化 |
我做自动化测试时有个体会:当测量点数从几十涨到几千,SCPI 的瓶颈会非常明显,界面像卡死一样,仪器却在那边空转等待。换成 TSP 之后,同样的业务流程在仪器端跑完,网络只承载最终结果和必要的调试输出,整个流程干净很多。
2.2 loadscript 到底发生了什么:下发、驻留与执行的边界
直接往 TSP 仪器的远程终端里粘贴下面这段,可以看到脚本的完整生命周期:
loadscript stress_scan for i = 1, 5 do -- 这里的逻辑由仪器端 Lua 解释器逐行执行 print("step", i) waitcomplete() end endscript stress_scan()这段代码的关键在loadscript与endscript这对命令。loadscript后面跟着的stress_scan是脚本名,中间所有内容会被仪器的脚本管理器捕获为一段可调用的程序;endscript表示定义结束。你看到stress_scan()在最后单独出现,是因为前面只是定义,真正执行还要靠这次调用。如果不调用,仪器什么也不会发生。
这里就是VS Code工具包介入的地方了:编辑器负责 Lua 语法与逻辑的静态检查,而loadscript这类外壳命令更适合交给下发脚本自动处理。我一般不在源文件里混写loadscript,源文件保持纯 Lua,工具包封装好发送过程,这样语言服务器可以稳定工作。
2.3 调试信息怎么回传:print、waitcomplete 与远端缓冲
TSP 脚本执行时,仪器端唯一稳定的回传通道是print。print的内容会进入仪器远程会话的输出缓冲,上位机通过 VISA 的 read 接口逐行读取。和桌面程序不一样,TSP 里没有打断点看变量的交互界面,调试全靠把关键状态用print打出来。我的习惯是在每个阶段切换后打一行状态标记,这样即使脚本跑挂了,也能源头定位到最后成功的行。
waitcomplete()是另一个必须理解透的同步点。TSP 仪器内部的测量操作经常进入异步队列,命令已经发出,但数据还没准备好。waitcomplete()会阻塞脚本执行,直到队列里的操作全部完成。它在 TSP 脚本里的作用相当于给数据流装了一道闸门,保证后面读到的数据是当前这一轮的,而不是上一轮的残留。
2.4 编辑端凭什么选 VS Code:跨平台、Git 与任务机制
TSP 脚本本质是文本,编辑器的首要任务是语法正确和可追溯。VS Code 的优势不在仪器通信,而在工程化:跨平台一致体验、内置 Git 客户端、任务自动化和代码片段管理。你不需要引入庞大的 IDE,装一个 Lua 语言服务器,把.tsp关联到 Lua,就能获得补全和语法诊断。搭配一个 Python 下发脚本,按Ctrl+Shift+B就能把当前文件送到仪器执行。这套组合轻量,也适合放进产线自动化框架里统一管理。
3. 用VS Code把最小TSP脚本跑通:语言服务、工作区与一键下发
3.1 让编辑器认识TSP脚本:Lua语言服务器与API声明文件
第一步是把 VS Code 配成一个能看懂 TSP 的编辑器。我常用的做法是安装 Lua Language Server,再把.tsp后缀关联到 Lua 语言。这样打开脚本文件时,高亮、括号匹配、语法检查都是现成的。工作区创建.vscode/settings.json,写入如下配置:
{ "files.associations": { "*.tsp": "lua" }, "Lua.runtime.version": "Lua 5.3", "Lua.workspace.library": [ "lib" ], "Lua.diagnostics.globals": [ "smua", "waitcomplete", "loadscript", "endscript" ] }这里把Lua.runtime.version设为Lua 5.3,是考虑到 TSP 仪器内置解释器普遍基于 Lua 5.0/5.1 分支,5.3 的诊断能力覆盖大部分语法,又不至于启用太新的语言特性。Lua.workspace.library指向工作区下的lib目录,这个目录里放一个 TSP 全局 API 声明文件,语言服务器读取后就能识别smua、waitcomplete这些仪器端才存在的全局符号。
在lib/tsp-globals.lua里我一般放这些声明:
-- 以下是 TSP 全局 API 的本地补全声明,不会发送到仪器 smua = {} smua.source = {} smua.measure = {} script = {} function script.list() end function waitcomplete() end function loadscript(name) end function endscript() end这些空函数和空表只是用来告诉语言服务器「这些符号是存在的」。这样你在.tsp文件里写smua.source.levelv时,不会因为全局变量未定义而满屏红色波浪线。声明文件本身不参与下发,它是纯本地的编辑器辅助资源,这也是工具包和普通文本编辑的本质区别。
3.2 最小TSP脚本文件:从编辑到代码结构
工作区跑通以后,新建一个hello.tsp,先不要写任何loadscript外壳,只写纯 Lua 逻辑:
-- hello.tsp:最小可用 TSP 脚本 local sum = 0 for i = 1, 4 do sum = sum + i -- 仪器端会把这行内容回传给上位机 print("i=", i, "sum=", sum) end print("final", sum)这段脚本做的事很简单:循环累加,边加边用print回传状态。它验证的是三件事:编辑器能正常解析 Lua 语法;Python 下发通道能正确传文件;仪器端能执行循环并回传输出。先跑通这条链路,再往上加源表测量逻辑会从容很多。
在源文件里保持纯 Lua 还有一个隐性好处:当你需要把一个脚本迁移到另一台 TSP 仪器,或交给同事复用,文件内容不掺杂外壳命令,语义清晰。loadscript外壳由发送工具自动补,后续改脚本名也只要改发送参数。
3.3 一键下发:用任务和Python把当前文件送到仪器
下发工具我用的是 PyVISA,它提供跨平台 VISA 资源访问,代码短且容易集成。先安装依赖:
pip install pyvisa pyvisa-py网络连接一般用 pyvisa-py 就能顶住,USB 连接则建议搭配厂商提供的 VISA 运行时。接着在工作区根目录创建tools/send_tsp.py:
import sys import pyvisa def send_file(addr, file_path): rm = pyvisa.ResourceManager() inst = rm.open_resource(addr) inst.timeout = 30000 # 毫秒,长测试时必须加大 inst.write_termination = "\n" inst.read_termination = "\n" with open(file_path, "r", encoding="utf-8") as f: source = f.read() source = source.replace("\r\n", "\n") if "loadscript" not in source: # 自动包一层具名脚本,并在末尾执行 source = "loadscript devscript\n" + source + "\nendscript\ndevscript()\n" inst.write(source.rstrip("\n") + '\nprint("__TS_DONE__")\n') try: while True: line = inst.read() if line.strip() == "__TS_DONE__": break print("TSP:", line) except pyvisa.errors.VisaIOError: print("-- script done --") finally: inst.close() if __name__ == "__main__": send_file(sys.argv[1], sys.argv[2])这个脚本的逻辑分四步:打开 VISA 资源并设置超时和换行符;读取.tsp文件内容;判断是否需要自动包loadscript外壳;写入仪器并循环读取print回传。__TS_DONE__是哨兵标记,读到它就认为脚本执行完毕,不至于每次都等满超时时间。
inst.timeout的桌面单位是毫秒。批量扫描时如果预期一轮测试要 60 秒,这里至少设成 90000,否则脚本还没跑完,上位机先放弃等待了。write_termination和read_termination统一成\n,是为了避开 Windows 下\r\n引起的解析错乱。调用方式举例如下:
python tools/send_tsp.py "TCPIP0::192.168.1.108::inst0::INSTR" hello.tsp然后在.vscode/tasks.json里注册一个默认构建任务,让命令可以在编辑器里直接触发:
{ "version": "2.0.0", "tasks": [ { "label": "send-tsp", "type": "shell", "command": "python", "args": [ "${workspaceFolder}/tools/send_tsp.py", "TCPIP0::192.168.1.108::inst0::INSTR", "${file}" ], "group": { "kind": "build", "isDefault": true }, "problemMatcher": [] } ] }按下Ctrl+Shift+B,当前打开的.tsp文件就会被下发并执行。Windows 上如果python命令不可用,把command改成py -3。这套任务机制的好处是,团队里任何人拿到工作区,按下同一个快捷键就能跑同一段下发流程,不依赖记忆菜单操作。
3.4 首次连接验证:先别跑业务逻辑
第一次连仪器,我建议先做一次最小回读验证,确认链路通、地址对、换行符匹配,再跑真正的测量脚本。
python -c "import pyvisa; rm=pyvisa.ResourceManager(); inst=rm.open_resource('TCPIP0::192.168.1.108::inst0::INSTR'); print(inst.query('print(100)'))"这里query('print(100)')发过去的是 TSP 命令,仪器执行后会回传100。如果能看到100,说明 VISA 资源名和网络配置没问题。如果超时,先确认上位机到仪器的网络能 Ping 通,再把资源字符串里的inst0和INSTR换成仪器实际支持的接口名。这一步能隔离出「网络问题」和「脚本问题」,后面调试会省很多时间。
4. 调试TSP脚本时最该较真的三件事:等待完成、数据回传与脚本驻留
4.1 同步问题:为什么waitcomplete不能省
TSP 脚本在仪器端虽然是顺序执行的,但源表类仪器内部存在异步队列。一条测量命令发出后,数据采集可能还在进行中,下一条脚本语句却已经踩上来了。这是我见过最多的翻车点:明明读到了值,却是上一轮或旧状态的残留。
-- 开启输出并设定电压 smua.source.output = smua.OUTPUT_ON smua.source.levelv = 1.0 -- 触发一次测量 smua.measure.iv() -- 等队列排空再继续 waitcomplete() print("channel ready")这里waitcomplete()是 TSP 里专门用来同步的挂起函数。它不接收业务参数,只负责阻塞脚本,直到之前排入的异步操作全部完成。测量、量程切换、输出电压变更这类操作之后,如果后续语句依赖操作结果,都应该补一个waitcomplete()。无脑在每个阶段之间塞一个,代价是几次毫秒级延迟,但能避免一大批难以复现的诡异数据问题。
4.2 print 回传数据怎么解析:缓冲、换行与超时
脚本下发后,仪器端print产生的文本会依次进入远程会话的缓冲区。上位机读取时要注意两个匹配:读终止符要和仪器输出一致;超时设置要大于脚本的最长执行时间。我的工具使用read_termination="\n",是因为 TSP 的 print 默认以换行结束每条记录。
读回传数据时有一个常见误区:以为脚本执行完,所有 print 就立即可以读。实际上仪器是执行一行、输出一行,上位机必须循环 read,直到遇见约定的结束哨兵。这也是我在下发脚本末尾统一加__TS_DONE__的原因,避免通过猜「等多久」来判定脚本是否结束。
while True: line = inst.read() if line.strip() == "__TS_DONE__": break print("TSP:", line)如果你要在回传数据里提取数值,别直接用字符串分割硬编下标。建议每一行 print 固定格式,比如用key=value的键值结构,上位机用split("=")解析。这样即使字段顺序调整,解析逻辑也不容易崩。
4.3 脚本驻留的坑:内存里的那半片脚本
loadscript创建的脚本驻留在仪器内存中,不会自动写入持久存储。仪器重启后,这些脚本会消失。这不算 bug,但工程上一旦脚本内部还引用了另一段脚本,就会出现重启后「函数找不到」的连锁报错。我见过有人把公共函数放在一段脚本里,测量流程放在另一段,仪器一重启整个测试流程就散架。
开发阶段想确认仪器内存里到底有哪些脚本,可以发送下面这条命令:
print(script.list())回传结果会列出当前已加载的脚本名。看到不认识的残留脚本,用对应的删除命令清理掉。批量跑测试之前,先看一眼这个输出,能避免「仪器里还留着上一次调试的半段代码」这种低级但也最难排查的问题。
5. TSP开发避坑:五个让我返工最多的问题
下面五条是我在实际调试中返工率最高的现场问题,每一条都遵循「现象、原因、解决」三层来拆。
5.1 脚本传上去了却不执行——忘了调用
现象:下发脚本没有任何报错,仪器也没有任何输出,看起来像石沉大海。
原因:loadscript只完成脚本定义,不会自动执行。如果发送工具没有在脚本末尾补一次调用,你看到的只是「仪器内存里多了一段代码」,业务逻辑一次都没跑。
解决:在脚本末尾显式调用,比如script_name()。用我提供的下发工具时,它会给纯 Lua 文件自动包一层devscript()调用,所以文件里不需要写调用行。如果你在某些终端里手工粘贴脚本,务必自己补上调用。最简单的自检方式:回传一条print("start")在文件第一行,跑了就会有输出。
5.2 输出被截断或者读到旧结果——读取早于waitcomplete
现象:上位机循环 read 时拿到不全的数据,或数值前后不一致,同一段脚本跑两次结果对不上。
原因:测量操作还在异步队列里,脚本已经执行到了后续读取逻辑。上位机和仪器端都没有错,只是同步点没踩对。更隐蔽的情况是上一轮测试的数据没清空,这一轮直接读到了残留值。
解决:在每条测量命令后补waitcomplete(),尤其涉及量程、输出开关、源电平切换时。我的习惯是在每个逻辑块之间加一行,并把读到的数据用print打上批次号,比如print("batch", batch_no, value),后续解析时对不上批次号就直接丢弃。
5.3 VS Code不报错、仪器端报语法错误——本地与仪器Lua版本不一致
现象:脚本在编辑器里看着干干净净,语法诊断全绿,下发到仪器却提示解析错误,或执行到一半报非法语句。
原因:本机 Lua 语言服务器默认用较新的 Lua 版本做诊断,TSP 仪器内置解释器往往停留在 Lua 5.0/5.1 分支。某些新语法本地能通过,仪器端不认识。典型的比如使用//整除运算符、goto跳转,或在变量名前用local之后又跨块引用。
解决:把.vscode/settings.json里的Lua.runtime.version调整为Lua 5.1或Lua 5.3,并且尽量只写最基础的 Lua 语法。涉及位运算、整除这类特性时先在仪器终端里做最小验证,再放进批量脚本。编辑器诊断只是辅助,最终裁判永远是仪器端解释器。
5.4 长扫描跑到一半网络超时——VISA超时设太短
现象:批量扫描执行到三分之一,上位机抛出超时异常,但仪器端看起来还在继续工作,数据通道却断了。
原因:Python 脚本里inst.timeout设置的是单次读写操作的最大等待时间,不是整个脚本的总时限。脚本单次 read 等不到数据时,如果 timeout 太小,PyVISA 会直接抛VisaIOError。长测量循环里,某一线程恰好处于无输出的采集阶段,就容易触发误判。
解决:把 timeout 设为预期最长单段执行时间的放大值。我一般给批量扫描设置成单点耗时乘以扫描点数再乘 1.5,最低不低于 30 秒。另外,循环里定期print一个进度标记,这样即使超时,你也能从最后输出的进度行判断仪器跑到哪里,不需要盲猜。
5.5 改完代码后仪器还在跑旧逻辑——残留脚本串台
现象:明明在编辑器里更新了函数逻辑,重新下发后行为没有变化,执行的还是上一版代码。
原因:脚本名冲突。新脚本与内存里旧脚本同名,定义时机或调用顺序有误,导致实际调用的仍然是旧版本。TSP 仪器不会自动清理同名脚本,loadscript的重复定义有时也不会立刻替换已驻留的版本。
解决:先查后删再下发。每次修改关键逻辑前,执行一次print(script.list())确认内存中的脚本列表;发现同名残留就先删除,再重新下发。更严格一点的做法是给每个版本的脚本名带上编号,比如scan_v2,彻底避开同名覆盖的模糊地带。这比靠感觉清理内存靠谱得多。
6. 把工具包用成生产力:脚本库、参数化模板与回读校验
开发到一定规模后,你手里会有很多个.tsp文件。这时候该上代码片段和脚本库两件事。代码片段解决的是写重复模板的问题,脚本库解决的是多台仪器之间复用公共逻辑的问题。
在.vscode下创建tsp.code-snippets,把最常用的循环模板放进去:
{ "TSP repeat scan": { "prefix": "tspfor", "body": [ "for i = 1, ${1:10} do", " ${2:-- test body}", " waitcomplete()", "end", "${3:}" ], "description": "TSP 循环测量模板" } }输入tspfor回车,编辑器会自动展开成带占位符的循环结构,Tab 键跳转填写参数。公共函数则单独抽成一个common.tsp,里面只放通用函数定义,不写测量执行逻辑。业务脚本通过loadscript引用公共脚本,下发工具一样能处理。这样你在几个测试流程间切换时,改的只是业务脚本,公共部分不用动。
回读校验是我最后一道固定动作。任何脚本正式跑批量前,都会先发一个最小测试并回读script.list()和一行print("READY"),确认仪器内存加载的是当前版本。这个习惯的成本是十几秒,但能拦住前面提到的残留脚本串台和版本不一致问题。
我见过有同事在万点矩阵上直接调试,跑到一半发现公式里少乘了一个系数,整轮数据作废重来。那次之后,我把这套「先查列表、再跑最小循环、最后跑全量」的流程写进了任务模板,再也不敢靠眼睛扫一遍代码就当验证完成。工具包的最终价值不是省掉那几行命令,而是让你的每一步操作都有回读、有验证、有版本可追溯。希望帮到你。
本文还有配套的精品资源,点击获取