-
背景
下位机为一个消防监测平台:平台以1s的周期以uart方式向上位机发送温度数据和监控状态,温度数据由四个8bit的数据组成,前两位为小数点后两位,后两位为小数点前两位,发送顺序由低位到高位,监控状态由一个2bit的数据表示,但还是由一个8bit的数据传输({6’b000000,xx});上位机可以向下位机发送数据以控制其舵机转动,发送8’hff正向转动,发送8’haa反向转动,发送8’h00停止转动。 -
UI布局
下拉框:
“平台串口”-app.sericom_name
“波特率”-app.Baud_rate
“数据位”-app.data_bits
“校验位”-app.parity
“停止位”-app.stop_bits
状态按钮:
“连接/断开平台”-app.open_close_sericom
按钮:
“正向转动”-app.roll_pos
“反向转动”-app.roll_neg
“停止转动”-app.roll_stop
信号灯:
“连接灯”-app.sericom_state_lamp
“警示灯”-app.warning_lamp
文本区域:
“提示信息”-app.notes
“摄氏度”-app.tempreture_data
“警报等级”-app.warning_state
- Code
首先贴出该程序需要的私有属性
properties (Access = private)
strarray % 输出提示信息
sObject % 串口
acc % 接受数据计数,用于判断数据属性
tempreture1 % 接收到的第一个温度数据
tempreture2
tempreture3
tempreture4
tem_str % 温度显示数据
end
首先编写程序打开时初始化代码,用于检测是否有可用串口
function startupFcn(app)
ss=seriallist;
if ~isempty(ss)
app.sericom_name.Items=ss;
app.strarray='已检测到平台';
else
app.sericom_name.Items={'COM1'};
app.strarray='未发现平台';
end
app.notes.Value=app.strarray;
app.acc=0;
end
下面编写串口连接/断开程序
function open_close_sericomValueChanged(app, event)
value = app.open_close_sericom.Value;
%%创建串口以及判断串口状态
switch value
case 1
%串口创建
delete(instrfindall);%注销系统之前已经打开的串口资源
try
seriComName=app.sericom_name.Value;
app.sObject=serial(seriComName);%创建串口
catch
app.strarray={'error:该串口可能被别的应用占用或串口命名错误!'};
app.notes.Value=app.strarray;
return;
end
%assignin('base',"ss",app.sObject);
%SERIAL specific properties:
set(app.sObject,'BaudRate',str2double(app.Baud_rate.Value)); %设置波特率
%set(app.sObject,'BreakInterruptFcn',[]); %设置中断
set(app.sObject,'DataBits',8); %数据位,默认为8,可以不用设置
set(app.sObject,'DataTerminalReady','on'); %
set(app.sObject,'FlowControl','none'); %
set(app.sObject,'Parity','none'); %校验位,一般情况下是无校验,none
set(app.sObject,'ReadAsyncMode','continuous'); %设置数据读取模式
set(app.sObject,'RequestToSend','on'); %
set(app.sObject,'StopBits',1); %停止位,默认为1,可以不用设置
%SERIAL other properties:
set(app.sObject,'ByteOrder','littleEndian'); %
%set(app.sObject,'BytesAvailableFcn',@instrcallback); %自动接收
set(app.sObject,'BytesAvailableFcn',{@instrcallback,app});
set(app.sObject,'BytesAvailableFcnMode','byte'); %
set(app.sObject,'BytesAvailableFcnCount',1); %
set(app.sObject,'inputBufferSize',1024) %设置输入缓冲区域为1K,单位字节
set(app.sObject,'outputBufferSize',1024) %设置输出缓冲区域为1K,单位字节
set(app.sObject,'Timeout',0.5);%设置一次写入或者读入操作完成最大时间为0.5s,单位为秒
set(app.sObject,'Terminator','LF');%触发中断的字符(一般是"换行符"LF)
%set(app.sObject,'TimerFcn',); %定时回调函数
set(app.sObject,'TimerPeriod',1); %定时周期
if app.sObject.Status == "closed"
% 打开串口
try
fopen(app.sObject);
catch
app.strarray={'error:该串口可能被别的应用占用或串口命名错误'};
app.notes.Value=app.strarray;
return
end
if(app.sObject.Status == "open")
app.sericom_state_lamp.Color="green";
app.strarray={'平台连接成功'};
app.notes.Value=app.strarray;
else
app.sericom_state_lamp.Color="red";
app.strarray={'error:平台连接失败'};
app.notes.Value=app.strarray;
end
else
app.sericom_state_lamp.Color="yellow";
app.strarray={'error:串口被占用'};
app.notes.Value=app.strarray;
end
case 0
%断开串口
delete(instrfindall);
app.sObject=[];
app.sericom_state_lamp.Color="red";
app.strarray={'连接已断开'};
app.notes.Value=app.strarray;
end
end
其中
set(app.sObject,'BytesAvailableFcn',{@instrcallback,app});
set(app.sObject,'BytesAvailableFcnMode','byte'); %
set(app.sObject,'BytesAvailableFcnCount',1); %
这三句需要特别注意:表示在接收到1byte的数据后就触发中断函数,而这个中断函数是Matlab自带函数,有需要时需要自己修改,该函数一般情况下位于Matlab安装文件夹下xxxx\toolbox\shared\instrument@instrument\路径下,在该项目中我将该函数修改如下:
function instrcallback(obj, event,app)%在输入参数中增加app一项
...
switch nargin%在输入参数个数判断中多增加一项防止因输入参数加入app而报错
...
case 3
if ~isa(obj, 'instrument') || ~isa(event, 'struct')
error(message('instrument:instrument:instrcallback:invalidSyntax'));
end
if ~(isfield(event, 'Type') && isfield(event, 'Data'))
error(message('instrument:instrument:instrcallback:invalidSyntax'));
end
...
myCallback(app);%在函数末尾调用app的公共函数,当然也可以将该函数内容直接写在instrcallback内,但为了修改和调试方便,我们直接调用该函数
在instrcallback函数修改完成后需要重启Matlab才会生效。
其中我编写的接收数据及处理函数myCallback(app)如下:
methods (Access = public)
function myCallback(app)
a=fread(app.sObject,1,'uint8');
app.strarray=['rx:',num2str(a)];
if app.acc==0
app.tempreture1=num2str(a);
app.acc=app.acc+1;
elseif app.acc==1
app.tempreture2=num2str(a);
app.acc=app.acc+1;
elseif app.acc==2
app.tempreture3=num2str(a);
app.acc=app.acc+1;
elseif app.acc==3
app.tempreture4=num2str(a);
app.acc=app.acc+1;
elseif app.acc==4
if a==0
app.warning_lamp.Color='blue';
app.warning_state.Value='二级警报';
elseif a==3
app.warning_lamp.Color='red';
app.warning_state.Value='一级警报';
else
app.warning_lamp.Color='green';
app.warning_state.Value='情况正常';
end
app.tem_str=[app.tempreture4,app.tempreture3,'.',app.tempreture2,app.tempreture1];
app.tempreture_data.Value=app.tem_str;
app.acc=0;
else
app.acc=0;
end
end
end
接下来是串口数据发送方面
正向转动程序:
function roll_posPushed(app, event)
%%串口发送模块
%检查端口是否在线--串口
if isempty(app.sObject)
app.strarray={'串口未连接 '};
app.notes.Value=app.strarray;
return
end
decData=hex2dec('ff');
%开始发送
fwrite(app.sObject, decData,'uint8'); % 以二进制形式向obj写入数据dataSend
%发送完成,日志显示
app.strarray={'tx:ff'};
app.notes.Value=app.strarray;
end
类似我们只需要改变发送内容就可以编写其他两个控制舵机转动按钮程序:
function roll_negButtonPushed(app, event)
%%串口发送模块
%检查端口是否在线--串口
if isempty(app.sObject)
app.strarray={'串口未连接 '};
app.notes.Value=app.strarray;
return
end
decData=hex2dec('aa');
%开始发送
fwrite(app.sObject, decData,'uint8'); % 以二进制形式向obj写入数据dataSend
%发送完成,日志显示
app.strarray={'tx:aa'};
app.notes.Value=app.strarray;
end
function roll_stopButtonPushed(app, event)
%%串口发送模块
%检查端口是否在线--串口
if isempty(app.sObject)
app.strarray={'串口未连接 '};
app.notes.Value=app.strarray;
return
end
decData=hex2dec('00');
%开始发送
fwrite(app.sObject, decData,'uint8'); % 以二进制形式向obj写入数据dataSend
%发送完成,日志显示
app.strarray={'tx:00'};
app.notes.Value=app.strarray;
end
最后我们需要编写关闭程序时的代码,关闭程序需要将创建的端口关闭并删除掉:
function UIFigureCloseRequest(app, event)
delete(app)
try
stopasync(app.sObject);%Stop asynchronous read and write operations
fclose(app.sObject);%关闭app.sObject句柄
delete(app.sObject);%删除app.sObject句柄
catch
end
end
- 结束语
最后,错误和不足之处望各位指正。