3.3 数据采集与控制程序设计
3.3.1 模拟量输入
在编程之前,首先进入“Measurement & Automation”软件窗口参数设置对话框中的AI设置项,设置模拟信号输入时的量程为-10.0~+10.0V,输入方式采用“Referenced Single Ended”(单端有参考地输入),如图3-8所示。
以下是采用MATLAB编写的模拟电压输入参考程序。
① 在ai.m文件中,设置打开回调函数ai_OpeningFcn (),添加以下代码完成程序初始化的工作,具体代码如下。
function ai_OpeningFcn(hObject,eventdata,handles,varargin) global num; global data; global p1; global p4; global p5; global t; p1=handles.axes1; p4=handles.edit1; p5=handles.edit2; num=0; %停止并删除已有的数据采集对象 openDAQ=daqfind; for i=1:length(openDAQ), stop(openDAQ(i)); delete(openDAQ(i)); end handles.output=hObject; 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'); set(handles.pushbutton4,'Enable','off');
② 在ai.m文件中,分别在“板卡设置”、“间断采集”、“连续采集”、“关闭程序”按钮相应回调函数pushbutton1_Callback()、pushbutton2_Callback()、pushbutton3_Callback()、pushbutton4_Callback()中添加代码,分别实现板卡设置、间断、连续采集和关闭GUI窗口功能,具体代码如下。
function pushbutton1_Callback(hObject,eventdata,handles)%板卡设置 global t; ai=analoginput('nidaq',1); %创建数据采集卡模拟输入对象 ichan=addchannel(ai,1); %添加1通道 set(ai,'InputType','SingleEnded'); %设置单端输入 set(ai,'SampleRate',500); %设置采样率为500 %添加ai到handle中,是用handle.ai来访问ai handles.ai=ai; set(handles.pushbutton1,'Enable','off'); set(handles.pushbutton2,'Enable','on'); set(handles.pushbutton3,'Enable','on'); set(handles.pushbutton4,'Enable','on'); guidata(handles.figure1,handles); %启动数据采集对象 start(handles.ai); %创建时间对象t,每隔1s触发timerCallback函数事件 t=timer('TimerFcn',{@timerCallback,handles.ai},'ExecutionMode','fixedRate','Period',1,'StartDelay',0); function pushbutton2_Callback(hObject,eventdata,handles)%间断采集 global num; global data; global t; stop(t); num=num+1; data(num)=getsample(handles.ai); set(handles.edit1,'String',num); set(handles.edit2,'String',sprintf('%0.1f',data(num))); draw(); alarm(); 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; %检查数据采集对象是否有效,有效则删除 if strcmp(get(handles.pushbutton4,'Enable'),'on') if isvalid(handles.ai) stop(handles.ai); %停止采集 delete(handles.ai); %从数据采集引擎中删除 end stop(t); delete(t); %删除时间对象 end clear all; %清除所有
④ 在ai.m文件中,添加时间对象t的timerCallback事件回调函数,实现定时数据采集和画图功能,具体代码如下。
function timerCallback(obj,event,hnd) global num; global data; global p4; global p5; num=num+1; data(num)=getsample(hnd); 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
程序设计、调试完毕,运行程序。
转动电位器旋钮,改变其输出电压(范围是0~5V),线路中AI指示灯亮度随之变化,同时,连续单击“间断采集”按钮或单击一次“连续采集”按钮,程序窗体中文本对象中的数字、图形控件中的曲线都将随电位器输出电压变化而变化。
程序运行界面如图3-17所示。
图3-17 程序运行界面
3.3.2 数字量输入
以下是采用MATLAB编写的数字量输入参考程序。
① 在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; %停止并删除已有的数据采集对象 openDAQ=daqfind; for i=1:length(openDAQ), stop(openDAQ(i)); delete(openDAQ(i)); end plot(1,1,'-o','MarkerFaceColor','g','MarkerSize',52);%鲜绿色报警灯 axis off; %关闭坐标轴 hold on; handles.dio=digitalio('nidaq',1); %创建数字量输入/输出对象 set(handles.dio,'TimerPeriod',0.5); %数字输入扫描时段(s) addline(handles.dio,5,'in'); %配置数字量输入端通道为5通道 guidata(hObject,handles); global t; %创建时间对象t,每隔0.5 s触发timerCallback函数事件 t=timer('TimerFcn',{@timerCallback,handles.dio},'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); %删除时间对象 delete(handles.dio); %删除数字输入量 clear all;
④ 在di.m文件中,添加时间对象t的timerCallback事件回调函数,实现周期检测数字量端口状态,并有报警功能,具体代码如下。
function timerCallback(obj,event,hnd) global num; global bz; global p1; global p2; val=getvalue(hnd); %数字量输入5通道 if val==1 plot(p1,1,1,'-o','MarkerFaceColor','g','MarkerSize',52); %鲜绿色报警灯 else plot(p1,1,1,'-o','MarkerFaceColor','r','MarkerSize',52); %红色报警灯 end %数字量输入5通道 if val==0 & bz==1 num=num+1; set(p2,'String',num); bz=2; end if val==1 bz=1; end
程序设计、调试完毕,运行程序。
用任何反光物体遮挡/离开“光电接近开关”,线路中DI指示灯2亮/灭,程序画面中开关计数器文本中的数字从1开始累加。
程序运行界面如图3-18所示。
3.3.3 数字量输出
以下是采用MATLAB编写的数字量输出参考程序。
① 在do.m文件中,设置打开回调函数do_OpeningFcn (),添加如下代码完成程序初始化的工作,具体代码如下。
function do_OpeningFcn(hObject,eventdata,handles,varargin) global num1; %打开次数 global num2; %关闭次数 global zt; %开关状态 num1=0; num2=0; zt=2; %停止并删除已有的数据采集对象 openDAQ=daqfind; for i=1:length(openDAQ), stop(openDAQ(i)); delete(openDAQ(i)); end plot(1,1,'-o','MarkerFaceColor','g','MarkerSize',50);%鲜绿色报警灯 axis off; %关闭坐标轴 hold on; handles.dio=digitalio('nidaq',1); %创建数字量输入/输出对象 addline(handles.dio,0,'out'); %配置数字量输出端0通道
② 在do.m文件中,分别在“打开指示灯”、“关闭指示灯”、“关闭程序”按钮相应回调函数pushbutton1_Callback()、pushbutton2_Callback()、pushbutton3_Callback()中添加代码,分别实现打开指示灯、关闭指示灯和关闭GUI窗口功能,具体代码如下。
function pushbutton1_Callback(hObject,eventdata,handles) %打开指示灯 global num1; global zt; %开关状态 if zt==2 putvalue(handles.dio,1 %置0通道状态为1,即打开指示灯 plot(handles.axes1,1,1,'-o','MarkerFaceColor','r','MarkerSize',50); %红色报警灯 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); %鲜绿色报警灯关闭指示灯 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) delete(handles.dio); %删除handles.dio对象 clear all;
程序设计、调试完毕,运行程序。
单击“打开指示灯”按钮,程序界面中指示灯颜色变为红色,打开次数加1;同时,线路中DO指示灯亮。
单击“关闭指示灯”按钮,程序界面中指示灯颜色变为绿色,关闭次数加1;同时,线路中DO指示灯灭。
程序运行界面如图3-19所示。
图3-18 程序运行界面
图3-19 程序运行界面
3.3.4 温度测控
以下是采用MATLAB编写的温度测控参考程序。
① 在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; global m; 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; %停止并删除已有的数据采集对象 openDAQ=daqfind; for i=1:length(openDAQ), stop(openDAQ(i)); delete(openDAQ(i)); end %设置模拟输入对象 handles.ai=analoginput('nidaq',1); %创建数据采集卡模拟输入对象ichan=addchannel(handles.ai,1); %添加通道1 set(handles.ai,'InputType','SingleEnded'); %设置单端输入 set(handles.ai,'SampleRate',500); %设置采样率为500 m=handles.ai; %设置数字输出对象 handles.dio=digitalio('nidaq',1); %创建数字量输入/输出对象 addline(handles.dio,1:2,'out'); %配置数字量输出端1和通道2 r2=handles.dio; 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,每隔0.5 s触发timerCallback函数事件 t=timer('TimerFcn',{@timerCallback,handles.ai},'ExecutionMode',' fixedRate ','Period',1,'StartDelay',0); start(handles.ai); %启动输入对象 start(handles.dio); %启动输出对象 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); %删除时间对象 putvalue(handles.dio,[0 0]); stop(handles.ai); %停止handles.ai对象 stop(handles.dio); %停止handles.dio对象 delete(handles.ai); %删除handles.ai对象 delete(handles.dio); %删除handles.dio对象 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=getsample(hnd); %获取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]); %置1:2通道值为[1 0] plot(p2,1,1,'-o','MarkerFaceColor','r','MarkerSize',32); elseif data(num)<maxdata putvalue(r2,[0 0]); %置1:2通道值为[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]); %置1:2通道值为[0 1] plot(p3,1,1,'-o','MarkerFaceColor','r','MarkerSize',32); end
程序设计、调试完毕,运行程序。
① 单击“绘曲线图”按钮,开始采集温度测量值并绘制连续的曲线图;单击“绘散点图”按钮,开始采集温度测量值并绘制间断的散点图。
② 当测量温度小于设定的下限温度值时,程序中下限指示灯改变颜色,相应线路中的DO指示灯1亮;当测量温度值大于设定的上限温度值时,程序中上限指示灯改变颜色,相应线路中的DO指示灯2亮。
③ 在报警指示区,可以改变下限、上限温度值:在下限指示文本框输入新的下限报警值,按回车键确认;在上限指示文本框输入新的上限报警值,按回车键确认。
④ 单击“保存数据”按钮,出现“另存为”对话框,指定路径,输入文件名,将采集的温度值保存到指定的文本文件中。
⑤ 单击“打开文件”按钮,出现“打开”对话框,选中文件名,打开文件,文件中的数据显示在表格中,并绘制曲线。
程序运行界面如图3-20所示。
图3-20 程序运行界面