LabWindows/CVI 2012 综合教程
第一部分:LabWindows/CVI 简介
1 什么是 LabWindows/CVI?

LabWindows/CVI (C for Virtual Instrumentation) 是美国 National Instruments (NI) 公司推出的一款基于 ANSI C 的集成开发环境,它专为测试、测量和控制系统设计,集成了代码编辑、编译、链接、调试和标准仪器驱动程序库等功能。
核心特点:
- 图形化用户界面 开发: 提供直观的拖放式工具,可以轻松创建类似仪器的用户界面(如示波器、万用表面板)。
- 强大的仪器控制: 内置对 GPIB、VXI、PXI、串口、VISA 等总线标准的支持,并能方便地生成和使用 IVI (Interchangeable Virtual Instruments) 驱动程序。
- 数据分析与信号处理: 集成了丰富的数学分析和信号处理函数库,如 NI-Analysis、NI-DSA 等。
- 与硬件的无缝集成: 与 NI 的数据采集卡、示波器、函数发生器等硬件产品完美配合。
- ANSI C 标准: 生成的代码是标准 C 代码,可读性强,易于维护和移植到其他平台。
2 LabWindows/CVI 2012 的工作环境
启动 LabWindows/CVI 2012 后,您会看到几个核心窗口:

- 工程窗口: 管理整个项目的文件,包括
.c(源代码),.h(头文件),.uir(用户界面),.prj(工程文件) 等。 - 代码编辑窗口: 用于编辑 C 源代码和头文件。
- 用户界面编辑窗口: 用于设计和编辑图形用户界面。
- 输出窗口: 显示编译、链接、运行时的信息和错误。
第二部分:创建第一个 LabWindows/CVI 项目——“Hello, World!”
让我们从一个最简单的例子开始,熟悉开发流程。
步骤 1:创建新工程
- 打开 LabWindows/CVI 2012。
- 选择菜单
File -> New -> Project。 - 在弹出的对话框中,选择
Empty Project,并指定工程名称和保存路径,点击OK,此时会弹出一个空的工程窗口。
步骤 2:创建源代码文件
- 在工程窗口中,右键点击
Source Files,选择Add Files to Project -> C Source File...。 - 输入文件名,
hello.c,点击OK,一个空的代码编辑窗口会打开。
步骤 3:编写 C 代码

在 hello.c 文件中输入以下代码:
#include <stdio.h> // 标准输入输出库
int main(int argc, char *argv[])
{
// printf 函数会在 "Output Window" 中打印信息
printf("Hello, LabWindows/CVI 2012!\n");
// 程序暂停,等待用户按键,否则窗口会一闪而过
printf("Press any key to exit...\n");
getchar(); // 等待一个字符输入
return 0; // 程序正常结束
}
步骤 4:编译和运行
- 编译: 在工程窗口中,右键点击
hello.c,选择Compile File hello.c,或者直接按快捷键Ctrl + F7,如果代码无误,输出窗口会显示0 errors, 0 warnings。 - 构建: 选择菜单
Build -> Build Project(快捷键Ctrl + F9),这会将所有编译好的目标文件链接成一个可执行文件 (.exe)。 - 运行: 选择菜单
Run -> Execute hello.exe(快捷键Ctrl + F5)。
您将在 LabWindows/CVI 的 输出窗口 中看到 "Hello, LabWindows/CVI 2012!" 和 "Press any key to exit..." 的提示。
第三部分:创建图形用户界面
这是 LabWindows/CVI 的核心功能,我们将创建一个带按钮和文本框的简单界面。
步骤 1:创建用户界面文件
- 在工程窗口中,右键点击
User Interface Files,选择Add Files to Project -> User Interface File...。 - 输入文件名,
hello_gui.uir,点击OK,此时会打开用户界面编辑窗口。
步骤 2:设计界面
- 从右侧的 控件面板 中,拖拽以下控件到界面编辑窗口的空白处:
- 一个
Command Button(命令按钮),命名为QUIT(Caption 属性设为 "Quit")。 - 一个
Numeric(数字框),命名为RESULT(Label 属性设为 "Result:")。 - 一个
Graph(图形),命名为WAVEFORM。
- 一个
- 调整控件的大小和位置,使其看起来整洁。
- 点击工具栏上的
保存按钮 (或Ctrl + S),保存.uir文件,LabWindows/CVI 会自动询问是否为控件生成回调函数,选择Yes。
步骤 3:关联代码
- 在工程窗口中,您会看到
hello_gui.uir文件下自动生成了三个文件:hello_gui.h,hello_gui.c, 和hello_gui.fp,这些是 UI 文件对应的 C 代码和函数面板。 - 打开
hello.c文件,修改代码如下:
#include <stdio.h>
#include "hello_gui.h" // 包含用户界面头文件
// 声明回调函数
int CVICALLBACK QuitCallback(int panel, int control, int event, void *callbackData, int eventData1, int eventData2);
int CVICALLBACK UpdateGraphCallback(int panel, int control, int event, void *callbackData, int eventData1, int eventData2);
int main(int argc, char *argv[])
{
// 初始化用户界面
if (InitCVIRTE(MAIN_THREAD, 0, 0) == 0) // 初始化 CVI 运行引擎
{
return -1; // 初始化失败
}
// 载入并显示面板
if (LoadPanel(0, "hello_gui.uir", PANEL) < 0)
{
return -1; // 载入面板失败
}
DisplayPanel(PANEL);
// 运行用户界面,进入消息循环
RunUserInterface();
// 卸载面板
DiscardPanel(PANEL);
// 结束 CVI 运行引擎
return 0;
}
// Quit 按钮的回调函数
int CVICALLBACK QuitCallback(int panel, int control, int event, void *callbackData, int eventData1, int eventData2)
{
switch (event)
{
case EVENT_COMMIT: // 当用户点击按钮时
// 停止运行用户界面
QuitUserInterface(0);
break;
}
return 0;
}
// 假设我们有一个按钮来触发图形更新,这里用一个简单的事件来演示
int CVICALLBACK UpdateGraphCallback(int panel, int control, int event, void *callbackData, int eventData1, int eventData2)
{
double yData[100];
int i;
switch (event)
{
case EVENT_COMMIT:
// 生成一些示例数据
for (i = 0; i < 100; i++)
{
yData[i] = sin(i * 0.1); // 正弦波
}
// 将数据绘制到图形上
PlotY(WAVEFORM, yData, 100, VAL_DOUBLE, VAL_THIN_LINE, VAL_EMPTY_SQUARE, VAL_SOLID, 1, VAL_RED);
break;
}
return 0;
}
代码解释:
#include "hello_gui.h":包含了所有 UI 控件和面板的定义。InitCVIRTE(...):初始化 LabWindows/CVI 的运行时环境,必须在main函数开头调用。LoadPanel(...):从.uir文件中加载一个面板到内存。DisplayPanel(...):在屏幕上显示加载的面板。RunUserInterface():启动消息循环,等待用户操作(如点击按钮、输入文本),这个函数会阻塞程序,直到调用QuitUserInterface()。DiscardPanel(...):从内存中卸载面板,释放资源。InitCVIRTE和DiscardPanel之间的代码是程序的主体。- 回调函数:这是 LabWindows/CVI 事件驱动编程的核心,当用户与控件交互(如点击按钮)时,会触发一个
event,程序会自动调用你预先定义好的回调函数。QuitCallback会在QUIT按钮被点击时被调用,其event参数为EVENT_COMMIT。
步骤 4:设置控件回调
- 打开
hello_gui.uir文件,进入用户界面编辑窗口。 - 双击
QUIT按钮,会弹出 回调函数编辑器。 - 在
Function Name中,输入QuitCallback,然后点击Add和OK。 - 双击
WAVEFORM图形,同样在回调函数编辑器中,为其Left Mouse Down事件添加一个回调函数,例如命名为UpdateGraphCallback。 - 再次保存
.uir文件。
步骤 5:编译和运行
- 构建工程 (
Ctrl + F9)。 - 运行工程 (
Ctrl + F5)。
您应该能看到一个带有图形界面的窗口,点击 "Quit" 按钮可以关闭程序;点击图形区域,会绘制出一个正弦波。RESULT 数字框中还没有任何操作,您可以尝试为其添加一个回调函数来显示数值。
第四部分:实战项目——虚拟电压表
这个项目将模拟一个从串口读取数据并显示在仪表盘上的虚拟电压表。
目标:
- 创建一个仪表盘来显示电压值 (0-5V)。
- 创建一个按钮,点击后开始或停止“数据采集”。
- 在后台模拟从串口读取数据,并实时更新仪表盘。
步骤 1:创建 UI
创建一个新的 .uir 文件,voltmeter.uir,并添加以下控件:
- 一个
Circular Knob(圆形旋钮) 或Numeric(数字显示),命名为VOLTMETER,用于显示电压值。 - 一个
Command Button,命名为START_STOP,Caption 设为 "Start"。 - 一个
String Box,命名为STATUS,Label 设为 "Status:",用于显示 "Running..." 或 "Stopped"。
步骤 2:编写代码
创建 voltmeter.c 文件,并编写代码:
#include <userint.h> // 用户界面函数库
#include "voltmeter.h" // UI 头文件
int mainCVICALLBACK; // 主面板句柄
int isRunning = 0; // 运行状态标志
// 回调函数声明
int CVICALLBACK StartStopCallback(int panel, int control, int event, void *callbackData, int eventData1, int eventData2);
int CVICALLBACK UpdateMeter(int panel, int control, int event, void *callbackData, int eventData1, int eventData2);
int main(int argc, char *argv[])
{
if (InitCVIRTE(MAIN_THREAD, 0, 0) == 0)
return -1; /* out of memory */
mainCVICALLBACK = LoadPanel(0, "voltmeter.uir", PANEL);
if (mainCVICALLBACK < 0)
return -1;
DisplayPanel(mainCVICALLBACK);
RunUserInterface();
DiscardPanel(mainCVICALLBACK);
return 0;
}
// Start/Stop 按钮的回调函数
int CVICALLBACK StartStopCallback(int panel, int control, int event, void *callbackData, int eventData1, int eventData2)
{
switch (event)
{
case EVENT_COMMIT:
isRunning = !isRunning; // 切换运行状态
if (isRunning)
{
SetCtrlAttribute(panel, START_STOP, ATTR_LABEL, "Stop");
SetCtrlVal(panel, STATUS, "Running...");
// 启动一个定时器,每隔 100ms 模拟一次数据采集
SetCtrlAttribute(panel, PANEL, ATTR_TIMER_ON, 1);
SetCtrlAttribute(panel, PANEL, ATTR_TIMER_INTERVAL, 100);
}
else
{
SetCtrlAttribute(panel, START_STOP, ATTR_LABEL, "Start");
SetCtrlVal(panel, STATUS, "Stopped");
// 停止定时器
SetCtrlAttribute(panel, PANEL, ATTR_TIMER_ON, 0);
}
break;
}
return 0;
}
// 面板的定时器回调函数
int CVICALLBACK UpdateMeter(int panel, int control, int event, void *callbackData, int eventData1, int eventData2)
{
double simulatedVoltage;
if (event == EVENT_TIMER_TICK)
{
// 模拟从串口读取一个 0.0 到 5.0 之间的随机电压值
simulatedVoltage = GetRandomNumber(0.0, 5.0);
// 更新仪表盘显示
SetCtrlVal(panel, VOLTMETER, simulatedVoltage);
}
return 0;
}
步骤 3:设置回调
- 在
voltmeter.uir中,为START_STOP按钮的EVENT_COMMIT事件添加StartStopCallback回调。 - 在
voltmeter.uir中,选中主面板PANEL,在右侧的 工具属性 窗口中,找到Callbacks页面,为EVENT_TIMER_TICK事件添加UpdateMeter回调。
步骤 4:运行
编译并运行项目,点击 "Start" 按钮,状态会变为 "Running...",并且仪表盘上的数值会每 100ms 更新一次,再次点击按钮(此时显示 "Stop"),数据采集停止。
第五部分:学习资源与进阶
1 官方资源
- NI 官方文档: 这是最权威、最全面的学习资料。
- LabWindows/CVI 2012 Help (F1): 按下
F1键可以随时查看函数、控件的详细文档。 - NI 社区: forums.ni.com/cws 可以找到大量讨论、问答和示例代码。
- LabWindows/CVI 2012 Help (F1): 按下
- 示例代码: LabWindows/CVI 安装目录下有大量的示例程序(通常在
...\samples\目录下),是学习高级功能的最佳途径。
2 进阶主题
当你掌握了基础后,可以学习以下内容:
- 文件 I/O: 学习如何读写
.txt,.csv,.ini等文件,保存和加载测量数据。 - 数据采集: 学习使用 NI-DAQmx 函数库,通过数据采集卡采集真实的模拟电压、数字信号、计数器等。
- 仪器控制: 学习使用 VISA 函数库,通过 GPIB、USB、LAN 等总线控制示波器、万用表、电源等外部仪器。
- 多线程与异步操作: 为了防止 UI 在执行耗时操作(如长时间的数据采集或网络通信)时卡死,需要学习使用 LabWindows/CVI 的多线程功能。
- DLL 和代码共享: 将常用的功能封装成动态链接库,供其他项目或语言调用。
- 发布应用程序: 学习如何将你的项目打包成一个独立的
.exe安装程序,分发给没有安装 LabWindows/CVI 的用户。
希望这份教程能帮助你顺利入门 LabWindows/CVI 2012,祝你学习愉快!
