利用Matlab AppDesigner编写一个基于uart通信的数据接收上位机

  • 背景
    下位机为一个消防监测平台:平台以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
  • 结束语
    最后,错误和不足之处望各位指正。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值