尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

VB通过VISA控制安捷伦波形发生器:从环境搭建到SCPI命令排错

VB通过VISA控制安捷伦波形发生器:从环境搭建到SCPI命令排错 简介VISA编程实例资源包面向测试测量领域开发者讲解如何通过VB语言调用VISA库控制安捷伦Keysight波形发生器覆盖方波、锯齿波、正弦波及16QAM调制波形的生成流程。内容从VISA库接口机制、任意波形发生器基础到VB事件驱动编程并给出初始化连接、设置参数、写入波形数据、启动输出及关闭会话的完整实现思路适合需要快速上手仪器控制的电子工程师与自动化测试人员。资源包共41个文件约145KB以VB源码、窗体设计文件、资源配置及编译生成的exe和dll为主另有工程配置文件与项目说明可直接在Visual Studio中打开调试。压缩包内包含Waveform Generatev0.9完整的解决方案与核心窗体模块已有1332人学习下载适合作为学习VISA编程、理解SCPI命令与仪器交互流程的入门参考。 只要是搞过仪器自动化的对VISA和SCPI这两个词肯定不陌生。我最近刚好有一台还挂着安捷伦logo的老型号波形发生器要接到工控程序里本来想图省事直接调厂商的DLL结果翻了一圈发现VB6环境下最可靠的反而是老老实实走VISA接口。这篇文章就把“用VB通过VISA控制安捷伦波形发生器输出波形”的完整链路写一遍从为什么选VISA、怎么装环境、怎么写出第一个能跑的最小闭环到真正把正弦波方波送出去最后是这几天实测撞出来的排错经验。全程用真实可跑的代码说话适合刚接手旧设备、考试要交课程设计或者网上找不到完整例程的人参考。1. 为什么最终选了VISAVB联仪器的几种路子对比先说一个反直觉的结论你以为你在给波形发生器写代码其实你是在跟它讲“普通话”。这个普通话就是SCPI命令而VISA是帮你把这句话递到仪器耳朵里的那双手。1.1 仪器通信的本质从物理链路到命令语言的两层问题要控制安捷伦波形发生器必须解决两层问题。第一层是物理链路波形发生器屁股后面可能拖着GPIB线、USB方口线或者网线不同链路的数据包格式、握手逻辑、传输速率完全不一样。第二层是仪器语义你要告诉它“输出一个1kHz、2Vpp的正弦波”这句话得用仪器能听懂的文本命令来表达也就是SCPI命令。VISA的全称是Virtual Instrument Software Architecture它把第一层问题统一封装了。GPIB、USB、LAN串口在你写的程序里都变成了同一个写操作函数。你只需要把SCPI字符串通过VISA丢出去剩下的事情由VISA在底层帮你翻译成对应总线上的具体传输。这就像你做App开发不用关心屏幕是刘海屏还是打孔屏一样VISA把底层的总线差异全部吞掉了。1.2 不走VISA的另类方案以及它们卡在哪里有人可能会问我不用VISA行不行我试过几条路都各有各的痛点。第一种是直接用网口TCP Socket发SCPI命令。波形发生器带LAN口的话确实可以在VB里用Winsock控件连上端口发字符串但坑在于不同型号对socket连接超时、缓存区大小、断开重连的处理五花八门而且一旦遇到USB或GPIB连接的老设备这套代码就完全作废了。第二种是调用厂商的私有DLL库。安捷伦/是德官方会提供某个具体型号的驱动SDK比如Agilent 33220A的IVI驱动。这条路单型号能用但你得把驱动初始化、属性设置、错误处理全部搞清楚代码绑定死在一个型号上。换一台其他厂牌的波形发生器整套代码就要推翻重写。第三种是干脆用人家打包好的上位机软件比如BenchVue或者Agilent的IO Library自带的Panel工具手动点几下把波形输出。这个适合临时验证没法嵌入到你的生产流程里。我把这几种方式整理成一张表方便你在选型的时候直接对号入座方案开发难度跨型号能力硬件需求适用场景TCP Socket直发SCPI中仅限带网口的型号网口原型验证、单型号专用厂商私有DLL低-中绑定单型号无特殊要求长期使用同一款仪器手动操作上位机Panel零无无临时测试、排查问题VISA标准接口中跨厂商、跨总线总线支持即可通用自动化测试体系我最后选VISA核心原因就一条它把“换总线”和“换仪器”的成本压在底层你的业务代码根本不用动。今天用GPIB联33220A明天换USB联33500B修改的只是打开设备时传入的资源名。2. 环境准备驱动、连接和仪器地址识别写代码之前先得把通信链路打通。这一步看着简单其实很多人栽在环境问题上折腾半天最后发现代码一个字都没错就是仪器地址写错了。2.1 装机三步走VISA运行时、IO库、驱动库第一步安装VISA运行时库。如果你用的是是德/安捷伦仪器建议直接装Keysight IO Libraries Suite它会自带VISA运行时和Connection Expert管理工具。如果你手头的设备更多是NI的也可以装NI-VISA。我的建议是不要同时装两套别问我为什么问就是我曾经因为两个VISA运行时抢默认连接出了灵异问题。第二步确保电脑能识别物理链路。用USB线连接的话装完IO库一般会自动识别。用GPIB卡连接需要额外装GPIB卡的驱动比如Keysight 82357B USB-GPIB卡就有自己的驱动包装完会在设备管理器里看到独立端口。第三步如果仪器型号比较老比如我手头的33220A官方还建议装一个仪器驱动库但实际上不装也能用VISA直接发SCPI命令驱动库的作用更多是给LabVIEW这种高级环境提供更友好的属性接口。VB里咱们直接操作字符串不依赖它可以跳过。2.2 连接方式的选择与带宽考量三种常见连接方式我实测的优先级是这样的如果仪器和电脑都在工控机旁边USB最省心一根线解决问题如果设备距离远或者要组多仪器测试台LAN口最灵活GPIB虽然老但抗干扰能力强在电磁环境复杂的产线上依然好用这也是很多老实验室还留着GPIB卡的原因。传输速度上不需要有心理负担。波形发生器的SCPI命令本身传输量很小就是几行文本哪怕是几十毫秒的往返时间也完全够用。不用担心用USB比GPIB慢会不会丢波形实测下来控制层面根本感知不到差异真正影响波形质量的是波形发生器自身的DAC精度。2.3 怎么查到仪器的VISA资源名装好IO库之后打开Keysight Connection Expert老版本叫Agilent IO Control它会自动扫描当前总线上挂着的仪器。扫描完成后每个仪器都会显示一个VISA资源名常见的格式是GPIBGPIB0::10::INSTR其中GPIB板卡索引通常是010是仪器的GPIB地址USBUSB0::0x0957::0x2B07::MY44001234::INSTR中间两段是厂商ID和产品ID最后一段是序列号LANTCPIP0::192.168.1.100::inst0::INSTR我强烈建议你在Connection Expert里先选中仪器点右键打开Interative IO或者Send Command Panel手动发一条*IDN?命令。如果这条命令能返回仪器的厂商和型号字符串说明物理链路和VISA环境已经通了。这一步卡住不用写代码排查省时省力。注意发完命令之后按一下Send Read按钮别只发不读。3. 最小闭环让VB程序把第一行命令送给波形发生器环境准备好之后终于可以打开VB6写代码了。不知道现在还有多少人用VB6但在我接触的工控和仪表行业VB6的存量项目真的不少。好消息是VISA的接口函数在VB6里调用起来并不复杂。3.1 在VB6工程里正确引用VISA有两种方式在VB6中使用VISA。第一种是在“工程 - 引用”里勾选VISA相关类型库这样就能直接用VISA对象模型里的类方法。这种方式看起来友好但我在不同机器上遇到过类型库版本不一致导致的问题。我推荐用第二种方式直接在标准模块里声明visa32.dll的导出函数。这种方式不依赖类型库注册状态只要dll在系统路径里代码就能跑。下面是需要用到的最小声明集合Public Declare Function viOpenDefaultRM Lib visa32.dll (ByRef vi As Long) As Long Public Declare Function viOpen Lib visa32.dll (ByVal sesn As Long, ByVal rsrcName As String, ByVal accessMode As Long, ByVal openTimeout As Long, ByRef vi As Long) As Long Public Declare Function viWrite Lib visa32.dll (ByVal vi As Long, ByVal buf As String, ByVal count As Long, ByRef retCount As Long) As Long Public Declare Function viRead Lib visa32.dll (ByVal vi As Long, ByRef buf As Byte, ByVal count As Long, ByRef retCount As Long) As Long Public Declare Function viClose Lib visa32.dll (ByVal vi As Long) As Long Public Declare Function viSetAttribute Lib visa32.dll (ByVal vi As Long, ByVal attr As Long, ByVal val As Long) As Long几个关键点我解释一下。viOpen里的rsrcName用ByVal String传资源名因为SCPI命令和资源名都是纯ASCII字符VB会自动转成ANSI字符串不会有编码问题。viWrite的buf也是用ByVal String这样最省事。viRead的buf比较特殊VISA底层往缓冲区里填字节所以要用Byte数组ByRef传进去读完之后再转成字符串。3.2 从资源管理器到设备句柄的完整打开流程VISA的调用逻辑是分层的。第一步先通过viOpenDefaultRM获得一个资源管理器句柄这个句柄代表本机的VISA运行环境。第二步通过viOpen传入这个资源管理器句柄和仪器的资源名获得与具体仪器通信的设备句柄。后面的所有读写操作都针对这个设备句柄。Dim viRM As Long Dim viInstr As Long Dim retCount As Long Dim status As Long Dim idnResponse As String status viOpenDefaultRM(viRM) If status 0 Then MsgBox VISA资源管理器打开失败错误码 status Exit Sub End If status viOpen(viRM, USB0::0x0957::0x2B07::MY44001234::INSTR, 0, 2000, viInstr) If status 0 Then MsgBox 仪器打开失败请检查资源名或物理连接错误码 status viClose viRM Exit Sub End If这里的accessMode参数传0表示用默认模式打开正常情况下不需要改成别的。openTimeout传2000的意思是打开操作最长等待2秒如果2秒内仪器没响应就返回错误码。这两个细节很多人不重视但如果你在大型测试系统里同时开几十台仪器超时参数就是防止程序卡死的关键。3.3 发送命令与读取响应封装之后的救火队员函数我把发送命令和读取返回封装成两个函数后面写业务逻辑的时候会清爽很多。在实际项目中你会发现封装越早后期调起来越舒服。Public Sub VisaWrite(ByVal viInstr As Long, ByVal cmdStr As String) Dim retCount As Long Dim status As Long status viWrite(viInstr, cmdStr, Len(cmdStr), retCount) If status 0 Then MsgBox 写命令失败 cmdStr 错误码 status End If End Sub Public Function VisaQuery(ByVal viInstr As Long, ByVal cmdStr As String) As String Dim status As Long Dim retCount As Long Dim buffer(0 To 1023) As Byte Dim response As String Call VisaWrite(viInstr, cmdStr) status viRead(viInstr, buffer(0), 1024, retCount) If status 0 Then response StrConv(buffer, vbUnicode) VisaQuery Left(response, retCount) Else VisaQuery End If End Function读响应时有个经典坑ViRead的缓冲区如果不够大而仪器返回的数据恰好超过缓冲区容量VISA会返回错误码。所以缓冲区开大一点通常没错1024字节对波形发生器的状态查询来说绰绰有余。读到的字符串末尾通常带着换行符在用的时候记得用Trim清一下。写到这里最小闭环就完整了。把上面几段代码拼到一起Frame窗体上加一个按钮点击后执行“打开资源管理器 - 打开设备 - 发送*IDN? - 读取返回 - 显示”一个用VB通过VISA控制安捷伦波形发生器的骨架就出来了。4. 配置波形输出SCPI命令里的关键参数与实测验证收到*IDN?的正确返回只是万里长征第一步真正的业务逻辑是配置波形参数并开启输出。这一节讲清楚我在实际项目里怎么设置SCPI命令每一步都对应真实的仪器行为。4.1 先把仪器“清场”复位与操作完成同步写正式配置之前先发两条命令*CLS清空错误队列*RST恢复出厂设置。这两条不是玄学*CLS能把历史遗留的报错清掉避免后续命令因为仪器处于错误状态而被拒绝执行。*RST则把所有参数恢复到默认值确保你不是在别人上次实验留下的参数基础上叠加设置。重点是*RST执行后仪器复位需要时间尤其是老型号机械继电器切换可能耗时几百毫秒。有些教程教人直接用Sleep函数延时我不推荐。标准做法是发*OPC?查询操作完成状态这个查询会阻塞到仪器完成所有挂起操作后返回“1”。我在项目里的用法是Call VisaWrite(viInstr, *CLS) Call VisaWrite(viInstr, *RST) Dim opcReturn As String opcReturn VisaQuery(viInstr, *OPC?) If Trim(opcReturn) 1 Then MsgBox 仪器复位异常 Exit Sub End If这段代码保证了后面的设置命令一定是在仪器真正复位完成后才发出比固定Sleep几百毫秒可靠得多。4.2 频率、幅度、偏置与波形类型参数波形发生器最核心的参数有这么几个波形类型、频率、幅度、偏置、负载阻抗。对应的SCPI命令如下 设置输出正弦波 Call VisaWrite(viInstr, FUNC SIN) 设置频率1kHz Call VisaWrite(viInstr, FREQ 1000) 设置幅度2Vpp峰峰值 Call VisaWrite(viInstr, VOLT 2.0) 设置直流偏置0.5V Call VisaWrite(viInstr, VOLT:OFFS 0.5) 设置输出负载为50欧姆 Call VisaWrite(viInstr, OUTP:LOAD 50) 开启输出通道 Call VisaWrite(viInstr, OUTP ON)命令格式有讲究。FREQ后面的单位缺省为Hz所以FREQ 1000就是1kHz。VOLT默认设置的是峰峰值Vpp不是有效值也不是峰值这一点特别容易搞错。你要是在面板上手输2V然后在示波器上看到4Vpp的信号可能就是这个单位理解错了。OUTP:LOAD这个参数是个隐形杀手。波形发生器内部会根据负载阻抗调整实际输出电压。如果你把负载设成50欧姆但实际接的是高阻示波器探头显示的电压幅值会失真。反过来也一样。所以接不同负载前先确认OUTP:LOAD设置正确。4.3 现场验证用仪器面板、示波器和回读来确认代码跑完之后怎么确认真的输出对了我每次都做三重验证。第一重看仪器面板。安捷伦波形发生器的前面板会显示当前波形的参数摘要包括波形类型、频率、幅度。如果面板显示的和代码里设置的一致基本可以判定命令生效了。第二重用回读命令确认。把参数读回来对比Dim freqRead As String Dim voltRead As String freqRead VisaQuery(viInstr, FREQ?) voltRead VisaQuery(viInstr, VOLT?)回读有额外的好处VISA查询模式下你读到的值是仪器内部真实使用的参数不是你在命令里写入的原始值。如果波形发生器内部对参数做了取整或范围限制回读值和设定值的差异一眼就能看出来。第三重才是接示波器看波形。前面两重过了再用示波器确认能省掉很多排查环节。示波器主要看频率对不对、幅度正确不正确、波形输出端有没有接错口。波形发生器的输出口通常不止一个主动通道和同步输出口别搞混比如通道1和Sync口。实测时我还遇到过一个典型的“假死”情况代码跑完仪器面板显示输出正常示波器就是没有波。最后一查是波形发生器的后面板Output按钮被手按掉了遥控状态下前面板也能手动断开输出。所以波形不出来先看仪器面板上的output指示灯亮不亮这一条经验能帮你节省半天。5. 忙了几天才撞见的坑VISAVB排错实录这一节是我最想写的部分。网上能找到的VISA教程基本都在讲函数怎么用但真正卡住你的往往是那些“看起来没问题但就是不通”的玄学问题。我把这几天实测遇到的坑按排查链路梳理出来。5.1 Write能成功但Read卡死终止字符与超时设置第一个让我头大的问题VisaWrite发送*IDN?之后VisaQuery里的viRead一直卡住不返回程序像死了一样。排查链路是这样的。我先怀疑是仪器没返回数据于是断开程序用IO库的Interactive IO手动发*IDN?结果显示正常。这说明仪器本身没问题。然后又怀疑是结束位没设置一查才发现问题就在这里。VISA的viRead函数默认情况下是读到指定字节数或者检测到终止字符才返回。很多型号的波形发生器返回字符串末尾没有终止符或者终止符需要显式配置。解决办法是在读取之前设置终止字符启用Const VI_ATTR_TERMCHAR_EN As Long H3FFF0031 Const VI_ATTR_TERMCHAR As Long H3FFF0032 启用终止字符默认终止字符是换行符 Call viSetAttribute(viInstr, VI_ATTR_TERMCHAR_EN, 1)设置之后viRead就会在接收到换行符时立即返回不会再傻等。顺手再设置一个合理的超时时间防止真的没数据把程序挂死Const VI_ATTR_TMO_VALUE As Long H3FFF001A Call viSetAttribute(viInstr, VI_ATTR_TMO_VALUE, 3000)这个属性的单位是毫秒3000表示读取超过3秒未完成就返回超时错误码。超时时间不建议设太大搞成30秒的话一旦设备出问题你的程序会在这个基础上翻倍等待排查问题时非常折磨。5.2 Timeout设太大反而不好判断问题说到超时顺手分享一个排错时的经验定位阶段不要用长超时用短超时反而更容易定位问题。有一次我设置命令发出去后一直没反应把超时设成了10秒结果每次出错都要等10秒程序崩溃一次要等好几轮才摸到规律。后来我把超时改成1000毫秒让错误快速暴露再通断几次物理连接很快发现是USB线接触不良导致的间歇性断开。在实际生产代码里我会把超时设置做成配置文件调试期用短超时稳定期用长超时。这样既不会在调试时浪费时间也不会在生产环境里因为网络偶尔抖动就误报故障。5.3 不同代际型号的SCPI兼容性安捷伦/是德波形发生器经历了多代产品演变SCPI命令基本兼容但细节差异你不在真机上踩一遍根本不知道。比如老型号33220A对VOLT和VOLT:OFFS的处理和老旧固件版本之间就有差异有些固件在FREQ命令不带单位时认有些要求必须带Hz后缀。33250A和33500B系列在通道数量、采样率方面也都有各自的命令扩展。33500B系列如果不先发SYST:REM命令进远程模式部分设置命令会被面板优先操作覆盖。接手不熟悉的型号第一件事就是从官网下载最新的SCPI命令参考手册CtrlF搜FUNC、VOLT、FREQ这几个关键词把该型号的命令格式确认一遍。这一步虽然枯燥但能省掉后面大量的试错时间。5.4 关于程序稳定性的三条忠告最后整理几条关于程序稳定性的实操心得。第一每次程序退出前记得viClose。VISA资源管理器句柄和设备句柄都算系统资源程序反复运行但不关闭句柄最终会耗尽资源导致viOpen失败。实测中连续跑几十次不关闭句柄系统里就会积累一堆无效设备会话表现就是设备偶尔连不上。第二异常处理必须兜底。VB6里的On Error GoTo在仪器控制里尤其重要。SCPI命令是文本协议你没法在编译期发现拼写错误只有运行时才会返回错误码。我习惯在每次VisaWrite之后检查错误码一旦出错就立刻中断并弹出错误信息而不是让程序带着错误状态继续跑。否则一条命令拼错可能引发后续一串诡异行为。第三把你的资源名和关健启动参数收敛到一个配置文件里。我差点忘了说VISA资源名串包含了USB序列号换一台仪器序列号就变了。如果你把设备地址散落在代码的各个角落换设备时改到崩溃。把它统一放在INI或注册表里这样换仪器时只改一行配置代码不用动。这是一个很多人看不上但实战中极好用的习惯。 一个收尾用的关闭函数示例 Public Sub VisaClose(ByRef viInstr As Long, ByRef viRM As Long) If viInstr 0 Then Call viClose(viInstr) viInstr 0 End If If viRM 0 Then Call viClose(viRM) viRM 0 End If End Sub上面这个关闭函数里加了句柄归零的操作很多人会忽略。它的好处是防止你在某个分支里重复关同一个句柄导致二次关闭错误这种错误在大型程序里很隐蔽遇到一次就知道值了。这几天的实测下来我对VISA最大的感受就是只要前期把环境链路和基础封装做好了后面换仪器换总线都只是改参数的事。如果你现在的项目也卡在VB控制波形发生器这个点上照着这个链路从环境验证、最小闭环到参数设置一步步来应该能省下不少研究官方手册的时间。本文还有配套的精品资源点击获取
返回列表