王海涛,陈后金
(北京交通大学 电子信息工程学院,北京 100044)
随着电子仪器与计算机技术的发展,数据采集技术在智能仪器、国防军事、工业生产和科学研究等领域得到了广泛应用。
传统的数据采集仪器的软件和硬件都是由厂商预先定义、预先封装的,这样,他们的灵活性就比较差。美国国家仪器公司(NI 公司)推出的交互式C语言开发平台—LabWindows/CVI,将C语言平台与用于数据采集、分析、处理和显示的测控专业工具结合起来,给熟悉C 语言的开发设计人员编写自动测试环境、检测系统、数据采集系统等应用软件提供了一个理想的软件开发环境。
现在,常用的采集方式是在计算机或者工控机安装外围控制接口(PCI)或工业标准总线接口(ISA)的数据采集卡。虽然这些板卡自带了设备驱动程序和数据采集函数库,在软件编程时可以直接调用自带的数据采集函数库实现数据采集和显示,但是,这些板卡通常价格昂贵、安装麻烦、容易受机箱内环境的影响,而且计算机或者工控机插槽数量有限,不能挂载很多设备。而通用串行总线(USB)的出现很好地解决了上述问题。
本文把LabWindows/CVI与USB结合起来,设计了一种基于LabWindows/CVI软件开发平台和USB2.0接口的数据采集系统,这个系统通过普通的USB连接线与计算机连接,支持即插即用,使用简单方便,既避免了传统仪器灵活性差,又不会有现在仪器成本高的缺点。
该数据采集系统由USB2.0接口芯片CY7C-68001、控制和计算芯片TMS320C6713、模数转换芯片AD7865及扩展接口芯片CPLD等组成。AD7865采集到电压和电流数据以后,经过TMS320C6713写入到CY7C68001芯片的端点,再通过USB总线传给上位机。系统硬件结构如图1。
图1 系统硬件结构框图
LabWindows/CVI以ANSI C为编程语言,它开发的应用程序不能直接调用Windows系统的应用程序编程接口。但是,在Windows平台下,应用程序和USB设备之间的通信必须通过计算机系统的应用程序接口(API)函数。这样,要想与USB设备通信,就必须在LabWindows/CVI平台开发的应用程序与Windows API函数之间建立联系。LabWindows/CVI开发的应用程序可以调用动态链接库(DLL)里面的函数,而DLL文件可以调用Windows的API函数,因此,DLL文件就是两者建立联系的桥梁。
本文将计算机与USB通信的各种API函数(比如打开一个存在的USB设备)都封装了起来,做成了一个DLL文件,然后在应用程序中进程加载和调用,进而访问USB设备,进行数据的采集和传输。这样做也便于程序的管理和代码的重复利用,并且很好地保护了代码的知识产权。
表1中列出了要加入的几个函数的声明及功能。其中:
myRequest和bulkControl分别是EZ-USB设备驱动头文件ezusbsys.h中定义的VENDOR_OR_CLASS_REQUEST_CONTROL和BULK_TRANSFER_CONTROL的结构体类型。
DLL文件可以在VC++ 6.0的环境下开发:
表1 DLL函数及功能
(1)建立一个Win32 Dynamic-Link Library的工程。
(2)在选择创建什么类型的DLL选项中选择“建立一个简单的DLL工程”。点击“完成”。
(3)在工程中加入必要的头文件和自己编写的函数,编译连接就可以生成LIB和DLL文件。
要在LabWindows/CVI中使用这个DLL函数,必须把DLL、LIB和主头文件拷贝到应用程序的文件夹下,并且在应用程序工程中添加LIB和C文件中包含拷贝过来的.h文件。
在本系统中,为了实现数据采集、显示并行处理,采用了多线程技术。这样就可以把这些任务分配给不同的线程,次线程完成数据的实时采集,而主线程完成显示和存储等。这样就能最大限度地保证数据的实时性,并且能及时响应界面的其他操作。
2.2.1 线程池的使用
LabWindows/CVI提供了两种机制实现多线程操作。这两种机制是线程池和异步定时器。线程池技术比较适用于运行不连续的任务或者是一个死循环;而异步定时器技术则比较适用于执行一些有规律的中断。根据本系统的实际情况,本文采用了线程池的机制。应用程序以用户界面为主线程,完成数据的显示和输入响应的任务。而数据采集线程则在响应用户界面采集指令后启动,并且它作为后台线程负责数据的采集,根据界面存储指令进行存储,而主线程则在发生一定的触发事件后进行数据的显示。
要使用LabWindows/CVI提供的线程池技术创建采集线程,必须调用Utility Library中的CmtScheduleThreadPoolFunction函数,并且将要在采集线程中运行的函数的名称传递给它,这样,线程池会安排一个空闲的线程来执行这个函数中的代码。传递给CmtScheduleThreadPoolFunction的函数成为线程池函数,它必须有下面的格式:
int CVICALLBACK ThreadFunction (void*functionData);
而主函数还需要调用库中的CmtSchedule-ThreadPoolFunction函数创建一个新的线程来执行用户指定的线程函数。这样,用户自定义的函数就可以在次线程中执行,同时,主线程执行主函数的回调而无需等待次线程的执行。
在主线程退出之前,需要调用CmtWait-ForThreadPoolFunctionCompletion函数,这样就可以使主线程等待线程池中的函数结束后再退出,不会造成次线程不能正确清理分配到的资源。
2.2.2 线程访问数据保护
在使用多线程时,必须考虑的问题就是数据的保护。因为某些变量可能同时被多个线程访问而发生冲突,导致程序运行错误。这些变量包括全局变量、动态分配内存的变量和静态局部变量。而LabWindows/CVI提供3种数据保护机制:线程锁、线程安全变量和线程安全队列。
因为本系统中数据采集线程和主线程之间有大量的数据进行传递,所以选用的是线程安全队列。线程安全队列是一种在线程间进行安全数组数据传递的机制,所以特别适用于在程序中有一个线程生成数组数据、而另外一个线程对数组数据进行处理的情况。线程安全队列(TSQ)是一个先进先出的队列,而且可以响应特定的事件。TSQ的工作过程为:(1)在主线程中创建一个TSQ,给它装载一个写回调函数,并且设置参数让TSQ在写满缓冲区时触发事件。(2)在主界面采集命令发出后,线程池分配一个空闲线程给数据采集函数,这样,该函数把获得的数据都写入TSQ中,假设TSQ缓冲区长度被设置为n,当缓冲区写满时,便会触发主线程的显示回调函数。(3)在显示回调函数中,主线程将数据从TSQ读出,存放在自定义的数组中,完成后续的处理和显示。这样,数据采集和显示就会同步进行。
程序控制和数据传输流程如图2。
图2 程序控制和数据传输流程图
本文基于LabWindows/CVI这一软件平台,设计了一种高速的USB2.0系统。运用多线程技术和DLL技术,极大地提高了系统性能,增强了系统的实时性,降低了系统的成本。经过测试,本系统达到了设计要求,运行良好。
[1]王海涛,付 钧,鲜 勇,等. 基于LabWindows/CVI的数据采集系统软件设计[J]. 舰船电子工程,2010(5):64-66.
[2]陈矫阳,陈 楸,刘桓龙. 基于LabWindows/CVI多线程数据采集的研究[J]. 科学技术与工程,2008(9):2459-2461.
[3]孔 丽,于红芸,张 利. LabWindows/CVI中调用VC动态库的方法与实例[J]. 海军航空工程学院学报,2004(3):364-366.
[4]王建新,杨世凤,隋美丽. LabWindows/CVI测试技术及工程应用[M]. 北京:化学工业出版社,2006.