MATLAB数据通信与测控应用编程实践
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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 程序运行界面