李菊叶
(陕西理工学院计算机科学与技术系,汉中723003)
在药品生产工艺流程中,将药物原料加工制成具有一定规格的药物制品,其中温度是影响药品质量的一个重要因素.为避免药品有效成分损失或破坏,在药物加工过程中,我们需要对反应罐的温度进行实时监测.本文实现了对陕西汉江药业公司化学车间生产过程中的化学反应罐温度的实时监测.
在Windows平台下搭建以IPC为核心的多通道实时数据采集系统,硬件上选用高速数据采集卡,软件上采用多线程编程技术及数字滤波技术,以满足高速度、高精度的数据采集、处理、显示等多任务要求.
系统硬件由工控机系统、多功能数据采集卡,信号调理电路组成.采集卡选用研华科技生产的PCI-9113A.每片 PCI-9113A具有 32位 PCI总线,32通道单端或16通道差动输入;卡上带有可存储1024个数据的FIFO,即插即用 .
硬件结构上由温度采集模块和数据处理模块两部分组成.采集模块通过温度传感器将实时温度物理量转换成模拟信号,再由A/D转换器转换为数字信号并传至计算机,由计算机进行温度连续采集.一个采集模块可以控制29个温度传感器,为了是采样数据更准确,在每个检测单元中设置多个采样传感器,并将其放置在制药罐内部不同位置上.硬件原理框图如图1所示.
图1 硬件原理框图
温度采集系统的软件设计包括参数设置、采集、处理、显示等模块.参数设置模块主要负责对数据采集系统的运行参数进行设置.运行的参数有:采样通道号、采样点数、采样周期及各路的上下限报警参数等;采集模块负责从数据采集卡上连续不断地读取采样数据;显示模块实时温度、温度曲线等数据的实时变化.基于实时性的要求,采样需要不间断运行,显示也同样需要连续不间断,而且显示模块所需要的数据必须来源于采样模块.因此,两个模块不仅需要各自同时独立运行,还要互相协作,并能实时响应用户发来的指令,为了满足这种采样需求,我们需要运用多线程技术.
PCI-9113A数据采集卡具有很完善的软件支持.在各种软件环境下均可使用,本系统所使用的软件开发工具C++Builder,其具备功能强大、高效及界面友好等特点,但并不具备直接对硬件I/O地址进行访问的能力,需要调用DLL函数实现对I/O口的访问和控制.PCI-9113A的开发商为该卡提供了标准动态连接库文件,通过调用动态连接库中的函数很容易对硬件设备进行底层的操作.
bool__fastcall TMainForm::StartSample(void)//启动数据采集卡
{
if(DemoFlag)return true;//测试
Card_9113=Register_Card(PCI_9113,0);
if(Card_9113<0)
return false;
else
{
AI_9113_Config(Card_9113,TRIG_INT_PACER);
AI_AsyncDblBufferMode(Card_9113,FALSE);
return true;
}
}
在Windows NT和Window s 9x中,多线程的编程实现需要调用一系列的 API函数,如 CreateThread、ResumeThread等,比较麻烦且容易出错.我们使用Inprise公司的新一代RAD开发工具C++Builder,可以方便地实现多线程的编程.我们利用C++Builder提供的TThread对象,多线程的编程变得非常简便易用.具体实现方法如下:
在C++Builder IDE环境下选择菜单File New,在New 栏中选中Thread Object,按OK,接下来弹出输入框,输入 TThread对象子类的名字My Thread,这样C++Builder自动为你创建了一个名为TMy Thread的 TThread子类.同时编辑器中多了一个名为Unit2.cpp的单元,这就是我们创建的TMy Thread子类的原码.
制药罐温度检测系统的应用程序设计中的三大部分即(数据采集模块、数据处理模块及显示模块)作以介绍,其它部分从略.
2.2.1 采集子线程
在C++Builder环境下,利用 GlobalAllocPtr函数来获得一段内存空间,主要用来存放采样来的数据.但Windows操作系统中采用的是虚拟存储的管理机制,通过GlobalAllocPtr函数获得的这段内存空间随时都有可能被置换到硬盘上,在读写硬盘时所耗费的时间,会导致采样的不连续性.为了消除这种不利因素,可以在将缓冲区的数据送往输入设备之前,首先调用GlobalAllocPtr函数以确保缓冲区的数据不会直接被置换到硬盘上.但是,如果仅仅只为输入设备开辟一个新的缓冲区,则当该缓冲区的空间被采样的数据填充满后,输入设备就没有缓冲区可以利用,结果导致采样的停止,从而造成了采样的不连续,所以在实际应用中 ,至少要为输入设备准备两个缓冲区才是合理的.采集模块的流程图见图2.
图2 采集子线程的流程图
void__fastcall My Thread::Execute()
PCI-9113A数据采集卡提供了非常丰富的采集函数,通过直接调用其自身携带的驱动程序来启动数据采集卡,并控制其工作.采集过程中,函数调用过程如下:调用PCI-9113A_CreateDevice函数创建设备对象,采集卡进入待机状态,监视温度的变化并同时等待用户的指令;当用户发出开始采集的命令后,采集、显示子线程创建并启动,采集子线程调用PCI22300_Read DevBulk AD,批量读取数据,一轮采集完成后调用SetEvent函数向显示子线程发出指令,并开始下一轮的采集.
2.2.2 信号处理子线程
因为在检测现场传感器所测到的模拟信号无法完全避免要混杂一些干扰信号,采用数字滤波来减弱或消除干扰的影响,提高测量的精度和可靠性.以及对分厂32个点的温度、湿度(实际使用29个点)进行实时监测、定期巡检并可以对任意采集点设置不同的报警上下限,使其超限报警.采集系统应可以周期(周期应小于等于5 min)自动采集、存储数据.并具有采集点方便扩充能力.32个点的温度、湿度误差应≤±0.2℃(-25~175℃).采集速度应大于32个点/min.所选择的计算机应具有足够大的空间,以存储庞大的温度数据.部分代码如下:
AI_ContScanChannels(Card_9113,31,AD_U_10_V,AI_Buf,512*32,(F64)80000,SYNCH_OP);//均值滤波后换算为温度保存
for(int i=0;i<32;i++)Sum[i]=0;
for(int k=0;k<16384;)
{
for(int i=0;i<32;i++,k++)
{
Sum[i]=Sum[i]+(AI_Buf[k*2]&0x0fff);//************
}
}
for(int i=0;i<32;i++)//采样、保存、显示全部通道,存入数据库时只存入已经开始批号的通道
{
//temp=Sum[i]/512.0*200/4095-25;
//str=str.FormatFloat("0.00",temp);
//RealTimeArray[i].TempData[RealTime-Array[i].SampleCount]=str.ToDouble();
//Temp Val[i]=Sum[i]/512.0*10/4095*19.858721-24.761047;
Temp Val[i]=(Sum[i]/512.0/4095*200-25)*k[i]+b[i];
if(TimerCount==0)
{
if(RealTimeArray[i].SampleCount==Max-Count)RealTimeArray[i].SampleCount=0;//数组中最多保存Max Count个数,超过则从头开始存储
2.2.3 显示子线程
数据实时显示模块主要是为现场工作人员提供很大的方便,他们可以实时观察各个参数之间的关系,为了将数据实时的用曲线图显示出来,同时又不能影响到数据采集的连续不间断性,因此显示模块就需要作为一个独立运行的子线程.然而绘图所必须的数据信息完全依附于采集子线程,即显示模块必须等待采集子线程的一次数据采集任务完成之后,才能执行一次绘图任务.为了实现这一功能则需要显示模块和采集模块运行时,在两者之间进行协调,我们可以通过调用多线程函数 WaitForSingleObject(hEv2ent,IN FIN I T E)函数实现,IN FIN I T E表示永远等待,即显示线程永远等待采集线程的绘图指令.显示模块流程如图3所示.
图3 显示模块流程图
部分代码如下:
void__fastcall TMainForm::Show ChannelData(bool Draw,bool All)//显示该实时通道或查询结果温度数据及曲线
{
int x,y,num;
float high,low,MaxVal,MinVal,DiffVal;
TColor PenColor;
String str,str1;
TDateTime time1,time2;
long difftime;
if(!Draw)return;
bit1->Canvas->Brush->Color=clWhite;//clMoneyGreen;////clBlack;//clInfoBk;
bit1->Canvas->Fill Rect(Rect(0,0,bit1->Width-1,bit1->Height-1));//清除原图形
Label49->Caption="";
Label48->Caption="";
Label53->Caption="";
bit1->Canvas->Pen->Width=1;//绘上、下限曲线
bit1->Canvas->Pen->Mode=pmCopy;//pmXor
……….}
程序中多个线程同时运行,难免要遇到使用同一系统资源,或者一个线程的运行要依赖另一个线程的完成等,这样需要在线程间进行同步的问题.在C++Builder中,为我们提供了用于创建Event的TEvent对象供我们使用.首先创建一个全局的TEvent对象作为所有线程可监测的标志.当一个线程完成某项特定的操作时,调用 TEvent对象的SetEvent()方法,这样将设置这个标志,其他的线程可以通过监测这个标志获知操作的完成.相反,要取消这个标志,可以调用ResetEvent()方法.在需要等待操作完成的线程中使用WaitFor()方法,将一直等待这个标志被设置为止.部分事件同步代码如下:
hEvent=CreateEvent(NULL,FAUSE,TRUE,NULL);
//采集线程请求事件的对象
WaitForSingleObject(hEvent,INFINITE);
//处理采集任务
SetEvent(hEvent);//重置事件对象
系统以工业控制计算机为核心,采用具有多功能数据采集卡PCI-9113A,利用C++Builder的多线程编程技术实现了多通道实时任务数据采集.系统已经投入使用,运行稳定,可靠性高.
[1]马建明.数据采集与处理技术[M].西安交通大学出版社,2005:15-20.
[2]张旭东,付 强,何松华,等.基于PCI接口的多通道高速数据采集系统数据采集与处理[J].系统工程与电子技术,2000,(6):240-244.
[3]张蕴玉,宋宇明,胡修林,等.多通道高速数据采集系统PCI接口的结构设计[J].华中科技大学学报(自然科学版),2004,32(8):73-75.
[4]王常力.工业控制计算机系统[J].电子工业出版社,1993.
[5]陈宽达.C++Builder深度历险[M].武汉:华中科技大学出版社,2002.
[6]石学文,陈 梅,武玉强,等.基于C++Builder的过程控制系统设计[D].华中科技大学硕士论文,2002.
[7]乔宗立.一种在C++Builder环境中调用Matlab数据的方法[J].湖北汽车工业学院学报,2002,15(4):30-33.
[8]刘森林 ,寇光杰.一种基于C++Builder的过程控制系统设计与实现[J].东南大学学报,2003,33(9):158-161.