NI-VISA 核心 API 整理
约定ViStatus返回值:≥ VI_SUCCESS(0)成功;<0错误 【IN】输入参数,【OUT】输出参数
1. viOpenDefaultRM:打开 VISA 资源管理器
ViStatus viOpenDefaultRM(ViSession *rmSession);调用示例
ViSession rmSession; ViStatus status = viOpenDefaultRM(&rmSession); if(status < VI_SUCCESS) { QLOG_ERROR() << "VISA资源管理器打开失败,请确认安装NI-VISA驱动"; return false; }参数分析
ViSession *rmSession【OUT】 输出资源管理器句柄;调用前变量无效,成功后得到合法句柄。
要点分析
- 所有 VISA 操作入口函数;全局建议只打开一次,不要反复创建销毁;
- 本机未安装 NI-VISA 驱动,调用直接失败;
- 资源管理器管理本机全部 GPIB/TCPIP/USB 仪器资源。
2. viOpen:建立单台仪器会话
ViStatus viOpen( ViSession rmSession, //【IN】资源管理器句柄 ViConstRsrc resourceName, //【IN】VISA资源字符串 ViAccessMode accessMode, //【IN】访问模式 ViUInt32 timeout, //【IN】打开超时 ViSession *instrSession //【OUT】仪器会话句柄 );调用示例
ViSession pInstrHandle; ViStatus status = viOpen(rmSession, "TCPIP0::192.168.1.70::inst0::INSTR", VI_NULL, VI_NULL, &pInstrHandle); if(status < VI_SUCCESS) { QLOG_ERROR() << "仪器连接失败"; viClose(rmSession); return false; }参数说明
rmSession:上一步打开的资源管理器句柄;resourceName:仪器资源地址字符串;accessMode = VI_NULL使用默认共享模式;timeout = VI_NULL继承会话默认超时;&pInstrHandle【OUT】连接成功输出仪器句柄,后续 viWrite/viScanf 全部依赖此句柄。
3. viFindRsrc:批量检索 VISA 仪器资源
ViStatus viFindRsrc( ViSession rmSession, //【IN】资源管理器句柄 ViString expr, //【IN】检索匹配表达式 ViFindList *findList, //【OUT】枚举列表句柄 ViUInt32 *count, //【OUT】搜索到设备总数 ViChar desc[] //【OUT】缓冲区,保存第一条资源字符串 );调用示例
ViFindList findList; ViUInt32 numInstrs; char instrDescriptor[VI_FIND_BUFLEN]; ViStatus status = viFindRsrc(defaultRM, "?*INSTR", &findList, &numInstrs, instrDescriptor); if(status < VI_SUCCESS) { QLOG_ERROR() << "仪器检索失败"; return status; }参数 & 问题分析
- 检索表达式标准写法:
"?*INSTR" findList:枚举句柄,必须最后调用viClose(findList)释放;instrDescriptor字符数组作为输出缓冲区,承载资源名称;- 仅获取第一条仪器,剩余设备依靠
viFindNext循环读取。
4. viFindNext:获取下一条仪器资源
ViStatus viFindNext( ViFindList findList, //【IN】viFindRsrc生成的枚举句柄 ViChar desc[] //【OUT】缓冲区,接收下一个资源字符串 );调用示例
while (--numInstrs) { status = viFindNext(findList, instrDescriptor); if(status < VI_SUCCESS) break; QLOG_DEBUG() << instrDescriptor; }要点分析
- 必须在
viFindRsrc成功之后调用; - 循环持续填充
instrDescriptor;检索结束或出错返回负数; - 整套枚举完成后务必
viClose(findList)。
5. viWrite:发送 SCPI 命令
ViStatus viWrite( ViSession session, //【IN】仪器会话句柄 ViBuf buffer, //【IN】待发送数据指针 void* ViUInt32 count, //【IN】期望发送字节数 ViUInt32 *retCount //【OUT】实际成功发送字节数 );调用示例
char stringinput[512]; strncpy(stringinput,"*RST\n",sizeof(stringinput)-1); ViUInt32 writeCount; ViStatus status = viWrite(pInstrHandle, (ViBuf)stringinput, (ViUInt32)strlen(stringinput), &writeCount); if(status < VI_SUCCESS) { QLOG_ERROR() << "指令发送失败"; return false; }分析
- 常用于下发
*RST、CONF:VOLT、READ?、ROUT:MON; retCount可用来校验半包:预期长度!= 实际长度代表发送异常;- SCPI 网口仪器建议指令末尾带上
\n作为命令终止符。
6. viScanf:格式化阻塞读取
ViStatus viScanf(ViSession session, ViString format, ...);调用示例
double DQ973A_voltage_data[16]; ViStatus status = viScanf(pInstrHandle,"%,1000lf",DQ973A_voltage_data); if(status < VI_SUCCESS) { QLOG_ERROR() << "读取测量数据超时/失败"; return false; }格式说明"%,1000lf"
%lf读取 double 浮点;,代表自动识别逗号分隔;1000最多解析 1000 个浮点数;
重点风险
- 阻塞函数,超时时间由
VI_ATTR_TMO_VALUE控制;主线程直接调用会 UI 卡死; - 必须判断返回 ViStatus!你原有代码完全忽略返回值,读取失败数组保留旧数值,造成虚假测量结果;
- 适配 DQ973A
READ?指令:仪器返回val1,val2,val3...自动拆分填入数组,无需手动字符串切割。
7. viSetAttribute:设置会话属性
ViStatus viSetAttribute( ViSession session, //【IN】仪器句柄 ViAttr attribute, //【IN】属性ID ViAttrState value //【IN】属性配置值 );调用示例
viSetAttribute(pInstrHandle, VI_ATTR_TMO_VALUE, 5000); viSetAttribute(pInstrHandle, VI_ATTR_SUPPRESS_END_EN, VI_FALSE); viSetAttribute(pInstrHandle, VI_ATTR_SEND_END_EN, VI_FALSE);参数分析
全部为输入参数,无输出;
VI_ATTR_TMO_VALUE:读写超时,单位 ms;VI_ATTR_SEND_END_EN = VI_FALSE关闭底层自动 END 信号,TCP 网口仪器标准配置,不要随意修改。
8. viClose:关闭各类 VISA 会话
ViStatus viClose(ViSession session); //【IN】任意合法句柄调用示例
viClose(pInstrHandle); //关闭仪器会话 viClose(findList); //关闭枚举列表 viClose(rmSession); //关闭资源管理器两条标准业务流程
流程 A:界面扫描仪器列表
viOpenDefaultRM → viFindRsrc → 循环viFindNext → viClose(findList) → viClose(defaultRM)注意:窗口扫描完成后关闭资源管理器;长期运行程序不建议频繁开关。
流程 B:连接 设备 + 采集数据
viOpenDefaultRM(全局一次) → viOpen(资源串, &pInstrHandle) → viSetAttribute 配置超时、END参数 → viWrite 下发SCPI配置指令 → viWrite("READ?\n") → viScanf 读取多路测量值 //采集结束/断开设备 → viClose(pInstrHandle) //程序退出时 viClose(rmSession)统一避坑清单
viOpenDefaultRM全局只打开一次,不要循环反复调用;viOpen/viSetAttribute/viWrite/viScanf每一步都校验ViStatus;viScanf阻塞,禁止在 Qt 主线程大量循环采集;viClose成对调用,禁止重复关闭同一个句柄;网络仪器固定配置:
VI_ATTR_SEND_END_EN = VI_FALSE;通讯异常优先仅关闭仪器句柄,不要轻易关闭资源管理器
rmSession;viWrite建议增加半包校验,对比writeCount与预期发送长度。