基于MATLAB App Designer的串口RS485 Modbus上位机

1. 前言

上学那会儿MATLAB还没有App Designer,记得当时开发了一个基于MATLAB的计算器。最近,由于项目需要一个简单的上位机做监控,结合自身实际,拟定了以下三种方案:

  1. PyQt5:结合QT和Python的GUI开发库,结合QSS可以做出漂亮的界面
  2. Streamlit:基于Python的web应用开发库,简单易上手,本来是用来做数据科学的快速展示的,在本地上拿来搞个界面也不错,颜值也很高
  3. App Deisgner:MATLAB出品,控件外观可自定义的东西不多,不过借助MATLAB强大的toolbox,数据通讯、采集、控制、分析非常方便

本文采用第三种开发,另外两种后续有时间再写。
使用的MATLAB版本为2018b,关于modbus协议请自行百度。

2. 界面框架

首先应该对界面大体框架进行简单设计,如下图所示

串口连接配置:设置要连接的串口,配置波特率
数据显示:读寄存器显示
控制:写寄存器控制

3. 设计过程

3.1 创建界面

如下图所示

打开界面后,往界面拖入控件修改基础属性并进行布局得到的界面如下

可以从组件浏览器看到界面的组件

完成界面设计后,就可以开始代码编写

3.2 代码编写

3.2.1 私有属性

点击代码视图->属性->私有属性,建立以下几个属性:

CurrentSerial   % 当前选择的串口
ModbusSerialRTU % modbus对象
TreadFlag       % 关闭停止标志
SwitchStateMsg  % 开关状态消息
SwitchList      % 开关对象列表
SwitchTagList   % 开关对象的标签,用于event查找
SliderMsg       % 滑块消息

3.2.1 初始化回调函数

右击组件浏览器中的app.modbusFigure,选择回调->添加StartupFcn回调,在自动生成的startupFcn回调方法中,添加以下代码:

app.SerialPortSelectDropDown.Items = seriallist;    % 获取计算机串口号列表并写入
app.TreadFlag = false; 
app.ModbusSerialRTU = 0;
% 两个开关的初始化
app.SwitchStateMsg = zeros(1,2);	% 开关消息数组初始化为0
app.SwitchList = [app.Switch1, app.Switch2];	% 设置开关列表
app.SwitchTagList = ["switch1", "switch2"];	% 开关标签列表,主要用于event事件查找
for i = 1:2	% 初始化开关标签
	app.SwitchList(i).Tag =  app.SwitchTagList(i);
end

第一行调用matlab的seriallist命令获取计算机当前可用串口号,并将其写入串口号选择下拉列表。

3.2.2 刷新按钮回调

刷新按钮用于手动刷新当前可用的串口号。右击组件浏览器的app.RefreshButton,选择回调->添加RefreshButton回调,添加以下代码

app.PortSelectDropDown.Items = seriallist;    % 获取计算机串口号列表并写入下拉框

3.2.3 连接按钮回调

连接按钮将尝试创建modbus对象,若创建成功则进入监控程序,并禁止使用连接按钮,若创建失败则在状态标签处进行显示。

            try
                app.ModbusSerialRTU = modbus('serialrtu', app.PortSelectDropDown.Value, ...
                'BaudRate', str2double(app.BaudSelectDropDown.Value), 'Timeout', 1);
                app.TreadFlag = true;
           catch
                app.ModbusSerialRTU = 0;
                app.StatusLabel.Text = 'WARNING: modbus创建失败';
                app.TreadFlag = false;
           end            
           if app.TreadFlag == true
               app.LinkButton.Enable = false;
               main(app, event);   % 进入监控                
           end

3.2.4 关闭按钮回调

关闭按钮主要功能是将app.TreadFlag设置位false,从而控制退出main(app, event),同时重新使能连接按钮

app.ModbusSerialRTU = 0;
app.TreadFlag = false;
app.LinkButton.Enable = true;

3.2.5 开关回调

GUI有两个开关,可以分别设置回调函数。此处,将两个开关的回调函数设置为相同,首先选择开关1,添加回调函数,然后选择开关2右击选择已有的回调函数定位到开关1回调函数即可。

if event.Source.Tag == app.SwitchTagList(1) % 此处演示如何判断开关触发源
    fprintf('开关1被按下了');
    app.StatusLabel.Text = '开关1被按下了';
else
    fprintf('开关2被按下了')
    app.StatusLabel.Text = '开关2被按下了';
end

if strcmp(app.Switch1.Value, 'On')
    app.SwitchStateMsg(1) = 1;
else
    app.SwitchStateMsg(1) = 0;
end

if strcmp(app.Switch2.Value, 'On')
    app.SwitchStateMsg(2) = 1;
else
    app.SwitchStateMsg(2) = 0;
end            
            
            % 当开关很多的时候可以这么写
%             for i = 1:length(app.SwitchList)
%                app.SwitchStateMsg(i) = strcmp(app.SwitchList(i).Value, 'On');
%             end

3.2.6 滑动条回调

滑动条用于设置输出电压,其在回调函数中写入

app.SliderMsg = event.Value;

3.2.7 modbus轮询函数

完成所有控件的回调后,点击函数->添加私有函数,命名为main(app, event),该函数在用户点击连接后将轮询读写寄存器

    methods (Access = private)
        
        function main(app, event)
            fprintf('modbus轮询\r\n');
            while true
                try
                    Rpm = read(app.ModbusSerialRTU, 'holdingregs', 33, 4, 1, 'uint16'); % 从机地址位1
                    pause(0.1);
                catch
                    app.StatusLabel.Text = '保持寄存器读取失败';
                    Rpm = zeros(1, 4);
                end
                app.UITable.Data = Rpm(1:4);
                
                try
                    write(app.ModbusSerialRTU, 'coils', 54, app.SwitchStateMsg, 2); % 从机地址为2
                    pause(0.1)
                catch
                    app.StatusLabel.Text = '线圈写入失败';
                end
                
                try
                    write(app.ModbusSerialRTU, 'holdingregs', 11, round(app.SliderMsg*1000), 3, 'uint16');  % 从机地址为3
                    pause(0.1)
                catch
                    app.StatusLabel.Text = '保持寄存器写入失败';
                end
                
                
                if app.TreadFlag == false 
                    fprintf('退出主线程\r\n');
                    app.UITable.Data = zeros(1, 4);
                    break; 
                end
            end
        end

4 测试

首先用Virtual SerialPort创建虚拟串口对COM1和COM2,使用ModSim32模拟3个从机,地址分别为1,2,3。运行app,通讯成功。

物联沃分享整理
物联沃-IOTWORD物联网 » 基于MATLAB App Designer的串口RS485 Modbus上位机

发表评论