2.3 数据采集与控制程序设计
使用研华板卡编程之前必须首先安装研华设备管理程序DevicemAnager和32位DLL驱动程序。同时研华提供ActiveDAQ控件,供MATLAB等可视化语言对其板卡编程使用。
图2-20 PC与PCI-1710HG 数据采集卡组成的温度测控线路
2.3.1 模拟量输入
以下利用ActiveDAQ控件实现模拟量输入。
① 在ai.m文件中,设置打开回调函数ai_OpeningFcn (),添加如下代码完成程序初始化的工作,具体代码如下。
function ai_OpeningFcn(hObject,eventdata,handles,varargin) global num; global data; global p1; global p4; global p5; p1=handles.axes1; p4=handles.edit1; p5=handles.edit2; num=0; axes(handles.axes1); xlabel('时间(s)');ylabel('电压(V)'); axis([0 200 0 5]); set(gca,'yTick',[0:0.5:5],'YMinorTick','on'); set(gca,'xTick',[0:20:200],'XMinorTick','on'); axismAnual; hold on; %保持图形 set(handles.pushbutton2,'Enable','off'); %使按钮失效 set(handles.pushbutton3,'Enable','off'); %使按钮失效 guidata(hObject,handles); global t; t=timer('TimerFcn',{@timerCallback,handles.activex1},'ExecutionMode','fixe dRate','Period',1,'StartDelay',0);
② 在ai.m文件中,分别在“板卡设置”、“间断采集”、“连续采集”、“关闭程序”按钮相应回调函数pushbutton1_Callback()、pushbutton2_Callback()、pushbutton3_Callback()、pushbutton4_Callback()中添加代码,分别实现板卡设置、间断、连续采集和关闭GUI窗口功能,具体代码如下。
function pushbutton1_Callback(hObject,eventdata,handles) %板卡设置 handles.activex1.SelectDevice; %选择模拟量输入设备 handles.activex1.OpenDevice; %打开模拟量输入设备 handles.activex1.CyclicMode=true; %循环方式采集数据 handles.activex1.StartChannel=0; %通道号为0 handles.activex1.SampleRate=500; %采样频率 handles.activex1.DataType='adReal'; %模拟量输入返回值为实型set(handles.pushbutton2,'Enable','on'); %使按钮有效 set(handles.pushbutton3,'Enable','on'); %使按钮有效 function pushbutton2_Callback(hObject,eventdata,handles) %间断采集 global num; global data; global t; stop(t); num=num+1; data(num)=handles.activex1.RealInput(1); %获取AI1通道数据(实型电压)set(handles.edit1,'String',num); set(handles.edit2,'String',sprintf('%0.1f',data(num))); draw(); function pushbutton3_Callback(hObject,eventdata,handles)%连续采集 global t; start(t); function pushbutton4_Callback(hObject,eventdata,handles) %关闭程序 delete(handles.figure1); %关闭对话框
③ 右击GUI窗口空白处,选择“View Callbacks”子菜单中的“DeleteFcn”选项,系统自动将光标定位于ai.m文件中的回调函数figure1_DeleteFcn()上,实现删除功能,具体代码如下。
function figure1_DeleteFcn(hObject,eventdata,handles) global t; stop(t); delete(t); %删除时间对象 handles.activex1.CloseDevice; %关闭设备 clear all; %清除所有
④ 在ai.m文件中,添加时间对象t的timerCallback事件回调函数,实现定时数据采集和画图功能,具体代码如下。
function timerCallback(obj,event,hnd) global num; global data; global p4; global p5; num=num+1; data(num)=hnd.RealInput(1); set(p4,'String',num); set(p5,'String',sprintf('%0.1f',data(num))); draw();
⑤ 在ai.m文件中,添加函数draw( ),实现采集数据的绘图功能,具体代码如下。
function draw( ) global num; global data; global p1; tx=0:1:num-1; plot(p1,tx,data,'-r','LineWidth',2); if num>200 p=get(p1,'Children'); delete(p); data=data(num); num=1; end
程序设计、调试完毕,运行程序。
① 首先进行板卡设置:单击“板卡设置”按钮,选中板卡设备:000:{PCI-1710HG I/O=C000Ver.A},单击“Select”按钮。
② 转动电位器旋钮,改变其输出电压(范围是0~5V),线路中AI指示灯亮度随之变化,同时,连续单击“间断采集”按钮或单击一次“连续采集”按钮,程序窗体中文本对象中的数字、图形控件中的曲线都将随电位器输出电压变化而变化。
③ 当测量电压小于或大于设定下限电压值(0.5V)或上限电压值(3.5V)时,程序画面中相应指示灯由绿色变为红色。
程序运行画面如图2-21所示。
图2-21 程序运行画面
2.3.2 模拟量输出
以下利用ActiveDAQ控件实现模拟量输出。
① 在ao.m文件中,设置打开回调函数ao_OpeningFcn(),添加如下代码完成程序初始化的工作,具体代码如下。
global state; %设置数据增大或减小状态global data; global middata; global num; global p1; global p2; state=0; middata=0; num=0; p1=handles.axes1; p2=handles.edit1; ylabel('电压(V)'); axis([0 100 0 10]); set(gca,'yTick',[0:1:10],'YMinorTick','on'); set(gca,'xTick',[0:10:100],'XMinorTick','on'); axismAnual; hold on; %保持图形 handles.activex1.SelectDevice; %选择模拟量输出设备 handles.activex1.Channel=0; %输出的通道号 handles.activex1.OutputRate=500; %输出频率 handles.activex1.DataType='adReal'; %模拟量输出数据类型(实型) handles.activex1.OutputType='adVoltage'; %模拟量输出数据类型(电压) handles.activex1.NumberOfOutputs=200; %输出数据的个数 guidata(hObject,handles); global t; t=timer('TimerFcn',{@timerCallback,handles.activex1},'ExecutionMode', 'fixedRate','Period',1,'StartDelay',0);
② 在ao.m文件中,分别在“连续输出”、“关闭程序”按钮相应回调函数pushbutton1_Callback()、pushbutton2_Callback()中添加代码,分别实现连续输出模拟电压和关闭GUI窗口功能,具体代码如下。
function pushbutton1_Callback(hObject,eventdata,handles) %连续输出 global t; start(t); function pushbutton2_Callback(hObject,eventdata,handles) %关闭程序 delete(handles.figure1); %关闭对话框
③ 在ao.m文件中,在Slider滚动条控件相应回调函数slider1_Callback()中添加代码,实现滚动条与坐标轴控件、编辑框的联动,即随着滚动条的移动,编辑框中将实时显示滚动条的值,并在坐标轴控件中画出曲线,具体代码如下。
function slider1_Callback(hObject,eventdata,handles) global data; global num; global t; stop(t); num=num+1; data(num)=get(handles.slider1,'Value'); handles.activex1.OpenDevice; %打开设备 handles.activex1.RealOutput(data(num)); set(handles.edit1,'String',data(num)); draw();
④ 右击GUI窗口空白处,选择“View Callbacks”子菜单中的“DeleteFcn”选项,系统自动将光标定位于ao.m文件中的回调函数figure1_DeleteFcn()上,实现删除功能,具体代码如下。
function figure1_DeleteFcn(hObject,eventdata,handles) global t; stop(t); delete(t); handles.activex1.CloseDevice; %关闭设备 clear all;
⑤ 在ao.m文件中,添加时间对象t的timerCallback事件回调函数,实现定时数据采集和画图功能,具体代码如下。
function timerCallback(obj,event,hnd) global state; global num; global data; global middata; global p2; num=num+1; hnd.OpenDevice; hnd.RealOutput(middata); data(num)=middata; draw(); set(p2,'String',middata); if state==0 middata=middata+1; if middata>=10 state=1; end else middata=middata-1; if middata<=0 state=0; end end
⑥ 在ao.m文件中,添加函数draw(),实现绘图及刷新功能,具体代码如下。
function draw() global num; global data; global p1; tx=0:1:num-1; plot(p1,tx,data,'-r','LineWidth',2); if num>100 p=get(p1,'Children'); delete(p); data=data(num); num=1; end
程序设计、调试完毕,运行程序。
首先进行板卡设置,选中板卡设备:000:{ PCI-1710HG I/O=C000Ver.A},单击“Select”按钮;单击“垂直滚动条”的上下箭头,生成一间断变化的数值(0~10),在程序画面中产生一个随之变化的曲线。同时,线路中发光二极管亮度随之变化,在示波器中显示程序画面中相同波形。程序运行界面如图2-22所示。
图2-22 程序运行界面
如果没有模拟输出电压(指示灯亮度不变化),可运行设备测试程序:在研华设备管理程序Advantech DevicemAnager对话框中单击“Test”按钮,出现“Advantech Device Test”对话框,通过选择“Analog Output”项进行测试。如果没有问题,再重新运行组态程序。
2.3.3 数字量输入
以下利用ActiveDAQ控件实现数字量输入。
① 在di.m文件中,设置打开回调函数di_OpeningFcn (),添加如下代码完成程序初始化的工作,具体代码如下。
function di_OpeningFcn(hObject,eventdata,handles,varargin) global num; global bz; global p1; global p2; num=0; bz=1; p1=handles.axes1; p2=handles.edit1; plot(1,1,'-o','MarkerFaceColor','g','MarkerSize',52); %鲜绿色报警灯 axis off; %关闭坐标轴 hold on; handles.activex1.SelectDevice; %选择设备 handles.activex1.OpenDevice; %打开设备 handles.activex1.ScanTime=500; %采样频率 guidata(hObject,handles); global t; t=timer('TimerFcn',{@timerCallback,handles.activex1},'ExecutionMode','fixedRate','Period',0.5,'StartDelay',0); start(t);
② 在di.m文件中,在“关闭”按钮相应回调函数pushbutton1_Callback()中添加代码,实现关闭GUI窗口功能,具体代码如下。
function pushbutton1_Callback(hObject,eventdata,handles) %关闭程序 delete(handles.figure1); %关闭对话框
③ 右击GUI窗口空白处,选择“View Callbacks”子菜单中的“DeleteFcn”选项,系统自动将光标定位于di.m文件中的回调函数figure1_DeleteFcn()上,实现删除功能,具体代码如下。
function figure1_DeleteFcn(hObject,eventdata,handles) global t; stop(t); delete(t); handles.activex1.CloseDevice; %关闭设备 clear all;
④ 在di.m文件中,添加时间对象t的timerCallback事件回调函数,实现周期检测数字量端口状态,以及报警功能,具体代码如下。
function timerCallback(obj,event,hnd) global num; global bz; global p1; global p2; hnd.Port=0; %数字量输入0端口 hnd.Bit=0; %数字量输入0端口第0位 zt1=hnd.BitInput; %数字量输入0端口第0位状态 %数字量输入0通道 if zt1==1 plot(p1,1,1,'-o','MarkerFaceColor','g','MarkerSize',52); %鲜绿色报警灯 else plot(p1,1,1,'-o','MarkerFaceColor','r','MarkerSize',52); %红色报警灯 end %数字量输入通道1 hnd.Port=0; hnd.Bit=1; zt2=hnd.BitInput; if zt2==0 & bz==1 num=num+1; set(p2,'String',num); bz=2; end if zt2==1 bz=1; end
程序设计、调试完毕,运行程序。
① 首先进行板卡设置,选中板卡设备:000:{ PCI-1710HG I/O=C000Ver.A},单击“Select”按钮;
② 打开/关闭“电气开关”,线路中DI指示灯1亮/灭,程序画面中信号指示灯亮/灭(颜色改变);
③ 用任何反光物体遮挡/离开“光电接近开关”,线路中DI指示灯2亮/灭,程序画面中开关计数器文本中的数字从1开始累加。
程序运行界面如图2-23所示。
2.3.4 数字量输出
以下利用ActiveDAQ控件实现数字量输出。
① 在do.m文件中,设置打开回调函数do_OpeningFcn(),添加如下代码完成程序初始化的工作,具体代码如下。
function do_OpeningFcn(hObject,eventdata,handles,varargin) global num1; %打开次数 global num2; %关闭次数 global zt; %开关状态 num1=0; num2=0; zt=2; plot(1,1,'-o','MarkerFaceColor','g','MarkerSize',50);%鲜绿色报警灯 axis off; %关闭坐标轴 hold on; handles.activex1.SelectDevice; %选择设备 handles.activex1.OpenDevice; %打开设备
② 在do.m文件中,分别在“打开指示灯”、“关闭指示灯”、“关闭程序”按钮相应回调函数pushbutton1_Callback()、pushbutton2_Callback()、pushbutton3_Callback()中添加代码,分别实现打开指示灯、关闭指示灯和关闭GUI窗口功能,具体代码如下。
function pushbutton1_Callback(hObject,eventdata,handles)%打开指示灯 global num1; global zt; %开关状态 if zt==2 plot(handles.axes1,1,1,'-o','MarkerFaceColor','r','MarkerSize',50);%红色报警灯 handles.activex1.Bit=0; %设置数字输出量第0位 handles.activex1.BitOutput(1); %数字输出量为1值 num1=num1+1; set(handles.edit1,'String',num1); zt=1; end function pushbutton2_Callback(hObject,eventdata,handles)%关闭指示灯 global num2; global zt; %开关状态 if zt==1 plot(handles.axes1,1,1,'-o','MarkerFaceColor','g','MarkerSize',50);%鲜绿色报警灯 handles.activex1.Bit=0; %设置数字输出量第0位handles.activex1.BitOutput(0); %数字输出量为0值 num2=num2+1; set(handles.edit2,'String',num2); zt=2; end function pushbutton3_Callback(hObject,eventdata,handles)%关闭程序 delete(handles.figure1); %关闭对话框
③ 右击GUI窗口空白处,选择“View Callbacks”子菜单中的“DeleteFcn”选项,系统自动将光标定位于do.m文件中的回调函数figure1_DeleteFcn()上,实现删除功能,具体代码如下。
function figure1_DeleteFcn(hObject,eventdata,handles) handles.activex1.CloseDevice; %关闭设备 clear all;
程序设计、调试完毕,运行程序。
① 首先进行板卡设置,选中板卡设备:000:{ PCI-1710HG I/O=C000Ver.A},单击“Select”按钮。
② 单击“打开指示灯”按钮,程序画面中指示灯颜色变为红色,打开次数加1;同时,线路中DO指示灯亮。
③ 单击“关闭指示灯”按钮,程序画面中指示灯颜色变为绿色,关闭次数加1;同时,线路中DO指示灯灭。
程序运行界面如图2-24所示。
图2-23 程序运行界面
图2-24 程序运行界面
2.3.5 温度测控
以下利用ActiveDAQ控件实现温度测量与控制。
① 在ai_do.m文件中,设置打开回调函数ai_do_OpeningFcn (),添加如下代码完成程序初始化的工作,具体代码如下。
function ai_do_OpeningFcn(hObject,eventdata,handles,varargin) global num; %采集数据的个数 global data; %采集数据 global mindata; %下限温度报警值 globalmAxdata; %上限温度报警值 global drawtype; %绘图类型选择 global p1; global p2; global p3; global q1; global q2; global q3; global q4; global r1; global r2; num=0; p1=handles.axes1; p2=handles.axes2; p3=handles.axes3; q1=handles.edit1; q2=handles.edit2; q3=handles.edit3; q4=handles.edit4; r1=handles.activex1; r2=handles.activex3; handles.activex2.SelectDevice; %选择模拟量输入设备 handles.activex3.SelectDevice; %选择数字量输出设备 handles.activex2.StartChannel=1; %模拟量输入设备通道1 handles.activex2.SampleRate=500; %模拟量输入设备采样频率 handles.activex2.DataType='adReal'; %模拟量输入设备输入值为实型 handles.activex2.OpenDevice; %打开模拟量输入设备 handles.activex3.OpenDevice; %打开数字量输出设备 drawtype=1; mindata=20; maxdata=30; set(handles.edit5,'String',mindata); set(handles.edit6,'String',maxdata); axes(handles.axes1); xlabel('时间(s)');ylabel('温度(℃)'); axis([0 200 0 50]); set(gca,'yTick',[0:5:50],'YMinorTick','on'); set(gca,'xTick',[0:20:200],'XMinorTick','on'); axismAnual; hold on; %保持图形 axes(handles.axes2); plot(1,1,'-o','MarkerFaceColor','g','MarkerSize',32);%鲜绿色报警灯 axis off; %关闭坐标轴 hold on; axes(handles.axes3); plot(1,1,'-o','MarkerFaceColor','g','MarkerSize',32);%鲜绿色报警灯 axis off; %关闭坐标轴 hold on; tabinit(); %数据表初始化 guidata(hObject,handles); global t; t=timer('TimerFcn',{@timerCallback,handles.activex2},'ExecutionMode','fixedRa te','Period',1,'StartDelay',0); start(t);
② 在ai_do.m文件中,分别在“绘制曲线”、“绘制散点图”、“保存数据”、“打开文件”、“关闭程序”按钮相应回调函数pushbutton1_Callback()、pushbutton2_Callback()、pushbutton3_Callback()、pushbutton4_Callback()、pushbutton5_Callback()中添加代码,分别实现绘制曲线、绘制散点图、保存数据、打开文件和关闭GUI窗口功能,具体代码如下。
function pushbutton1_Callback(hObject,eventdata,handles) %连续采集数据,绘制曲线图 global drawtype; drawtype=1; function pushbutton2_Callback(hObject,eventdata,handles) %连续采集数据,绘制散点图 global drawtype; drawtype=2; function pushbutton3_Callback(hObject,eventdata,handles)%保存文件 global num; global data; [filename,pathname]=uiputfile( ... { '*.txt','TEXT-files (*.txt)'; ... '*.c','C-Files (*.c)'; ... '*.cpp','C++-Files (*.cpp)'; ...'*.m','M-Files (*.m)'; ... '*.*','All Files (*.*)'},...'保存为'); if isequal([filename,pathname],[0,0]) return; else handles.filePath=fullfile(pathname,filename); fid=fopen(handles.filePath,'w'); str=data; fprintf(fid,'%0.1f ',str); fclose(fid); end guidata(hObject,handles); function pushbutton4_Callback(hObject,eventdata,handles)%打开文件 global num; global data; global t; stop(t); tabinit(); [filename,pathname]=uigetfile( ... {'*.txt;*.c;*.cpp;*.m','TEXT Files (*.txt,*.c,*.cpp,*.m)';... '*.txt','TEXT-files (*.txt)'; ... '*.c','C-Files (*.c)'; ... '*.cpp','C++-Files (*.cpp)'; ... '*.m','M-Files (*.m)'; ... '*.*','All Files (*.*)'},... '打开'); if isequal([filename,pathname],[0,0]) start(t); return; else handles.filePath=fullfile(pathname,filename); fid=fopen(handles.filePath,'r'); str=fread(fid,'*char'); str=str'; data=str2num(str); fclose(fid); %关闭文件 num=length(data); handles.activex1.Col=1; for i=1:1:num handles.activex1.Row=i; handles.activex1.Text=sprintf(' %0.1f',data(i)); end %计算平均值、最大值和最小值 averd=mean(data); set(handles.edit2,'String',sprintf('%0.1f',averd)); mind=min(data); set(handles.edit3,'String',sprintf('%0.1f',mind)); maxd=max(data); set(handles.edit4,'String',sprintf('%0.1f',maxd)); draw(); start(t); end function pushbutton5_Callback(hObject,eventdata,handles) %关闭程序 delete(handles.figure1); %关闭对话框
③ 在ai_do.m文件中,分别在edit5、edit6编辑框控件相应回调函数edit5_Callback()、edit6_Callback()中添加代码,实现设置上下限温度报警值功能,具体代码如下。
function edit5_Callback(hObject,eventdata,handles) %改变下限报警灯的值 globalmAxdata; global mindata; mindata1=str2num(get(hObject,'String')); if mindata1>=maxdata warndlg('设定的上限温度不能比下限温度小,请重新设置!','注意!!!'); set(hObject,'String',mindata); else mindata=mindata1; end function edit6_Callback(hObject,eventdata,handles) %改变上限报警灯的值 globalmAxdata; global mindata; maxdata1=str2num(get(hObject,'String')); %字符串转数字 if maxdata1<=mindata warndlg('设定的上限温度不能比下限温度小,请重新设置!','注意!!!'); set(hObject,'String',maxdata); else maxdata=maxdata1; end
④ 右击GUI窗口空白处,选择“View Callbacks”子菜单中的“DeleteFcn”选项,系统自动将光标定位于ai_do.m文件中的回调函数figure1_DeleteFcn()上,实现删除功能,具体代码如下。
function figure1_DeleteFcn(hObject,eventdata,handles) global t; stop(t); delete(t); %删除时间对象 handles.activex3.Bit=1; %数字量输出第1位 handles.activex3.BitOutput(0); %数字量输出0值 handles.activex3.Bit=2; %数字量输出第2位 handles.activex3.BitOutput(0); %数字量输出0值 handles.activex2.CloseDevice; %关闭模拟量输入设备 handles.activex3.CloseDevice; %关闭数字量输出设备 clear all; %清除所有
⑤ 在ai_do.m文件中,添加时间对象t的timerCallback事件回调函数,实现定时数采集据并进行实时控制、计算、画图等功能,具体代码如下。
function timerCallback(obj,event,hnd) global num; global data; global q1; global q2; global q3; global q4; global r1; num=num+1; u=hnd.RealInput(1); %获取AI1通道数据(电压值) data(num)=(u-1)*50; r1.Col=1; r1.Row=num; r1.Text=sprintf(' %0.1f',data(num));; set(q1,'String',sprintf('%0.1f',data(num))); %计算平均值、最大值和最小值 averd=mean(data); set(q2,'String',sprintf('%0.1f',averd)); mind=min(data); set(q3,'String',sprintf('%0.1f',mind)); maxd=max(data); set(q4,'String',sprintf('%0.1f',maxd)); alarm(); draw();
⑥ 在ai_do.m文件中,添加函数tabinit()、draw()和alarm(),分别实现数据表格初始化、采集数据的绘图和超限报警指示灯功能,具体代码如下。
%数据表格初始化 function tabinit() global r1; r1.Cols=2; r1.Rows=201; r1.Col=0; for i=1:1:200 r1.Row=i; r1.Text=num2str(i); end r1.Row=0; r1.Col=0; r1.Text='序号'; r1.Col=1; r1.Text='温度值'; r1.TopRow=1; %置在第一页 r1.LeftCol=1; function draw() %画连续曲线/间断散点图 global num; global data; global drawtype; global p1; global r1; if drawtype==1 cla(p1); tx=0:1:num-1; plot(p1,tx,data,'-r','LineWidth',2); end if drawtype==2 cla(p1); plot(p1,data,'-ro','MarkerSize',2); end if num>=200 p=get(p1,'Children'); delete(p); data=data(num); r1.Clear; tabinit(); num=0; end function alarm() %报警 global num; global data; global mindata; globalmAxdata; global p2; global p3; global r2; if data(num)<=mindata putvalue(r2,[1 0]); %置16:17通道值为[1 0] plot(p2,1,1,'-o','MarkerFaceColor','r','MarkerSize',32); elseif data(num)<maxdata putvalue(r2,[0 0]); %置16:17通道值为[0 0] plot(p2,1,1,'-o','MarkerFaceColor','g','MarkerSize',32); plot(p3,1,1,'-o','MarkerFaceColor','g','MarkerSize',32); else putvalue(r2,[0 1]); %置16:17通道值为[0 1] plot(p3,1,1,'-o','MarkerFaceColor','r','MarkerSize',32); end
程序设计、调试完毕,运行程序。
① 程序启动,首先进行板卡设置,选中板卡设备:000:{ PCI-1710HG I/O=C000Ver.A},单击“Select”按钮(选择两次)。
② 单击“绘曲线图”按钮,开始采集温度测量值并绘制连续的曲线图;单击“绘散点图”按钮,开始采集温度测量值并绘制间断的散点图。
③ 当测量温度小于设定的下限温度值时,程序中下限指示灯改变颜色,相应线路中的DO指示灯1亮;当测量温度值大于设定的上限温度值时,程序中上限指示灯改变颜色,相应线路中的DO指示灯2亮。
④ 在报警指示区,可以改变下限、上限温度值:在下限指示文本框输入新的下限报警值,按回车键确认;在上限指示文本框输入新的上限报警值,按回车键确认。
⑤ 单击“保存数据”按钮,出现“另存为”对话框,指定路径,输入文件名,将采集的温度值保存到指定的文本文件中。
⑥ 确单击“打开文件”按钮,出现“打开”对话框,选中文件名,打开文件,文件中的数据显示在表格中,并绘制曲线。
程序运行界面如图2-25所示。
图2-25 程序运行界面