资讯动态

LabWindows/CVI入门:用C语言开发上位机波形显示程序

发布时间:2026/10/24 4:52:04 来源:尧图企业网站定制
简介这份资料是以实例驱动的LabWindows/CVI基础教程定位为测控领域上位机开发入门读物面向熟悉ANSI C并了解Windows基础操作的工程师与学生。教程从NI LabWindows/CVI的集成开发环境讲起通过一个包含Acquire、Clear、Quit三个按钮的演示程序循序渐进地介绍创建UIR文件、添加并配置Command Button与Graph控件、修改Panel属性、编写回调函数、实现正弦波生成与清除等完整流程帮助读者快速理解事件驱动编程思想和基本开发步骤。资源为单个PDF文档压缩包大小仅1.08MB内容精炼、便于按章节阅读和检索该资源目前已获得978人次学习受到一定关注。除入门操作外教程还对菜单路径、关键术语及待修改项采用特殊格式标注并延伸介绍数据处理、实时系统接口与硬件通信等进阶主题适合希望系统掌握LabWindows/CVI应用技能的初学者作为第一份实践指南。1. LabWindows/CVI 到底是什么给会 C 语言但没做过上位机的人很多人第一次听到 LabWindows/CVI 这个词心里会犯嘀咕它和 LabVIEW 是一回事吗名字里带 Windows是操作系统吗CVI 又是什么缩写这里先给你吃一颗定心丸LabWindows/CVI 是 National Instruments 公司推出的一套面向测控领域的软件开发平台它最大的特点是把你已经学过的 C 语言变成一把能做数据采集、分析和界面显示的多功能瑞士军刀。也就是说你不需要重新学一门新语言用熟悉的 C 语法就能写出带窗口、有按钮、能画曲线图的上位机软件。它特别适合哪类人如果你学过 C 语言但对怎么用 C 写一个带界面的程序完全没有概念或者你是做硬件、测试相关工作的经常需要写个小工具来读设备数据、画波形、存日志那 LabWindows/CVI 几乎就是为你准备的。你可以把它理解成一个测控领域专用版的 C 语言 IDE它把 C 语言和一大堆现成的测控函数库打包在了一起让 C 语言真正能落地到数据采集、仪器控制这些实际场景里。我第一次接触它的时候刚在单片机上写过流水灯对上位机三个字一头雾水。后来发现用 CVI 写一个采集程序不需要自己从零去画窗口、管消息它把这些底层的东西全部封装好了你只需要专注于数据怎么处理这一件事。这就是它最大的价值C 语言负责逻辑CVI 负责把界面和硬件通道都铺好。读完这篇文章你会知道从哪下手、先学什么并且能跟着做出第一个能跑的程序。2. 为什么要学 CVI它解决了 C 语言写界面的老大难2.1 纯 C 语言做测控软件痛苦在哪里如果你试着用纯 C 语言写一个带界面的测控程序很快就会发现几个绕不过去的坎。第一个是窗口和控件的问题在 Windows 下写界面要注册窗口类、处理消息循环、响应鼠标点击代码量大且逻辑绕。第二个是数据可视化的痛点你想画一条波形曲线用纯 C 去调用绘图 API需要处理坐标转换、画笔属性、重绘逻辑随便一写就是几百行。第三个是硬件交互的复杂度读取一块采集卡的数据要去查寄存器、配置 DMA、处理中断这对新手来说完全是黑匣子。2.2 CVI 的解法控件拖一拖函数调一调LabWindows/CVI 的思路是把这些痛点全部包起来。它提供了一个可视化的界面编辑器你像搭积木一样把按钮、图形显示控件拖到面板上软件自动生成对应的 C 代码框架你要做的只是往框架里填逻辑。数据采集、信号处理这些功能它以库函数的形式提供比如想生成一个正弦波调一个函数就行参数填进去数据就出来了。我第一次跑通 CVI 程序时感受特别深整个界面设计过程就像在画板上摆控件而代码部分只需要在几个回调函数里写几行逻辑。这和我印象中写程序等于敲几百行代码完全不同。CVI 做的事情本质上就是让 C 语言开发者没有门槛地上手测控软件开发你懂 C 语言的基础语法、懂函数调用就能把一套测控系统拼出来。2.3 它和 LabVIEW、Visual Studio 有什么不一样很多人会拿 CVI 和 LabVIEW 比较简单说LabVIEW 是图形化编程用连线的方式搭程序而 CVI 是文本式编程你写的依然是标准 C 代码只不过在函数库里调用了大量现成的测控函数。这对习惯了写代码的人来说更自然。和 Visual Studio 相比CVI 的优势在于它是专门为测控场景设计的里面有大量与数据采集、仪器控制、信号分析相关的库函数做上位机开发的效率远高于用通用 IDE 从零搭建。做数据采集CVI 能直接调用厂商提供的驱动函数做波形显示CVI 的 Graph 控件开箱即用不需要自己去封装绘图逻辑。3. 第一个核心概念工程里那三个文件是干什么的3.1 从 UIR 到 C 再到 H各司其职刚开始学 CVI最让人困惑的就是它的工程文件结构。一个最简单的 CVI 项目里通常会有三类文件.uir是用户界面文件它保存了你设计的窗口和控件布局.c是你写逻辑代码的源文件.h是头文件由 CVI 自动生成里面定义了每个控件的常量标识符。它们三者的关系用造房子来类比最合适.uir是房子的户型图规定了哪里有门哪里有窗.c是住在里面的人的生活习惯决定了下班回家是先开灯还是先开电视.h则是门牌号让程序能找到每个控件的位置。3.2 回调函数界面向你“报告”的通道CVI 编程里最重要的思想是事件驱动它的体现就是回调函数。你在界面放了一个 Acquire 按钮当用户点击它时程序怎么知道要执行什么操作答案就是你给这个按钮绑定了一个回调函数比如AcquireCallback一旦按钮被点击CVI 会自动调用这个函数。你不需要像传统 C 一样写一个死循环去不停地检查有没有人被点击了CVI 把这件事替你做了你只需要写好点击之后干什么。我第一次学的时候不太理解为什么要设置三样东西Constant Name、Callback Function 和 Label。这里给你解释清楚Label 是按钮上显示的文字用户看到的Constant Name 是程序里用来标识这个控件的常量名代码里通过它来引用按钮Callback Function 是点击按钮后调用的函数名是界面和逻辑之间的桥梁。这三样分别对应给人看、给代码看和给事件处理用搞明白这一点后面写代码就不会懵。3.3 代码生成搭好骨架再添肉CVI 有一个非常实用的功能就是在你设计完界面后它可以自动生成程序框架。你会得到一个完整的main函数它会完成初始化、装载界面、显示面板、启动消息循环这些基础操作。同时你界面上每个绑定了回调函数的控件都会自动生成一个空壳版的回调函数你只需要往里面填具体逻辑。这大大降低了入门难度你不需要从零写main函数也不用自己去注册窗口类。以我们要做的正弦波显示程序为例CVI 会自动生成四个函数入口函数main它负责装载和显示面板AcquireCallback负责响应 Acquire 按钮生成波形ClearCallback负责响应 Clear 按钮清除波形QuitCallback负责响应 Quit 按钮退出程序。其中main和QuitCallback的代码是完整的而后两个函数的内部逻辑需要你自己去添加。这种框架自动生成、逻辑自己填充的模式就是 CVI 高效开发的核心所在。4. 第一个例子做一个能显示正弦波的小程序4.1 设计界面放三个按钮和一个波形图前面铺垫了不少现在正式动手。我们做一个小程序界面上有 Acquire、Clear、Quit 三个按钮还有一个波形显示区。点击 Acquire生成一条正弦波并画出来点击 Clear波形消失点击 Quit程序退出。启动 CVI 后选择 File 菜单里的 New再选择 User Interface 来创建一个.uir文件画布上会出现一个空白的 Panel。在这个空白面板上单击右键会弹出一个快捷菜单里面列出了所有可用的控件类型。我们这个程序需要三样东西三个命令按钮和一个图形显示控件。从快捷菜单里选择 Command Button再选 Square Command Button创建出一个方形按钮同样的方式创建三次。再从菜单里选择 Graph创建一个图形显示控件。然后用鼠标把它们拖到你喜欢的位置。命令按钮在 CVI 里的名字叫 Command Button图形显示控件叫 Graph。如果你找不到某个控件记住这个命名规律会方便很多。所有控件都在右键菜单里按类别排好按钮属于 Command Button 类绘图属于 Graph 类。对于纯新手比较稳妥的第一步是只添加这四种元素不要添加其他控件分散注意力。4.2 修改属性给控件起名和绑定回调界面元素摆放好后要逐个修改属性。双击 Panel 的空白处会弹出 Panel 的属性设置窗口把 Panel Title 改为 Demo Panel同时勾选 Auto-Center Vertically 和 Auto-Center Horizontally这样程序运行时面板会在屏幕中央显示。双击 Graph 控件的蓝色区域会弹出它的属性窗口把 Label 改为 ShowWave这个 Label 就是这个控件的名字。双击 Acquire 按钮弹出 Command Button 的属性窗口这里有三项要改Constant Name 改为 ACQUIRECallback Function 改为 AcquireCallbackLabel 改为 Acquire。同样的方法修改另外两个按钮Clear 按钮的 Constant Name 为 CLEARCallback Function 为 ClearCallbackLabel 为 ClearQuit 按钮的 Constant Name 为 QUITCallback Function 为 QuitCallbackLabel 为 Quit。全部改完后记得保存文件命名为 FirstSample.uir。这里特别说明一下回调函数这个概念。AcquireCallback这个函数本身不需要你手动创建CVI 的代码生成功能会自动生成它的空壳。你现在在属性窗口里填的这个名字是在告诉 CVI以后 Acquire 按钮的点击事件就交给这个名字的函数去处理。 同理对于 Clear 和 Quit 也是同一个道理。这一步能顺利做下来整个工程的骨架就有了。4.3 生成代码框架让 CVI 替你搭好 main界面设计完成后把.uir编辑窗口切换为当前活动窗口在菜单栏选择 Code → Generate → All Code。弹出生成代码的设置对话框在 Select QuitUserInterface Callback 里勾选 QuitCallback意思是退出程序时调用它。其他选项先不用动直接点击 OKCVI 会自动生成一个完整的.c源文件里面包含了程序的框架代码。生成的代码大致如下所示注意看它的结构main函数里已经写好了初始化和界面装载#include cvirte.h #include userint.h #include FirstSample.h static int panelHandle; int main (int argc, char *argv[]) { if (InitCVIRTE (0, argv, 0) 0) return -1; /* out of memory */ if ((panelHandle LoadPanel (0, FirstSample.uir, PANEL)) 0) return -1; DisplayPanel (panelHandle); RunUserInterface (); DiscardPanel (panelHandle); return 0; }这段代码里InitCVIRTE是初始化 CVI 运行时环境LoadPanel负责把刚才设计好的FirstSample.uir加载到内存里DisplayPanel让面板显示在屏幕上RunUserInterface启动了消息循环——程序会一直运行在这里直到某个回调函数调用了QuitUserInterface。你不需要修改main函数里的任何内容理解它的流程即可。同时生成的还有三个回调函数的空壳以及头文件FirstSample.h。你注意到没有代码里引用了FirstSample.h这个头文件是 CVI 根据你的界面文件自动生成的里面用#define定义了每个控件的常量标识符比如PANEL、ACQUIRE、CLEAR、QUIT。这些常量在代码里直接使用背后对应的就是你在界面里摆放的各个控件。4.4 添加逻辑用函数面板生成正弦波现在到了整个程序最核心的部分往AcquireCallback和ClearCallback里填充逻辑。先把光标定位到AcquireCallback函数里case EVENT_COMMIT:的下面空白处这一步很重要因为 CVI 的函数面板插入功能会把生成的代码插到光标所在的位置。接下来在菜单栏里选择 Library → Advanced Analysis这一步会打开函数面板库。CVI 把函数按类别组织好了诸如信号生成、信号处理、曲线拟合等功能都归在 Advanced Analysis 库里。在弹出的对话框里选择 Signal Generation再选 Sine Pattern打开正弦波生成函数面板。你需要在这个面板上填写参数output array填wave这是存放波形数据的数组命名要一致samples填100意思是生成 100 个采样点cycle填1表示生成一个完整周期的正弦波。填好参数后点击插入按钮CVI 会自动把完整的函数调用代码写到光标位置。插入的代码大致长这样double wave[100]; SinePattern (100, 1.0, 0.0, wave);其中SinePattern是 CVI 提供的库函数它根据你传入的采样点数和周期数在数组里生成对应的正弦波数据。1.0是信号的幅值0.0是初始相位wave是接收数据的数组。但注意这只是生成了数据还没有把数据显示到 Graph 控件上。要让波形显示出来还需要再调用一次绘图库函数。同样的方式在 Library 里找到 User Interface 相关的绘图函数把wave数组和 Graph 控件关联起来让数据在ShowWave这个控件上画出来。4.5 把波形画出来关联数据与控件在同一个函数里紧接着刚才生成的正弦波数据我们用一个 Graph 控件专属的函数把数据显示出来。从函数面板里选择 Plot 相关的操作需要指定数据来源和画布对象。画布对象直接填panelHandle和ShowWave数据来源填刚才生成的wave数组再指定一下点数100。画出波形的那行代码核心函数大概长这样PlotWaveform (panelHandle, PANEL_SHOWWAVE, wave, 100, VAL_DOUBLE, VAL_FLOAT, VAL_WHITE, VAL_SOLID, 1, VAL_ZERO, 0.0, 1.0);先解释一下这些参数。panelHandle是面板的句柄告诉函数我们要在哪个窗口上画PANEL_SHOWWAVE是目标控件的常量名指定画在哪个控件上wave是数据数组100是数据点的个数VAL_DOUBLE表示数据以 double 类型传入VAL_FLOAT表示以浮点数类型绘制后面几个参数分别控制颜色、线型等视觉属性新手阶段先不深究照着填就能画出可见的波形。简单理解这一行就是告诉 CVI帮我把 wave 数组里的数据以一条白线的方式画在界面的波形区里。ClearCallback 的逻辑就要简单多了一条删除绘图内容的函数调用即可。在 ClearCallback 的 EVENT_COMMIT 分支中加入清除绘图的代码。ClearGraph (panelHandle, PANEL_SHOWWAVE);这一行执行后所有在这个 Graph 控件上绘制过的数据都会被清除。QuitCallback 不需要你写任何代码CVI 生成的框架里已经有QuitUserInterface(0)它会退出消息循环程序随后执行DiscardPanel清理资源并退出。完整写完后整个流程就闭环了用户点 Acquire → 程序生成正弦波数据 → 在 Graph 上画出来 → 用户点 Clear → 波形被清除 → 用户点 Quit → 程序退出。这一个小程序走下来你对 CVI 的工作模式会有整体认识界面拖控件、属性设回调、代码生成器搭框架、函数面板插逻辑。4.6 编译运行点一下就能看到波形代码全部填好后保存源文件然后点击编译运行按钮。程序启动后你会看到一个标题为 Demo Panel 的窗口上面有三个按钮和一个空白波形图。点击 Acquire一条正弦波立刻出现在图形显示区点击 Clear波形消失点击 Quit程序关闭。如果顺利看到这几步恭喜你你已经可以在 CVI 里完成一个完整的小应用了。关于参数修改有一个值得一试的练习把SinePattern里的周期数从1改成3运行后再点 Acquire你会看到屏幕上出现三条首尾相连的正弦波。这个改动会让你直观体会到改代码里的数据 → 影响界面输出这个过程也能帮你建立对函数参数的敏感度。我强烈建议你动手改一次因为看十遍教程不如亲手调一次参数这一改你才会真正理解了数据和图形之间的关系。5. 新手最容易踩的坑编译报错和界面没反应5.1 提示找不到头文件或常量名未定义新手很常见的一个报错是编译时说某个常量或头文件找不到比如PANEL未定义。这个问题的原因通常是你修改了界面控件的 Constant Name 之后没有重新生成代码或者修改界面后没有重新生成头文件。CVI 的头文件是自动生成的你改完控件属性后需要让头文件同步更新。解决办法很直接在.c文件里检查#include的那行头文件名是否和你的.uir文件名一致确认一致后回到.uir界面编辑窗口重新执行一次 Code → Generate → All Code。只要你重新生成了代码头文件里的常量定义就会刷新。记住一个原则先改界面再生成代码这个顺序不要反了。5.2 点击按钮没有反应程序没有任何输出有时程序能启动、界面能正常显示但点了 Acquire 按钮后什么也不发生。最常见的原因是回调函数压根没有绑定上或者回调函数里代码有问题。你可以先检查属性设置打开按钮的属性窗口看 Callback Function 那一栏是否填写正确并且是否和代码里的函数名完全一致——注意大小写和拼写必须一模一样。还有一个隐蔽的错误是代码里的 EVENT_COMMIT 判断分支没有真正执行到因此可以先在case EVENT_COMMIT:分支里加一个断掉或弹窗提示运行时点击按钮观察是否进入函数这个方法会帮你快速定位问题。5.3 波形生成了但 Graph 上什么都不显示数据生成了、代码也执行了但图形显示区依然是空的。这种情况的根源大概率出在PlotWaveform的参数上特别是控件 ID 写错。检查组件的常量名是不是PANEL_SHOWWAVE和界面里 Graph 控件的 Constant Name 是否匹配。另外检查数据数组的类型与长度是否和绘图函数期望的一致有时候波形其实画出来了但因为坐标范围不合适导致看不到可以试着调用自动缩放坐标轴的函数让波形自动适配显示区域。5.4 调试工具用起来断点是你最好的朋友还有一点想跟你分享的是调试技巧一定要学会用 CVI 的断点功能。在代码编辑器左侧点击一下就会出现红点程序运行到这一行时会停下来你可以逐步查看变量的当前值。我第一次写回调函数时总是搞不清数据到底有没有生成后来在SinePattern那行设了断点运行后停下来去看wave数组的内容一眼就发现了问题所在。在我学 CVI 的那段时间里一度觉得断点是老手才用的东西但实际上新人才更需要它——因为它能让你看到程序运行到某个点时到底发生了什么省去很多瞎猜时间。调试面板里还可以直接查看数组的前几十个元素这对确认波形数据是否生成非常有用。6. 下一步学什么从正弦波到真正的测控系统做完第一个正弦波程序你已经掌握了一套完整的 CVI 开发链路创建界面、放置控件、设置回调函数、生成代码骨架、用函数面板添加逻辑、编译运行和调试。接下来有两个大的学习方向一个是往纵深去研究 CVI 作为上位机平台的核心功能比如定时器控件、数据采集库函数、仪器通信库函数另一个是横向扩展自己的项目经验尝试把界面、数据采集和存储这几块能力拼装成真正能用的工具。我最想建议你的三个项目练习难度从低到高每一级都是围绕真实需求设计的表格里写的信号模拟采集器继续做深具体做法是在界面上加一个定时器控件如果放在 CVI 里就是 Timer 控件让它每隔 100 毫秒触发一次回调在回调里读取一个模拟数据源然后实时在 Graph 上刷新显示这就是一个简化版的心电图或信号监测界面。这个练习能让你学会如何处理持续产生的数据和前面点按钮生成一次波形的静态方式完全不同。简易版电压记录系统是另一个好方向如果你手头有支持 CVI 的采集卡或串口设备试着去调用厂商提供的驱动函数读回一个真实的电压值换算成物理量后显示在界面上同时把数据实时写入文本文件。这个练习打通了硬件到软件的完整链路哪怕数据不精确、界面很简陋也远比停留在软件模拟阶段更有收获。第三个方向是波形数据回放器把之前正弦波程序里生成的波形数据保存到本地文件再写一个独立的读取程序从文件里把数据读出来重绘到 Graph 上。这个小项目会让你切身体会到数据存储和读取在测控系统里有多重要也会让你对 CVI 里文件操作函数的使用变得熟练。完成这三个练习后你已经具备独立搭建一套基础测控系统原型的底气了。最后给你一句经验之谈也是我自己的血泪教训学 CVI 最容易犯的错误就是沉溺于界面设计的细节让按钮抠得特别精致、配色特别讲究然后始终不敢去碰代码部分。实际上 CVI 的价值是在于快速把逻辑跑通界面能做到看得清楚就行时间要花在回调函数和数据流身上。从那以后我每次写 CVI 程序都强制自己先保证核心逻辑能编译、能运行界面从简等逻辑验证没问题了再回头优化体验。希望这篇入门文能给你的 CVI 学习之路开个好头也希望你从第一个正弦波程序开始慢慢找到用 C 语言做测控的感觉动手写出属于你的第一个上位机工具。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑