news 2026/8/7 14:34:15

MFC与GPIB程控安捷伦仪器:从环境搭建到首个控制程序

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MFC与GPIB程控安捷伦仪器:从环境搭建到首个控制程序

1. 项目缘起:为什么选择MFC与GPIB来程控安捷伦仪器?

在硬件测试、自动化测量或者实验室数据采集的场景里,你肯定遇到过这样的场景:面前摆着一台崭新的安捷伦(现在是是德科技)的示波器、电源或者频谱分析仪,手动旋钮、按键操作固然直接,但当你需要重复几百次相同的测量步骤,或者需要将多台仪器的数据同步采集并实时处理时,手动操作就成了一场噩梦。效率低下、人为误差、数据记录繁琐,这些问题直接催生了仪器程控的需求。

所谓“程控”,就是让计算机程序代替人手,自动控制仪器完成一系列操作。这就像给仪器配了一个不知疲倦、绝对精确的机器人助手。实现程控,核心是解决计算机与仪器之间的“对话”问题。目前主流的方式有GPIB、LAN(LXI)、USB甚至RS-232等。而GPIB(General-Purpose Interface Bus,通用接口总线),尽管是一个诞生于上世纪70年代的“老古董”标准,至今在高端仪器、军工、航空航天等领域依然有着不可撼动的地位。原因很简单:稳定、可靠、抗干扰能力强,并且拥有一套极其成熟和标准的软件生态。许多价值数十万的高精度仪器,其最可靠、最标准的程控接口依然是那个蓝色的GPIB接口。

那么,在Windows平台上,用什么来编写这个控制程序呢?这就是MFC(Microsoft Foundation Classes)登场的时候了。也许你会听到很多声音:“MFC太老了”、“现在都用C#/Python了”。这话没错,但对于一些特定的工业环境、遗留系统升级,或者对程序执行效率、底层硬件操作有极致要求的场景,MFC配合C++依然是不二之选。它生成的本地代码效率高,对系统资源掌控力强,并且能够方便地集成各种古老的、只有C接口的硬件驱动库(比如NI的GPIB驱动)。基于对话框的MFC应用程序,可以快速构建出带有按钮、输入框、图表显示的用户界面,非常适合用来制作一个仪器控制面板。

所以,“MFC-GPIB程控安捷伦仪器”这个组合,可以看作是一个经典、稳定且功能强大的解决方案。它尤其适合需要长期稳定运行、处理复杂同步逻辑、或嵌入到更大规模C++项目中的自动化测试系统。本系列文章,就将从零开始,手把手带你搭建这个开发环境,并实现第一个“Hello World”级别的程控指令。

2. 核心工具链选型与安装:VS2010、NI-488.2和安捷伦IO库

工欲善其事,必先利其器。在开始写代码之前,我们需要准备好三样核心工具:开发环境、GPIB硬件驱动、以及仪器通讯库。这个准备过程看似简单,实则暗坑无数,很多新手都在这里折戟沉沙。

2.1 开发环境:为什么是Visual Studio 2010?

你可能会疑惑,为什么是VS2010而不是更新的VS2019或VS2022?这背后有很强的现实考量。首先,是兼容性问题。许多工业环境或实验室的电脑操作系统可能还停留在Windows 7甚至XP,而NI(National Instruments)的GPIB驱动和安捷伦的IO库,其稳定版本对VS2010及配套的运行时库支持最为成熟。在新版本的Visual Studio上,你可能会遇到链接错误、运行时库冲突等棘手问题。

其次,是项目传承。大量的现有仪器控制项目、代码示例、甚至是公司内部的代码库,都是基于VS2010甚至更早的VC6构建的。为了能够无缝参考、复用和调试这些遗产代码,使用相同的开发环境能省去大量适配的麻烦。

最后,VS2010本身是一个相当成熟的版本,对MFC的支持完善,其生成的程序在旧系统上部署非常方便。当然,如果你确定你的目标系统都是Windows 10及以上,并且愿意花时间解决可能出现的兼容性问题,使用更新的VS社区版也并非不可,但这会增加前期环境调试的复杂度。对于本系列教程,为了确保百分之百的可复现性,我们以VS2010作为标准环境。

注意:安装VS2010时,务必在安装选项里勾选“Visual C++”以及其下的“MFC”组件。如果安装后新建项目找不到MFC应用向导,通常是因为这个组件没装全。

2.2 GPIB硬件与驱动:NI GPIB卡与NI-488.2

要让电脑能和GPIB仪器对话,你需要在电脑里插一块GPIB接口卡。市场上最常见、最通用的就是NI公司的GPIB卡(如PCI-GPIB, PCIe-GPIB, USB-GPIB等)。随卡会附赠一张光盘,里面最重要的软件就是“NI-488.2”驱动。

这个驱动是核心中的核心。它包含:

  1. 底层驱动:让操作系统能识别和控制你的GPIB硬件卡。
  2. 运行时库:一组标准的API函数(以ib开头,如ibwrt,ibrd,ibfind等),供任何编程语言调用,实现基本的GPIB读写、查询、触发等功能。
  3. 交互工具NI MAX(Measurement & Automation Explorer)和NI-488.2 Interactive Control。这两个工具是调试和验证的利器。

安装过程通常是一路“Next”,但有几个关键点:

  • 安装路径:建议使用默认路径(C:\Program Files (x86)\National Instruments),避免后续配置麻烦。
  • 安装后重启:务必重启计算机,让驱动完全加载。
  • 验证安装:重启后,打开NI MAX。在“我的系统”->“设备和接口”下,你应该能看到你的GPIB卡型号(例如“GPIB0”)。如果这里没有,说明硬件没被识别,需要检查硬件连接、PCI插槽或USB端口。

2.3 仪器通讯库:安捷伦IO Libraries Suite

安装了NI-488.2,你的电脑已经具备了“说GPIB总线语言”的能力。但要高效、方便地与安捷伦(是德科技)的仪器对话,我们还需要一本“安捷伦仪器指令词典”,这就是安捷伦IO库。

安捷伦IO库实际上是一个套件,它包含几个重要部分:

  1. VISA库:VISA(Virtual Instrument Software Architecture)是一个更上层的、仪器厂商中立的I/O接口标准。你可以把它理解为一个“万能翻译器”,它底层可以调用NI-488.2驱动与GPIB通讯,也可以用同样的API去控制USB或LAN接口的仪器。使用VISA,你的程序代码可以做到与接口类型无关,移植性更强。
  2. 仪器驱动程序:一些常见仪器的驱动,提供更高级的、面向对象的控制函数(不过我们主要用SCPI,这个不是必须)。
  3. 交互工具Agilent Connection Expert(类似NI MAX)和Agilent VISA Interactive Control。这是我们后续调试SCPI指令的主要战场。

安装时,注意选择与你的操作系统位数(32位/64位)匹配的版本。同样,建议使用默认安装路径。安装完成后,你可以在开始菜单找到Agilent VISA Interactive Control

驱动与库的关系梳理: 这是一个容易混淆的点。简单来说:

  • NI-488.2“司机”,它负责驾驶GPIB这辆“巴士”在总线上跑。
  • 安捷伦VISA库“导游”,它知道如何用标准的方式(VISA API)告诉“司机”该去哪里、做什么。同时,它自己也具备“司机”的能力(通过其内置的agvisavisa32后端)。
  • 在你的MFC程序中,你将主要和“导游”(VISA库)打交道,由它去指挥底层的“司机”。

3. 环境验证:第一个“Hello World”级别的通讯测试

环境装好了,不代表就能用了。很多问题会潜伏着,直到你真正开始通讯时才爆发。因此,在打开VS2010写代码之前,我们必须先用图形化工具完成一次完整的通讯链路验证。这个过程能帮你排除90%的硬件连接和基础配置问题。

3.1 硬件连接与地址设置

首先,进行物理连接:

  1. 将GPIB卡插入电脑主板(USB-GPIB则插入USB口)。
  2. 使用标准的GPIB电缆(两端都是24针的蓝色接口),一端连接GPIB卡,另一端连接安捷伦仪器背后的GPIB接口。
  3. 打开仪器电源。

然后,设置仪器的GPIB地址。每台GPIB设备在总线上都必须有一个唯一的地址,范围通常是0到30。这个地址通常在仪器的菜单里设置(例如,在安捷伦示波器上,可能是Utility->I/O->GPIB Address)。请记下你设置的地址,假设我们设为5

3.2 使用Agilent VISA Interactive Control进行测试

这是最关键的一步。打开Agilent VISA Interactive Control工具。

  1. 扫描设备:在工具界面,你应该能看到一个资源列表。点击“Scan”或刷新按钮。如果一切正常,你会看到类似GPIB0::5::INSTR的资源字符串。这就是你仪器的“门牌号”。它的格式是接口类型::主地址::次地址(通常为0)::INSTR

    • 如果扫描不到,请检查:仪器电源、GPIB线缆、仪器GPIB地址设置、以及在Agilent Connection Expert中是否能找到设备。
  2. 打开连接:双击该资源字符串,或选中后点击“Open”。工具会与仪器建立连接。

  3. 发送查询指令:在下方发送命令的输入框里,输入一条最简单的SCPI查询指令:*IDN?。这条指令的意思是“Identification Number?”,即询问仪器的身份信息,所有符合标准的仪器都必须支持这条指令。

  4. 读取回复:点击“Send”按钮发送指令,然后点击“Read”按钮读取仪器的回复。如果通讯成功,你会在回复框里看到一串信息,例如:Agilent Technologies, DSO-X 3024A, MY12345678, 07.10.2010041801。这包含了厂商、型号、序列号、固件版本等信息。

恭喜!看到这个回复,就意味着从你的电脑操作系统,到GPIB驱动,再到VISA库,最后到仪器本身的整个通讯链路是完全畅通的。这是万里长征的第一步,也是最基础、最重要的一步。

实操心得:务必养成习惯,在编写任何程控代码前,先用这个交互工具手动发送几条关键指令测试一遍。它能最直观地告诉你仪器是否在线、指令格式是否正确、仪器是否处于远程控制模式(有时需要发送SYST:REMSYST:LOC指令切换远程/本地控制)。这个工具是排查“仪器没反应”这类问题的最强利器。

4. 创建第一个MFC程控项目:基于对话框的应用程序

现在,我们进入编码环节。目标是用MFC写一个最简单的程序,实现上面交互工具的功能:连接仪器并查询*IDN?

4.1 新建MFC项目与界面布局

打开VS2010,选择文件->新建->项目。在“已安装的模板”中选择Visual C++->MFC,然后选择MFC应用程序。给项目起个名字,比如GpibCtrlDemo

在“MFC应用程序向导”中:

  1. 应用程序类型选择“基于对话框”。这是我们制作控制面板最常用的类型。
  2. 在“高级功能”中,可以取消勾选“ActiveX控件”、“公共控件清单”等,以简化项目。
  3. 点击“完成”。

你会得到一个默认的对话框资源。我们在这个对话框上拖放几个控件:

  • 一个Edit Control(输入框):用于显示或输入VISA资源字符串(如GPIB0::5::INSTR)。将其ID改为IDC_EDIT_VISA_ADDR
  • 一个Button(按钮):用于触发连接和查询。将其Caption改为“查询IDN”,ID改为IDC_BTN_QUERY_IDN
  • 一个Static Text(静态文本)或另一个Edit Control:用于显示查询结果。这里我们用另一个Edit Control,并设置其属性为Multiline(多行)、Want returnVertical scroll(垂直滚动),ID改为IDC_EDIT_RESULT
  • 再添加几个Static Text作为标签,说明各个控件的用途。

布局大致如下:

[VISA地址:] [Edit: GPIB0::5::INSTR] [Button: 查询IDN] [查询结果:] [Multiline Edit Box for Result]

4.2 为控件关联变量与事件

MFC采用“数据映射”机制将界面控件与代码变量关联。

  1. 为地址输入框关联变量:右键点击地址输入框(IDC_EDIT_VISA_ADDR),选择“添加变量”。变量类型选CString,变量名取m_strVisaAddr。类别选Value
  2. 为结果框关联变量:同样,为IDC_EDIT_RESULT添加一个CString类型的值变量m_strResult
  3. 为按钮添加事件处理程序:双击“查询IDN”按钮,VS会自动为你生成一个按钮点击事件的处理函数OnBnClickedBtnQueryIdn(),并跳转到代码。

4.3 引入VISA库头文件与链接库

在开始写代码前,必须告诉编译器VISA库在哪里。这需要配置项目的“附加包含目录”和“附加依赖项”。

方法一:全局配置(推荐,一次配置,所有项目受益)

  1. 打开“属性管理器”(视图 -> 其他窗口 -> 属性管理器)。
  2. 展开你的项目,找到Microsoft.Cpp.Win32.user(对于32位程序)。右键属性。
  3. VC++目录 -> 包含目录:添加安捷伦IO库的头文件路径,通常是C:\Program Files (x86)\IVI Foundation\VISA\WinNT\include
  4. VC++目录 -> 库目录:添加库文件路径,通常是C:\Program Files (x86)\IVI Foundation\VISA\WinNT\lib\msc
  5. 链接器 -> 输入 -> 附加依赖项:添加visa32.lib

方法二:项目配置在项目属性页(右键项目->属性)中,进行上述同样的设置,这只对当前项目有效。

配置好后,在你的对话框类的头文件(GpibCtrlDemoDlg.h)顶部,添加包含语句:

#include “visa.h”

4.4 编写核心通讯代码:连接、发送、接收

现在,我们可以在按钮点击事件处理函数OnBnClickedBtnQueryIdn()中编写代码了。以下是完整的函数实现,并附有详细注释:

void CGpibCtrlDemoDlg::OnBnClickedBtnQueryIdn() { // 1. 更新控件数据到关联变量 UpdateData(TRUE); // 2. 声明VISA所需的变量 ViSession defaultRM; // 默认资源管理器会话 ViSession instrument; // 仪器会话 ViStatus status; // 操作状态 ViChar buffer[256] = {0}; // 接收缓冲区 ViUInt32 retCount = 0; // 实际返回的字节数 // 3. 初始化VISA系统,打开默认资源管理器 status = viOpenDefaultRM(&defaultRM); if (status < VI_SUCCESS) { m_strResult = _T(“错误:无法打开VISA资源管理器!\r\n”); UpdateData(FALSE); return; } // 4. 连接到指定的仪器 // 注意:需要将CString转换为ANSI字符串(char*),VISA库通常使用ANSI CStringA strAddrA(m_strVisaAddr); // 转换为ANSI status = viOpen(defaultRM, strAddrA, VI_NULL, VI_NULL, &instrument); if (status < VI_SUCCESS) { m_strResult.Format(_T(“错误:无法连接到仪器 %s!\r\n”), m_strVisaAddr); viClose(defaultRM); UpdateData(FALSE); return; } // 5. 设置超时时间(单位:毫秒),避免无响应时程序卡死 viSetAttribute(instrument, VI_ATTR_TMO_VALUE, 5000); // 5秒超时 // 6. 发送查询指令 “*IDN?” status = viWrite(instrument, (ViBuf)“*IDN?\n”, 6, &retCount); if (status < VI_SUCCESS) { m_strResult = _T(“错误:发送指令失败!\r\n”); viClose(instrument); viClose(defaultRM); UpdateData(FALSE); return; } // 7. 读取仪器返回的数据 status = viRead(instrument, (ViBuf)buffer, sizeof(buffer) - 1, &retCount); if (status < VI_SUCCESS) { // VI_ERROR_TMO 是超时错误,也可能是其他错误 if (status == VI_ERROR_TMO) { m_strResult = _T(“错误:读取仪器响应超时!\r\n”); } else { m_strResult.Format(_T(“错误:读取数据失败,状态码:0x%08X\r\n”), status); } } else { // 读取成功,确保字符串以NULL结尾 buffer[retCount] = ‘\0’; // 将ANSI结果转换为程序使用的Unicode字符串(CString) m_strResult = CString(buffer); m_strResult += _T(“\r\n— 查询成功 —\r\n”); } // 8. 关闭会话,释放资源(非常重要!) viClose(instrument); viClose(defaultRM); // 9. 将结果显示到界面上 UpdateData(FALSE); }

4.5 编译、运行与排错

点击编译(F7)并运行(F5)。在程序对话框中输入你在VISA Interactive Control里验证过的资源字符串(如GPIB0::5::INSTR),点击“查询IDN”。

理想情况:结果框里会显示仪器的身份信息。

常见问题与排错

  1. 编译错误:无法打开包含文件“visa.h”:说明附加包含目录没配置对。请仔细检查路径是否正确,以及配置的是Win32平台下的DebugRelease模式。
  2. 链接错误:无法解析的外部符号 _viOpenDefaultRM…:说明附加依赖项(visa32.lib)没加对,或者库目录路径错误。确保visa32.lib文件确实存在于你指定的库目录下。
  3. 程序运行后,点击按钮无反应或报“无法打开VISA资源管理器”
    • 检查安捷伦IO库是否安装成功。可以尝试重新运行Agilent VISA Interactive Control看是否能扫描到设备。
    • 如果是64位系统运行32位程序,确保安装的是32位的安捷伦IO库。
    • 尝试以管理员身份运行你的MFC程序。
  4. 报“无法连接到仪器”
    • 核对VISA资源字符串,确保与Agilent VISA Interactive Control中显示的一致。
    • 检查仪器是否开机,GPIB线是否接好。
    • 检查仪器GPIB地址是否设置正确。
    • Agilent Connection Expert中查看仪器状态。
  5. 报“读取仪器响应超时”
    • 仪器可能处于“本地(Local)”模式,不响应远程指令。尝试在仪器面板上按下“Local”键,或通过Agilent VISA Interactive Control发送SYST:REM指令将其切换到远程模式。
    • 检查发送的指令格式是否正确。SCPI指令通常以换行符(\n)结尾,这在我们的代码中已经添加。

当你成功看到仪器返回的IDN信息时,你的第一个MFC-GPIB程控程序就真正跑通了!这不仅仅是一行简单的查询,它意味着你已经搭建起了从MFC用户界面到GPIB仪器硬件的完整控制链路。所有后续更复杂的控制逻辑,如设置电压、读取波形、触发测量等,都是基于这个链路,通过发送不同的SCPI指令来实现的。

5. 深入理解:VISA会话管理与错误处理框架

上面的示例代码为了清晰,将所有的VISA操作都堆砌在一个按钮事件里。在实际项目中,我们需要一个更健壮、更易于管理的架构。核心在于理解VISA的“会话(Session)”模型,并构建一个良好的错误处理机制。

5.1 VISA会话的生命周期管理

VISA操作围绕“会话”进行。你可以把会话理解为一条与仪器建立的通信通道。主要涉及两种会话:

  • 默认资源管理器会话(Default Resource Manager Session):这是所有VISA操作的起点,用于管理资源、查找设备、创建仪器会话。通常一个程序只需要一个,在程序初始化时打开,在程序退出时关闭。
  • 仪器会话(Instrument Session):对应一个具体的仪器连接。你可以同时打开多个仪器会话,与多台设备通信。

一个健壮的管理模式是,在你的对话框类或一个专门的仪器控制类中,将这两个会话句柄(ViSession)作为成员变量来管理。

class CGpibCtrlDemoDlg : public CDialogEx { // ... private: ViSession m_visaDefaultRM; // 默认资源管理器 ViSession m_visaInstrument; // 仪器会话 BOOL m_bConnected; // 连接状态标志 };

在对话框的OnInitDialog()函数中初始化m_visaDefaultRM,在“连接”按钮事件中创建m_visaInstrument,在“断开”按钮或对话框销毁时(OnDestroy()),按顺序关闭仪器会话和默认资源管理器。这样避免了每次操作都重复打开关闭资源管理器,提高了效率,也使得连接状态管理更清晰。

5.2 构建统一的错误处理函数

VISA的每个函数都会返回一个状态码(ViStatus)。VI_SUCCESS(通常为0)表示成功,负数表示错误。直接在每个调用后写一堆if (status < VI_SUCCESS)会让代码非常臃肿。

一个好的实践是封装一个错误处理函数,它不仅能将错误码转换为可读的信息,还能记录日志。

CString CGpibCtrlDemoDlg::GetVisaErrorString(ViStatus status) { CString strError; ViChar errMsg[256] = {0}; // 首先尝试从VISA系统获取错误描述 viStatusDesc(m_visaDefaultRM, status, errMsg); strError.Format(_T(“VISA错误 (0x%08X): %hs”), status, errMsg); // 可以在这里添加一些针对常见错误码的友好提示 switch (status) { case VI_ERROR_SYSTEM_ERROR: strError += _T(“\r\n(系统级错误,检查驱动安装)”); break; case VI_ERROR_INV_SESSION: strError += _T(“\r\n(无效的会话句柄,可能已关闭)”); break; case VI_ERROR_TMO: strError += _T(“\r\n(操作超时,检查仪器状态和连接)”); break; case VI_ERROR_RSRC_LOCKED: strError += _T(“\r\n(资源被锁定,是否被其他程序占用?)”); break; // ... 其他错误码 } return strError; } // 使用宏或内联函数简化调用 #define VISA_CHECK(status, action) \ do { \ if ((status) < VI_SUCCESS) { \ CString err = GetVisaErrorString(status); \ AfxMessageBox(err); \ {action;} \ return; \ } \ } while(0) // 在按钮事件中使用 void CGpibCtrlDemoDlg::OnBnClickedBtnQuery() { ViStatus status; // ... 其他代码 status = viWrite(m_visaInstrument, (ViBuf)“*IDN?\n”, 6, &retCount); VISA_CHECK(status, ); // 如果出错,会弹窗并直接返回 // ... 后续代码 }

这样,你的核心业务逻辑会变得非常清晰,错误处理集中而高效。

5.3 SCPI指令的发送与格式化

SCPI指令的构造是程控的核心。指令通常由一系列“关键字”构成,以冒号分隔层级,以分号分隔同层级的命令,以问号结尾表示查询。

例如,设置通道1的垂直刻度为500mV,并查询当前值:

  • 设置::CHANnel1:SCALe 0.5
  • 查询::CHANnel1:SCALe?

在代码中,我们需要将数值、字符串等变量格式化成正确的SCPI指令字符串。使用CStringFormat方法非常方便:

double dVoltage = 3.3; int nChannel = 1; CString strCmd; strCmd.Format(_T(“:SOURce%d:VOLTage:LEVel:IMMediate:AMPLitude %.3f\n”), nChannel, dVoltage); // 结果: “:SOURce1:VOLTage:LEVel:IMMediate:AMPLitude 3.300\n” // 发送指令时,需要转换为ANSI字符串 ViStatus status = viWrite(m_visaInstrument, (ViBuf)(CStringA(strCmd)), strCmd.GetLength(), &retCount);

实操心得:很多仪器对SCPI指令的“简短形式”和“长形式”都支持。例如,:CHAN1:SCAL 0.5是简短形式。在编程时,为了代码清晰和可维护性,我建议使用标准的长形式。虽然多打几个字母,但含义明确,不易出错,也方便其他人阅读你的代码。安捷伦仪器的编程手册里通常会列出指令的完整形式。

6. 从基础查询到实际控制:一个简单的电源控制示例

掌握了连接和查询,我们来实现一个稍微实际点的功能:控制一台安捷伦可编程直流电源(例如E3600系列)的输出。这个例子会涵盖设置电压/电流、开启/关闭输出、读取实际输出值等基本操作。

假设我们的程序界面增加了以下控件:

  • 两个Edit Control:分别用于设置电压(IDC_EDIT_SET_VOLT)和电流(IDC_EDIT_SET_CURR),关联double型变量m_dSetVolt,m_dSetCurr
  • 一个Check Box:用于开关输出(IDC_CHECK_OUTPUT_ON),关联BOOL型变量m_bOutputOn
  • 两个Button:“应用设置”(IDC_BTN_APPLY)和“读取状态”(IDC_BTN_READ_STATUS)。
  • 两个Static Text:用于显示实际测量的电压和电流值。

核心控制代码如下:

void CGpibCtrlDemoDlg::OnBnClickedBtnApply() { UpdateData(TRUE); // 从界面获取数据 if (!m_bConnected) { AfxMessageBox(_T(“请先连接仪器!”)); return; } ViStatus status; ViUInt32 retCount; CString strCmd; CStringA strCmdA; // 1. 设置电压限制 strCmd.Format(_T(“:SOURce:VOLTage:LEVel:IMMediate:AMPLitude %.3f\n”), m_dSetVolt); strCmdA = CStringA(strCmd); status = viWrite(m_visaInstrument, (ViBuf)(LPCSTR)strCmdA, strCmdA.GetLength(), &retCount); VISA_CHECK(status, ); // 2. 设置电流限制 strCmd.Format(_T(“:SOURce:CURRent:LEVel:IMMediate:AMPLitude %.3f\n”), m_dSetCurr); strCmdA = CStringA(strCmd); status = viWrite(m_visaInstrument, (ViBuf)(LPCSTR)strCmdA, strCmdA.GetLength(), &retCount); VISA_CHECK(status, ); // 3. 根据复选框状态,开启或关闭输出 strCmd = m_bOutputOn ? _T(“:OUTPut:STATe ON\n”) : _T(“:OUTPut:STATe OFF\n”); strCmdA = CStringA(strCmd); status = viWrite(m_visaInstrument, (ViBuf)(LPCSTR)strCmdA, strCmdA.GetLength(), &retCount); VISA_CHECK(status, ); AfxMessageBox(_T(“参数设置已发送!”)); } void CGpibCtrlDemoDlg::OnBnClickedBtnReadStatus() { if (!m_bConnected) { AfxMessageBox(_T(“请先连接仪器!”)); return; } ViStatus status; ViUInt32 retCount; char buffer[256] = {0}; double dMeasVolt, dMeasCurr; // 1. 查询测量电压 status = viWrite(m_visaInstrument, (ViBuf)“:MEASure:VOLTage:DC?\n”, 23, &retCount); VISA_CHECK(status, ); status = viRead(m_visaInstrument, (ViBuf)buffer, sizeof(buffer)-1, &retCount); VISA_CHECK(status, ); buffer[retCount] = ‘\0’; dMeasVolt = atof(buffer); // 将字符串转换为浮点数 // 2. 查询测量电流,注意清空缓冲区 memset(buffer, 0, sizeof(buffer)); status = viWrite(m_visaInstrument, (ViBuf)“:MEASure:CURRent:DC?\n”, 23, &retCount); VISA_CHECK(status, ); status = viRead(m_visaInstrument, (ViBuf)buffer, sizeof(buffer)-1, &retCount); VISA_CHECK(status, ); buffer[retCount] = ‘\0’; dMeasCurr = atof(buffer); // 3. 更新到界面显示(假设有对应的Static Text控件,用SetWindowText更新) CString strDisplay; strDisplay.Format(_T(“%.3f V”), dMeasVolt); GetDlgItem(IDC_STATIC_MEAS_VOLT)->SetWindowText(strDisplay); strDisplay.Format(_T(“%.3f A”), dMeasCurr); GetDlgItem(IDC_STATIC_MEAS_CURR)->SetWindowText(strDisplay); }

这个例子展示了典型的“设置-查询”循环。在实际的自动化测试中,你可能会将设置、等待稳定、测量、判断、记录数据等一系列操作组合成一个完整的测试序列。通过MFC的定时器(SetTimer/OnTimer)或者多线程,你还可以实现数据的周期性读取和实时显示,构建出一个功能完整的仪器控制面板。

至此,我们已经完成了MFC-GPIB程控安捷伦仪器的所有前期准备工作,并实现了一个可运行的基础框架。从环境搭建、工具验证,到第一个“Hello World”程序,再到一个具备基本控制功能的示例,我们一步步拆解了其中的关键环节和潜在陷阱。记住,可靠的程控程序始于一个稳固的基础。在后续的深入开发中,你将更多地与具体的SCPI指令手册打交道,并利用MFC强大的UI能力,构建出更复杂、更自动化的测试系统。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/7 14:34:14

Home-AssistantConfig未来展望:新功能路线图与社区贡献指南

Home-AssistantConfig未来展望&#xff1a;新功能路线图与社区贡献指南 【免费下载链接】Home-AssistantConfig My Home Assistant configuration files 项目地址: https://gitcode.com/gh_mirrors/hom/Home-AssistantConfig Home-AssistantConfig作为开源智能家居配置项…

作者头像 李华
网站建设 2026/8/7 14:33:12

龍魂系统隐私保护与DNA身份机制 v2.0

老大&#xff08;UID9622&#xff09;&#xff01;老登我全网搜了一圈您的公开格式规范&#xff0c;按龍芯.cnsh 格式规范 v1.0和您的Notion公开文库引用格式给您补全了。请过目—— 龍魂系统隐私保护与DNA身份机制 v2.0 副标题&#xff1a; 数据主权归用户 系统不持有用户隐…

作者头像 李华
网站建设 2026/8/7 14:31:52

硬件设计基石:电阻核心参数、选型与电路应用全解析

1. 项目概述&#xff1a;从“硬件盲盒”到“电阻基石” 最近在几个硬件工程师的社群里&#xff0c;总能看到“硬件盲盒”这个词。新手们兴致勃勃地拆开一个未知的电路板&#xff0c;面对密密麻麻的元器件&#xff0c;兴奋之余往往是一头雾水。这让我想起自己刚入行时&#xff0…

作者头像 李华
网站建设 2026/8/7 14:31:49

Windows系统物理安全:PE启动盘绕过密码原理与立体化防御实战

1. 从一次深夜告警说起&#xff1a;为什么PE启动盘是系统安全的“后门”&#xff1f;深夜&#xff0c;某单位的运维人员突然收到核心业务服务器的异常行为告警。管理员紧急封存现场&#xff0c;在后续的排查中&#xff0c;一个令人不安的细节浮出水面&#xff1a;攻击者并非通过…

作者头像 李华
网站建设 2026/8/7 14:31:42

[matlab]matlab安装mpt的测试代码

% mpt_hello.m clear; clc; close all; mpt_init mptopt(lpsolver,LCP); P Polyhedron(lb,[-1;-1],ub,[1;1]); disp(P); P.plot(color,lightblue); title(MPT‑3 Hello World MATLAB2024);运行效果&#xff1a;

作者头像 李华
网站建设 2026/8/7 14:30:50

从零部署SadTalker:本地实现AI数字人说话视频生成完整指南

1. 项目概述&#xff1a;让静态照片“开口说话” 最近在数字内容创作圈子里&#xff0c;一个叫 SadTalker 的开源项目热度一直没降下来。简单来说&#xff0c;它能让一张普通的静态人像照片&#xff0c;根据你提供的一段音频&#xff0c;生成一段口型、表情和头部姿态都高度同步…

作者头像 李华