在工业控制领域,实时数据采集是一个最基本且最重要的环节。如何提高采集速度一直是工控技术人员所关心的问题。作为一个实时操作系统,MS-DOS曾给工控软件提供了一个非常好的平台。在DOS环境下,利用Turbo C或其他语言,可以很方便地开发出具有极高速度的实时数据采集软件。然而,要在DOS下设计出一个图文并茂、操作方便的用户界面,却是一件费时费力的工作。Windows95的出现,给计算机用户展现了一个焕然一新的画面。多窗口,多任务,以鼠标为主的操作方式,这在原先的DOS环境下是无法想象的。同时,也涌现出大量Windows95环境下的软件开发工具,如:Visual C++,Visual Basic,Borland C++,Borland Delphi等。利用这些新的开发工具,可以很方便地设计出符合Windows95标准的用户界面。Turbo C已不再能满足人们的需要。然而,作为代价,WIN95的实时性却大大下降。WIN95的多任务机制使得一个程序不能独占全部的资源,也使得实时数据采集难以实现。因此,许多工控技术人员至今仍在DOS环境下编写数据采集软件。然而,习惯了Windows界面后,很少有用户愿意回到DOS下去面对那枯燥无味的屏幕,许多工控软件用户在协议中直接提出了要在WIN95下运行的要求。因此如何在WIN95下设计出高速数据采集程序已成为工控技术人员关心的焦点。
1 定时中断存在的问题
一个基本的数据采集系统通常包括A/D和D/A两个部分。在许多场合下,A/D和D/A操作都要精确定时。通常的办法是利用计时器中断,先设定定时间隔,然后对计时器中断进行挂钩,在中断程序中进行所需的操作。在数据量较大时,发送和采集的数据都只能以文件的格式存储在硬盘上。中断程序的任务是:
①从文件中取出数据;
②必要时对数据进行转换;
③通过D/A卡发送数据;
④从A/D卡读取数据;
⑤必要的数据转换;
⑥将数据存入文件;
⑦其他的数据处理工作。
在DOS下设计程序时,我们就是这样做的。在Windows环境下,通过编写设备驱动程序(.VXD),仍然可以实现对定时中断的挂钩。然而,如果将同样的代码放在中断程序中,就会出现问题。
①中断程序过长,当定时间隔很小,即采样频率较高时,会造成死机。
②由于Windows95的多任务机制,在中断程序中进行文件处理,会造成系统的崩溃。
在DOS下之所以这样设计程序,就是因为DOS是单任务操作系统。而Windows95是一个多任务操作系统,当同时有多个任务存在时,在中断程序中对文件进行操作可能会造成系统函数的重入,导致系统的崩溃。实验证明,这种崩溃是必然的,而且是致命的,只有通过Reset键才能恢复。
那么,如何解决这些问题呢?
2 解决的办法
Windows为我们提供了解决的办法:
①采用Windows提供的定时器代替硬件定时中断。使用这种方法非常安全和简单,可以在定时函数内进行任何操作,决不会造成死机现象。然而,Windows定时器的实时性非常差,既不能保证定时精度,也不能保证中断次数,而且,定时间隔也不能太短(>55 ms)。显然,即使对于精度要求很低的数据采集系统,利用Windows定时器作为解决办法也不能令人满意。
②利用Windows提供的多媒体定时器。顾名思义,这种定时器原本是为多媒体服务而设计的,例如MIDI,然而,也可以将其另作它用。因为多媒体定时器的精度非常高,与硬件中断相当,而且也比较安全,不容易造成死机现象。唯一的缺点是中断频率太低,最高只能达到1 kHz,即每毫秒中断一次。因此无法用于有较高速度要求的系统,当然,对于较低速度的数据采集系统。多媒体定时器仍是一种极好的选择。
③利用Windows95 的多线程,并与硬件中断相结合,实现高速数据采集。这正是下面要介绍的方法,利用这种方法,可以实现Windows95下的高速数据采集,并从容地对数据进行处理和保存。
3 多线程和双缓冲
为了保证数据采集的精度和速度,仍然采用了硬件中断的办法,利用硬件定时器(如AD卡上的定时器)产生中断,并编写VXD为其设置相应的中断处理程序。为了提高中断速度以及避免函数重入,去掉了中断过程中一切与系统调用有关的部分,将中断程序缩至最短。这样,中断程序所做的就是读取AD卡上的数据,将其送到一个公用的缓冲区,并维护一个缓冲区指针,而把数据处理和文件存盘的工作留给处理线程去完成。
公用缓冲区由两个大小相同、且连续的子缓冲区组成,称为双缓冲,用于暂存AD转换的数据。每次定时中断到达时,中断程序从AD卡上读一个数据放入缓冲区,并将缓冲区指针加1,当两个子缓冲区全部填满后,将缓冲区指针归0,后读入的数据冲掉原来的数据。
开始中断采集的同时,由主程序启动一个线程,称为处理线程。处理线程在后台与主程序并行工作,用它监视双缓冲指针。当发现双缓冲指针从子缓冲区1进入子缓冲区2时,说明中断程序已将子缓冲区1填满,这时,处理线程对子缓冲区1中的数据进行处理并存盘。同理,当发现双缓冲指针从子缓冲区2回到子缓冲区1时,对子缓冲区2中的数据进行处理和存盘。
只要缓冲区足够大,即使在采集频率非常高的情况下,中断程序填满一个子缓冲区的时间也会比较长,而处理线程进行一次处理和存盘的时间相对比较短。因此,中断程序和处理线程可以并行地访问缓冲区而不会发生交叠,即中断程序访问的子缓冲区与处理线程访问的子缓冲区总是不同的。这样,就可以在不丢失任何一个数据的情况下实现长时间连续高速采集。该方法实现的框图如图1。
图1 利用双缓冲和多线程提高数据采集速度 这实际相当于在PC机的内存中开辟了一片FIFO队列。与硬件FIFO相比,这种方法大大降低了成本,而且,由于将其放在内存中,可以轻易地得到非常大的FIFO队列,减轻了数据处理的负担。另外,由于采用了双缓冲的结构,避免了数据访问的交叠,实现了不丢失任何数据的长时间连续高速采集。当然,由于采用中断方式采集数据,每次采集都要占用CPU时间,使采集速度受到一定限制,无法达到采用硬件FIFO队列的水平。
4 应用
运用上述方法,成功地完成了对MTS疲劳实验系统的改造。该系统是美国MTS公司的产品,主要用于材料疲劳实验。由于存在操作复杂,接口、存储设备不兼容等一系列问题,需要进行改造更新。在分析清楚其工作原理后,用一台586计算机、A/D卡、D/A卡和通信卡替换了原来由PDP11计算机构成的系统,并用Visual C++在Windows95环境下为其设计了软件。
该软件的核心问题是通过D/A卡向实验机构发送用户设定的波形,实验机构根据这一波形驱动液压机构,对实验件施加相应的力。同时,实验件及液压机构上都安装有传感器,计算机还必须通过A/D卡采集这些传感器信号,并进行处理和存盘。由于波形是通过软件产生的,为了保证波形的精度,必须采用精确的定时。利用A/D卡上的定时器产生硬件中断,编写设备驱动程序VXD对此中断进行挂钩,在中断过程中通过D/A卡向实验执行机构发送波形数据,并对传感器信号进行数据采庥。为了产生平滑的波形,就需要较高的中断频率。
利用上述方法,为A/D和D/A分别开辟了一个双缓冲,启动一个线程对两个双缓冲中的数据进行处理。波形(D/A)数据预先存放在文件中,处理线程根据缓冲区指针的位置,定期从文件中读出数据,填充到各个子缓冲区中去;同时对A/D缓冲区中数据进行处理,并存入文件。这样,留给中断程序的工作就只剩下:
①从D/A缓冲区中取出数据送给D/A卡;
②从A/D卡上读取数据送到A/D缓冲区;
③缓冲区指针加1;
④启动A/D转换,为下一次读取做准备。
中断程序的代码被缩至最短,并通过嵌入汇编的方法对其进行优化,中断频率因此大大提高。根据实际应用的需要,并考虑到安全性与稳定性等因素,程序最后采用了30 kHz的采样频率。在主频为100 MHz的奔腾计算机上,该程序运行稳定,用示波器观察D/A卡发送的波形,没有任何失真现象。若采用速度更高的CPU,中断频率还可进一步提高。
在Windows95环境下设计程序,使用户界面极其友好。如果用户愿意,可以不使用键盘,只靠一个鼠标就能完成全部实验过程。通过软件示波器可在电脑屏幕上实时观察发送的波形和采集到的波形,可方便地实现波形的打印和打印预览,自动对采集的数据进行处理和存盘,并最大限度地防止用户的误操作。