精华内容
下载资源
问答
  • MODBUS读写工具

    2010-09-03 11:04:47
    单机MODBUS读写操作(03,05命令),可观察通讯贞
  • C#对三菱和西门子,欧姆龙等支持Modbus的服务器进行读写,不需要额外的组件,读取操作只要放到后台线程就不会卡死线程,本组件支持超级方便的高性能读写操作 1)附件C#代码全开源,所有代码公开,不使用第三方组件。...
  • 基于TIA博途通过Modbuspoll库文件实现多个温控设备的modbus轮询读写操作
  • Qt Modbus TCP 通讯读写操作

    千次阅读 热门讨论 2019-11-16 21:36:01
    Qt Modbus TCP通讯读写Modbus TCP 协议Modbus TCP/IP协议格式Qt Modbus 模块 读写 Modbus TCP 协议 Modbus协议是一个master/slave架构的协议。有一个节点是master节点,其他使用Modbus协议参与通信的节点是slave节点...

    Modbus TCP 协议

    Modbus协议是一个master/slave架构的协议。有一个节点是master节点,其他使用Modbus协议参与通信的节点是slave节点。每一个slave设备都有一个唯一的地址。在串行和MB+网络中,只有被指定为主节点的节点可以启动一个命令(在以太网上,任何一个设备都能发送一个Modbus命令,但是通常也只有一个主节点设备启动指令)。

    Modbus TCP/IP协议格式

    ModbusTCP协议组成

    • 主机poll端发送读写报文样例如下,该报文对应的功能码选择的10
    • 如上图所示,报文主要分为两部分。协议头(MBAP Header)和PDU。PDU 又包含功能码(Function code)和数据(Data)两部分。
    0232 0000 0009 01 10 9C490001020000
    

    协议头: 0232 0000 0009 01
    PDU: 10 9C490001020000

    数据含义
    0232主机发出的检验信息,从机slave将这个两个字节放在响应报文中
    0000表示协议标识符,00 00为modbus的TCP/IP协议
    0009数据长度,用来指示接下来数据的长度,单位字
    01设备地址,用以标识连接在串行线或者网络上的远程服务端的地址
    10功能码,此时代码10为WriteMultipleRegisters写多个寄存器数据
    9C49写入的起始地址40009
    0001写入寄存器长度,16进制
    02 0000写入的数据,02 表示数据长度为两个字节; 0000表示两个字节的数据

    Qt Modbus 模块 读写

    • 引入模块
    QT       += serialbus serialport
    
    • 引入头文件
    #include <QModbusTcpClient>
    #include <QModbusReply>
    
    • 建立连接
    	QModbusClient *modbusDevice=new QModbusTcpClient();
     	modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter,1502);
        modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, "127.0.0.1");
        modbusDevice->setTimeout(2000);
        modbusDevice->setNumberOfRetries(3);
        modbusDevice->connectDevice();
    
    • 读取
    //发送请求
    QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters,30001,1);
    
        if (auto *reply = modbusDevice->sendReadRequest(readUnit, 1))
        {
            if (!reply->isFinished())
            {
                connect(reply, &QModbusReply::finished,this,&ThisClass::readReady);
            }
            else
            {
                delete reply;
            }
        }
    //处理请求
    void ThisClass::readReady()
    {
        QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
        if (!reply)
            return;
    
        if (reply->error() == QModbusDevice::NoError)
        {
            const QModbusDataUnit unit = reply->result();
    //        if(unit.startAddress()==30001){
                qint16 res=unit.value(0)//        }
    
        }
        else
        {
        }
        reply->deleteLater(); // delete the reply
    }
    
    • 向40009寄存器地址写入1 (0232 0000 0009 01 10 9C49 0001 02 0001)
    //写请求
    // 9C490001020001 :数据部分
     	QByteArray data=QByteArray::fromHex("9c490001020001");
       // QModbusRequest::WriteMultipleRegisters:功能码 10
        QModbusRequest request(QModbusRequest::WriteMultipleRegisters,
            data);
    // 1 :设备地址
        if (auto *reply = modbusDevice->sendRawRequest(request,1))
        {
            if (!reply->isFinished())
            {
                connect(reply, &QModbusReply::finished,this,&ThisClass::writeReady);
            }
            else
            {
                delete reply;
            }
        }
        //响应处理
        void ThisClass::writeReady()
    {
        QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
        if (!reply)
            return;
    
         reply->deleteLater(); // delete the reply
    }
    
    展开全文
  • Coils程序示例04. HoldingRegisters程序示例05. 综合示例(未实现)06. 程序下载07. 附录 01. 概述 Qt中几个常用的串口modbus类 QModbusRtuSerialSlave //modbus串口通信方式下的服务器类 ...

    00. 目录

    01. 概述

    Qt中几个常用的串口modbus类

    QModbusRtuSerialSlave       //modbus串口通信方式下的服务器类
    QModbusRtuSerialMaster      //串口通信方式下的客户端类
    QModbusServer               // QModbusServer类接收和处理modbus的请求。
    QModbusDataUnit             //存储接收和发送数据的类,数据类型为1bit和16bit
    QModbusReply                //客户端访问服务器后得到的回复(如客户端读服务器数据时包含数据信息)
    

    02. 开发环境

    Windows系统:Windows10

    Qt版本:Qt5.15或者Qt6

    Pro配置文件如下

    QT       += core gui serialbus serialport
    
    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    
    CONFIG += c++11
    
    # You can make your code fail to compile if it uses deprecated APIs.
    # In order to do so, uncomment the following line.
    #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
    
    SOURCES += \
        main.cpp \
        widget.cpp
    
    HEADERS += \
        widget.h
    
    # Default rules for deployment.
    qnx: target.path = /tmp/$${TARGET}/bin
    else: unix:!android: target.path = /opt/$${TARGET}/bin
    !isEmpty(target.path): INSTALLS += target
    
    

    03. 写Coils程序示例

    widget.h文件

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    
    //前向声明
    class QModbusClient;
    class QModbusReply;
    
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();
    
    
    private:
        QModbusClient *modbusDevice = nullptr;
    
    
    private slots:
        void onReadReady();
    };
    #endif // WIDGET_H
    
    

    widget.cpp文件

    #include "widget.h"
    #include <QModbusRtuSerialMaster>
    #include <QModbusDataUnit>
    #include <QModbusReply>
    #include <QVariant>
    #include <QSerialPort>
    #include <QDebug>
    
    //构造函数
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
        //1. 创建QModbusDevice对象
        modbusDevice = new QModbusRtuSerialMaster;
    
        //2. 如果处于连接状态,则断开连接
        if (modbusDevice->state() == QModbusDevice::ConnectedState)
        {
            //断开连接设备
            modbusDevice->disconnectDevice();
        }
    
        //3. 设置串口相关参数
        //设置串口信息
        modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, QVariant("COM3"));
        //设置校验 无校验
        modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
        //设置波特率
        modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud19200);
        //设置停止位
        modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
        //设置数据位
        modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
    
        //4. 设置其他信息
        //设置超时时间
        modbusDevice->setTimeout(1000); //1秒
        //设置失败重试次数
        modbusDevice->setNumberOfRetries(3);
    
        //5. 连接到设备
        bool ok = modbusDevice->connectDevice();
        if (!ok)
        {
            qDebug() << "连接到串口失败: " << modbusDevice->errorString();
        }
        else
        {
            qDebug() << "连接到串口成功";
        }
    
        //6. 发送写请求
        //从地址0开始写10个保持寄存器的值
        //QModbusDataUnit writeData(QModbusDataUnit::HoldingRegisters, 0, 10);
    
        //从地址0开始写10个线圈的值
        QModbusDataUnit writeData(QModbusDataUnit::Coils, 0, 10);
        for (int i = 0; i < writeData.valueCount(); i++)
        {
            writeData.setValue(i, (i * i) % 2);
        }
    
        qDebug() << "发送的数据为: " << writeData.values();
    
        QModbusReply* reply = modbusDevice->sendWriteRequest(writeData, 1);
        if (reply)
        {
            if (!reply->isFinished())
            {
                //接收响应信息
                connect(reply, &QModbusReply::finished, this, [this, reply](){
                    if (reply->error() == QModbusDevice::ProtocolError)
                    {
                        //接收到的响应信息是协议错误
                        qDebug() << "写入数据错误:" << reply->errorString();
                    }
                    else if (reply->error() != QModbusDevice::NoError)
                    {
                        //接收到的响应消息是其它错误
                        qDebug() << "写入数据错误: " << reply->errorString();
                    }
                    else
                    {
                        //接收到的消息没有错误 一般没有必要解析响应消息
                        const QModbusDataUnit data = reply->result();
    
                        qDebug() << "消息数据个数:" << data.valueCount() << " :" << data.values();
    
                    }
    
                    reply->deleteLater();
                });
            }
            else
            {
                //发送没有响应数据
                //broadcast replies return immediately
                reply->deleteLater();
            }
        }
        else
        {
            qDebug() << "sendWriteRequest Error: " << reply->errorString();
        }
    
    
        //7. 发送读取数据请求
        //从地址0开始读取10个保持寄存器的值
        //QModbusDataUnit data(QModbusDataUnit::HoldingRegisters, 0, 10);
        //从地址0开始读取10个离散输入量的值
        //QModbusDataUnit data(QModbusDataUnit::DiscreteInputs, 0, 10);
    
        //QModbusDataUnit::Coils 从地址0开始读取10个线圈值
        QModbusDataUnit data(QModbusDataUnit::Coils, 0, 10);
    
        //QModbusDataUnit::InputRegisters 从地址0开始读取10个输入寄存器的值
        //QModbusDataUnit data(QModbusDataUnit::InputRegisters, 0, 10);
    
        reply = modbusDevice->sendReadRequest(data, 0x1);
        if (nullptr == reply)
        {
            qDebug() << "发送请求数据失败: " << modbusDevice->errorString();
        }
        else
        {
            if (!reply->isFinished())
            {
                connect(reply, &QModbusReply::finished, this, &Widget::onReadReady);
            }
            else
            {
                //broadcast replies return immediately
                delete reply;
            }
        }
    
    }
    
    
    //析构函数
    Widget::~Widget()
    {
        if (modbusDevice)
        {
            modbusDevice->disconnectDevice();
        }
    
        delete modbusDevice;
    }
    
    //准备读取数据的槽函数
    void Widget::onReadReady()
    {
        auto reply = qobject_cast<QModbusReply*>(sender());
        if (nullptr == reply)
        {
            return;
        }
    
        //判断是否出错
        if (reply->error() == QModbusDevice::NoError)
        {
            //读取响应数据
            const QModbusDataUnit responseData = reply->result();
    
            qDebug() << "读到数据为:" << responseData.values();
    
        }
        else if (reply->error() == QModbusDevice::ProtocolError)
        {
            qDebug() << "Read response Protocol error: " << reply->errorString();
        }
        else
        {
            qDebug() << "Read response Error: " << reply->errorString();
        }
    
    
        //删除reply
        reply->deleteLater();
    }
    
    
    

    执行结果

    20:32:27: Starting D:\ProgramData\Qt\build-Test-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\debug\Test.exe ...
    连接到串口成功
    发送的数据为:  QVector(0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
    消息数据个数: 10  : QVector(0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
    读到数据为: QVector(0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
    20:34:02: D:\ProgramData\Qt\build-Test-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\debug\Test.exe exited with code 0
    

    04. 写HoldingRegisters程序示例

    widget.h文件

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    
    //前向声明
    class QModbusClient;
    class QModbusReply;
    
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();
    
    
    private:
        QModbusClient *modbusDevice = nullptr;
    
    
    private slots:
        void onReadReady();
    };
    #endif // WIDGET_H
    
    

    widget.cpp文件

    #include "widget.h"
    #include <QModbusRtuSerialMaster>
    #include <QModbusDataUnit>
    #include <QModbusReply>
    #include <QVariant>
    #include <QSerialPort>
    #include <QDebug>
    
    //构造函数
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
        //1. 创建QModbusDevice对象
        modbusDevice = new QModbusRtuSerialMaster;
    
        //2. 如果处于连接状态,则断开连接
        if (modbusDevice->state() == QModbusDevice::ConnectedState)
        {
            //断开连接设备
            modbusDevice->disconnectDevice();
        }
    
        //3. 设置串口相关参数
        //设置串口信息
        modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, QVariant("COM3"));
        //设置校验 无校验
        modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
        //设置波特率
        modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud19200);
        //设置停止位
        modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
        //设置数据位
        modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
    
        //4. 设置其他信息
        //设置超时时间
        modbusDevice->setTimeout(1000); //1秒
        //设置失败重试次数
        modbusDevice->setNumberOfRetries(3);
    
        //5. 连接到设备
        bool ok = modbusDevice->connectDevice();
        if (!ok)
        {
            qDebug() << "连接到串口失败: " << modbusDevice->errorString();
        }
        else
        {
            qDebug() << "连接到串口成功";
        }
    
        //6. 发送写请求
        //从地址0开始写10个保持寄存器的值
        QModbusDataUnit writeData(QModbusDataUnit::HoldingRegisters, 0, 10);
        for (int i = 0; i < writeData.valueCount(); i++)
        {
            writeData.setValue(i, i * i);
        }
    
        qDebug() << "发送的数据为: " << writeData.values();
    
        QModbusReply* reply = modbusDevice->sendWriteRequest(writeData, 1);
        if (reply)
        {
            if (!reply->isFinished())
            {
                //接收响应信息
                connect(reply, &QModbusReply::finished, this, [this, reply](){
                    if (reply->error() == QModbusDevice::ProtocolError)
                    {
                        //接收到的响应信息是协议错误
                        qDebug() << "写入数据错误:" << reply->errorString();
                    }
                    else if (reply->error() != QModbusDevice::NoError)
                    {
                        //接收到的响应消息是其它错误
                        qDebug() << "写入数据错误: " << reply->errorString();
                    }
                    else
                    {
                        //接收到的消息没有错误 一般没有必要解析响应消息
                        const QModbusDataUnit data = reply->result();
    
                        qDebug() << "消息数据个数:" << data.valueCount() << " :" << data.values();
    
                    }
    
                    reply->deleteLater();
                });
            }
            else
            {
                //发送没有响应数据
                //broadcast replies return immediately
                reply->deleteLater();
            }
        }
        else
        {
            qDebug() << "sendWriteRequest Error: " << reply->errorString();
        }
    
    
        //7. 发送读取数据请求
        //从地址0开始读取10个保持寄存器的值
        QModbusDataUnit data(QModbusDataUnit::HoldingRegisters, 0, 10);
        //从地址0开始读取10个离散输入量的值
        //QModbusDataUnit data(QModbusDataUnit::DiscreteInputs, 0, 10);
    
        //QModbusDataUnit::Coils 从地址0开始读取10个线圈值
        //QModbusDataUnit data(QModbusDataUnit::Coils, 0, 10);
    
        //QModbusDataUnit::InputRegisters 从地址0开始读取10个输入寄存器的值
        //QModbusDataUnit data(QModbusDataUnit::InputRegisters, 0, 10);
    
        reply = modbusDevice->sendReadRequest(data, 0x1);
        if (nullptr == reply)
        {
            qDebug() << "发送请求数据失败: " << modbusDevice->errorString();
        }
        else
        {
            if (!reply->isFinished())
            {
                connect(reply, &QModbusReply::finished, this, &Widget::onReadReady);
            }
            else
            {
                //broadcast replies return immediately
                delete reply;
            }
        }
    
    }
    
    
    //析构函数
    Widget::~Widget()
    {
        if (modbusDevice)
        {
            modbusDevice->disconnectDevice();
        }
    
        delete modbusDevice;
    }
    
    //准备读取数据的槽函数
    void Widget::onReadReady()
    {
        auto reply = qobject_cast<QModbusReply*>(sender());
        if (nullptr == reply)
        {
            return;
        }
    
        //判断是否出错
        if (reply->error() == QModbusDevice::NoError)
        {
            //读取响应数据
            const QModbusDataUnit responseData = reply->result();
    
            qDebug() << "读到数据为:" << responseData.values();
    
        }
        else if (reply->error() == QModbusDevice::ProtocolError)
        {
            qDebug() << "Read response Protocol error: " << reply->errorString();
        }
        else
        {
            qDebug() << "Read response Error: " << reply->errorString();
        }
    
    
        //删除reply
        reply->deleteLater();
    }
    
    
    
    

    执行结果

    20:23:23: Starting D:\ProgramData\Qt\build-Test-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\debug\Test.exe ...
    连接到串口成功
    发送的数据为:  QVector(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)
    消息数据个数: 10  : QVector(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)
    读到数据为: QVector(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)
    20:23:29: D:\ProgramData\Qt\build-Test-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\debug\Test.exe exited with code 0
    

    05. 综合示例

    程序界面

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J2Oiv0Co-1621479015968)(assets/image-20210520104421393.png)]

    settingdialog.h文件

    #ifndef SETTINGDIALOG_H
    #define SETTINGDIALOG_H
    
    #include <QDialog>
    #include <QtSerialPort>
    
    namespace Ui {
    class SettingDialog;
    }
    
    //串口设置相关类
    class SettingDialog : public QDialog
    {
        Q_OBJECT
    
    public:
        struct Settings
        {
            //串口名
            QString serialName = "COM3";
            //校验位
            int parity = QSerialPort::NoParity;
            //波特率
            int baud = QSerialPort::Baud19200;
            //数据位
            int dataBits = QSerialPort::Data8;
            //停止位
            int stopBits = QSerialPort::OneStop;
    
            //响应时间
            int responseTime = 1000;
            //重试次数
            int numberOfRetries = 3;
        };
    
        explicit SettingDialog(QWidget *parent = nullptr);
        ~SettingDialog();
    
        //返回参数设置信息
        Settings  settings() const;
    
    private slots:
        void on_btnApply_clicked();
    
    private:
        Ui::SettingDialog *ui;
        Settings m_settings;
    };
    
    #endif // SETTINGDIALOG_H
    
    

    settingdialog.cpp文件

    #include "settingdialog.h"
    #include "ui_settingdialog.h"
    
    //构造函数
    SettingDialog::SettingDialog(QWidget *parent) :
        QDialog(parent),
        ui(new Ui::SettingDialog)
    {
        ui->setupUi(this);
    
        //设置默认参数信息
        ui->serialNameLineEdit->setText(tr("COM3"));
        ui->parityComboBox->setCurrentIndex(0);
    
        ui->baudComboBox->setCurrentText(QString::number(m_settings.baud));
        ui->dataBitComboBox->setCurrentText(QString::number(m_settings.dataBits));
        ui->stopBitComboBox->setCurrentText(QString::number(m_settings.stopBits));
    
        ui->spinBoxTimeOut->setValue(m_settings.responseTime);
        ui->spinBoxRetry->setValue(m_settings.numberOfRetries);
    
    }
    
    //析构函数
    SettingDialog::~SettingDialog()
    {
        delete ui;
    }
    
    //返回参数信息
    SettingDialog::Settings SettingDialog::settings() const
    {
        return m_settings;
    }
    
    //引用按钮槽函数
    void SettingDialog::on_btnApply_clicked()
    {
        m_settings.serialName = ui->serialNameLineEdit->text();
        m_settings.parity = ui->parityComboBox->currentText().toInt();
        m_settings.baud = ui->baudComboBox->currentText().toInt();
        m_settings.dataBits = ui->dataBitComboBox->currentText().toInt();
        m_settings.stopBits = ui->stopBitComboBox->currentText().toInt();
    
        m_settings.responseTime = ui->spinBoxTimeOut->value();
        m_settings.numberOfRetries = ui->spinBoxRetry->value();
    
        //隐藏参数设置对话框
        hide();
    }
    
    

    mainwindow.h文件

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QModbusDataUnit>
    #include "writeregistermodel.h"
    
    QT_BEGIN_NAMESPACE
    
    namespace Ui
    {
    class MainWindow;
    }
    
    class SettingDialog;
    class QModbusClient;
    class QModbusReply;
    
    
    QT_END_NAMESPACE
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
        //信号与槽进行关联
        void initActions();
    
        //读请求数据包封装
        QModbusDataUnit readRequest() const;
    
        //写请求数据包封装
        QModbusDataUnit writeRequest() const;
    
    private slots:
        void onConnectButtonClicked();
    
        void onConnectTypeChanged(int);
    
        void onModbusStateChanged(int state);
    
        void onReadButtonClicked();
        void onReadReady();
    
    
        void onWriteButtonClicked();
    
        void onReadWriteButtonClicked();
    
        void onWriteTableChanged(int);
    
    private:
        Ui::MainWindow *ui = nullptr;
    
        SettingDialog *m_settingDialog = nullptr;
    
        QModbusClient *modbusDevice = nullptr;
    
        QModbusReply *reply = nullptr;
    
        WriteRegisterModel *writeModel = nullptr;
    
    };
    #endif // MAINWINDOW_H
    
    

    mainwindow.cpp文件

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QMessageBox>
    #include "settingdialog.h"
    #include <QModbusRtuSerialMaster>
    #include <QModbusReply>
    #include <QStandardItemModel>
    #include <QModbusDataUnit>
    
    //连接类型枚举变量
    enum ModbusConnection {
        Serial,
        Tcp
    };
    
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        //创建对象
        m_settingDialog = new SettingDialog(this);
    
        //初始化信号与槽
        initActions();
    
        //创建写模型
        writeModel = new WriteRegisterModel(this);
        writeModel->setStartAddress(ui->sbWriteStartAddr->value());
        writeModel->setNumberOfValues(ui->cbWriteCount->currentText());
    
        //MVC
        ui->treeViewWrite->setModel(writeModel);
        //隐藏第二列
        ui->treeViewWrite->hideColumn(2);
        connect(writeModel, &WriteRegisterModel::updateViewport,
                ui->treeViewWrite->viewport(), QOverload<>::of(&QWidget::update));
    
    
        //默认为串口连接方式
        ui->cbConnType->setCurrentIndex(0);
        onConnectTypeChanged(0);
    
        auto model = new QStandardItemModel(10, 1, this);
        for (int i = 0; i < 10; i++)
        {
            model->setItem(i, new QStandardItem(QStringLiteral("%1").arg(i + 1)));
        }
    
        ui->cbWriteCount->setModel(model);
        ui->cbWriteCount->setCurrentText("10");
    
        connect(ui->cbWriteCount, &QComboBox::currentTextChanged,
                writeModel, &WriteRegisterModel::setNumberOfValues);
    
        auto valueChanged = QOverload<int>::of(&QSpinBox::valueChanged);
        connect(ui->sbWriteStartAddr, valueChanged, writeModel, &WriteRegisterModel::setStartAddress);
    
    
        connect(ui->sbWriteStartAddr, valueChanged, this, [this, model](int i){
            int lastIndex = 0;
            const int curIndex = ui->cbWriteCount->currentIndex();
            for (int j = 0; j < 10; j++)
            {
                //设置使能
                if (j < (10 - i))
                {
                    lastIndex = j;
                    model->item(j)->setEnabled(true);
                }
                else
                {
                    //设置禁用
                    model->item(j)->setEnabled(false);
                }
            }
    
            if (curIndex > lastIndex)
            {
                ui->cbWriteCount->setCurrentIndex(lastIndex);
            }
    
        });
    }
    
    
    //析构函数
    MainWindow::~MainWindow()
    {
        if (modbusDevice)
        {
            modbusDevice->disconnectDevice();
            delete modbusDevice;
        }
        delete ui;
    }
    
    
    
    //信号与槽进行关联s
    void MainWindow::initActions()
    {
        //使能部分功能
        ui->actionConnect->setEnabled(true);
        ui->actionDisconnect->setEnabled(false);
        ui->actionQuit->setEnabled(true);
        ui->actionOption->setEnabled(true);
    
        //禁用读写操作
        ui->btnRead->setEnabled(false);
        ui->btnWrite->setEnabled(false);
        ui->btnReadWrite->setEnabled(false);
    
    
        //信号与槽关联
        connect(ui->btnConnect, &QPushButton::clicked,
                this, &MainWindow::onConnectButtonClicked);
    
        connect(ui->actionConnect, &QAction::triggered,
                this, &MainWindow::onConnectButtonClicked);
    
        connect(ui->actionDisconnect, &QAction::triggered,
                this, &MainWindow::onConnectButtonClicked);
    
        //读操作按钮槽函数关联
        connect(ui->btnRead, &QPushButton::clicked,
                this, &MainWindow::onReadButtonClicked);
    
        connect(ui->btnWrite, &QPushButton::clicked,
                this, &MainWindow::onWriteButtonClicked);
    
        connect(ui->btnReadWrite, &QPushButton::clicked,
                this, &MainWindow::onReadWriteButtonClicked);
    
        connect(ui->cbRegisterType, QOverload<int>::of(&QComboBox::currentIndexChanged),
                this, &MainWindow::onWriteTableChanged);
    
        connect(ui->cbConnType, QOverload<int>::of(&QComboBox::currentIndexChanged),
                this, &MainWindow::onConnectTypeChanged);
    
        //退出菜单
        connect(ui->actionQuit, &QAction::triggered, this, &MainWindow::close);
        //显示参数设置对话框
        connect(ui->actionOption, &QAction::triggered, m_settingDialog, &QDialog::show);
    
        connect(ui->actionAbout, &QAction::triggered, [=]() {
            QMessageBox::aboutQt(this, "About Qt");
        });
    }
    
    //构建请求报文
    QModbusDataUnit MainWindow::readRequest() const
    {
        //const auto type = static_cast<QModbusDataUnit::RegisterType>(ui->cbRegisterType->currentData().toInt());
    
       QModbusDataUnit::RegisterType type = QModbusDataUnit::Invalid;
    
       qDebug() << ui->cbRegisterType->currentText();
    
       if (ui->cbRegisterType->currentText() == QString("线圈"))
       {
            type = QModbusDataUnit::Coils;
       }
       else if (ui->cbRegisterType->currentText() == QString("离散输入"))
       {
           type = QModbusDataUnit::DiscreteInputs;
       }
       else if (ui->cbRegisterType->currentText() == QString("输入寄存器"))
       {
           type = QModbusDataUnit::InputRegisters;
       }
       else if (ui->cbRegisterType->currentText() == QString("保持寄存器"))
       {
           type = QModbusDataUnit::HoldingRegisters;
       }
    
        qDebug() << "请求报文类型: " << type;
    
        //获取
        int startAddress = ui->spReadStartAddr->value();
        Q_ASSERT(startAddress >= 0 && startAddress < 10);
    
        quint16 numberOfEntries = ui->cbReadCount->currentText().toUShort();
    
        return QModbusDataUnit(type, startAddress, numberOfEntries);
    }
    
    
    //写请求数据包封装
    QModbusDataUnit MainWindow::writeRequest() const
    {
        QModbusDataUnit::RegisterType type = QModbusDataUnit::Invalid;
    
        qDebug() << ui->cbRegisterType->currentText();
    
        if (ui->cbRegisterType->currentText() == QString("线圈"))
        {
             type = QModbusDataUnit::Coils;
        }
        else if (ui->cbRegisterType->currentText() == QString("离散输入"))
        {
            type = QModbusDataUnit::DiscreteInputs;
        }
        else if (ui->cbRegisterType->currentText() == QString("输入寄存器"))
        {
            type = QModbusDataUnit::InputRegisters;
        }
        else if (ui->cbRegisterType->currentText() == QString("保持寄存器"))
        {
            type = QModbusDataUnit::HoldingRegisters;
        }
    
         qDebug() << "请求报文类型: " << type;
    
         //获取
         int startAddress = ui->sbWriteStartAddr->value();
         Q_ASSERT(startAddress >= 0 && startAddress < 10);
    
         quint16 numberOfEntries = ui->cbWriteCount->currentText().toUShort();
    
         //qDebug() << "Test: " << startAddress << " " << numberOfEntries;
         return QModbusDataUnit(type, startAddress, numberOfEntries);
    }
    
    //连接和断开连接的槽函数
    void MainWindow::onConnectButtonClicked()
    {
        if (!modbusDevice)
        {
            return;
        }
    
        //清空状态栏消息
        statusBar()->clearMessage();
    
        if (modbusDevice->state() != QModbusDevice::ConnectedState)
        {
            auto type = static_cast<ModbusConnection>(ui->cbConnType->currentIndex());
            if (type == Serial)
            {
                //设置串口连接信息
                modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
                                                     m_settingDialog->settings().serialName);
                modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
                                                     m_settingDialog->settings().parity);
                modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
                                                     m_settingDialog->settings().baud);
                modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
                                                     m_settingDialog->settings().dataBits);
                modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
                                                     m_settingDialog->settings().stopBits);
            }
            else
            {
                //TCP连接信息
            }
    
            modbusDevice->setTimeout(m_settingDialog->settings().responseTime);
            modbusDevice->setNumberOfRetries(m_settingDialog->settings().numberOfRetries);
    
            if (!modbusDevice->connectDevice())
            {
                statusBar()->showMessage(tr("Connect failed..") + modbusDevice->errorString(), 5000);
            }
            else
            {
                statusBar()->showMessage(tr("Connect Successfully"), 5000);
                qDebug() << "连接OK";
                ui->actionConnect->setEnabled(false);
                ui->actionDisconnect->setEnabled(true);
    
                //使能读写操作
                ui->btnRead->setEnabled(true);
                ui->btnWrite->setEnabled(true);
                ui->btnReadWrite->setEnabled(true);
    
            }
        }
        else
        {
            //断开连接
            modbusDevice->disconnectDevice();
            ui->actionConnect->setEnabled(true);
            ui->actionDisconnect->setDisabled(true);
            qDebug() << "断开连接成功";
    
            //禁用读写操作
            ui->btnRead->setEnabled(false);
            ui->btnWrite->setEnabled(false);
            ui->btnReadWrite->setEnabled(false);
        }
    }
    
    
    //连接类型槽函数 TCP Serial
    void MainWindow::onConnectTypeChanged(int index)
    {
        //如果之前存在连接,则断开连接,然后释放内存
        if(modbusDevice)
        {
            modbusDevice->disconnectDevice();
            delete modbusDevice;
            modbusDevice = nullptr;
        }
    
        auto type = static_cast<ModbusConnection>(index);
        if (type == Serial)
        {
            modbusDevice = new QModbusRtuSerialMaster(this);
            qDebug() << "new QModbusRtuSerialMaster Ok";
            statusBar()->showMessage("new QModbusRtuSerialMaster Ok", 3000);
    
        }
        else if (type == Tcp)
        {
    
        }
        else
        {
            statusBar()->showMessage("连接类型非法", 5000);
        }
    
        connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error){
            statusBar()->showMessage(modbusDevice->errorString(), 5000);
        });
    
        if (!modbusDevice)
        {
            //分配空间失败
            ui->btnConnect->setDisabled(true);
            if (type == Serial)
            {
                statusBar()->showMessage(tr("创建Modbus Master失败"), 5000);
            }
            else
            {
                statusBar()->showMessage(tr("创建Modbus Client失败"), 5000);
            }
        }
        else
        {
            connect(modbusDevice, &QModbusClient::stateChanged,
                    this, &MainWindow::onModbusStateChanged);
        }
    
    }
    
    
    //Modbus状态改变槽函数
    void MainWindow::onModbusStateChanged(int state)
    {
        //判断Modbus设备连接是否处于连接状态
        bool connected = (state != QModbusDevice::UnconnectedState);
    
        ui->actionConnect->setEnabled(!connected);
        ui->actionDisconnect->setEnabled(connected);
    
        if (QModbusDevice::UnconnectedState == state)
        {
            ui->btnConnect->setText(tr("Connect"));
        }
        else
        {
            ui->btnConnect->setText(tr("Disconnect"));
        }
    }
    
    
    //读操作槽函数
    void MainWindow::onReadButtonClicked()
    {
        if (!modbusDevice)
        {
            return;
        }
    
        ui->textEditRead->clear();
        statusBar()->clearMessage();
    
        //发送请求报文数据
        auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->sbServerAddr->value());
        if (reply)
        {
            if (!reply->isFinished())
            {
                //完毕之后 自动触发槽函数
                connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);
            }
            else
            {
                //广播消息  不需要返回响应
                delete reply;
            }
        }
        else
        {
            statusBar()->showMessage(tr("Read Error: ") + modbusDevice->errorString(), 5000);
        }
    
    
    }
    
    
    //读取数据
    void MainWindow::onReadReady()
    {
        auto reply = qobject_cast<QModbusReply*>(sender());
        if (!reply)
        {
            return;
        }
    
        if (reply->error() == QModbusDevice::NoError)
        {
            const QModbusDataUnit data = reply->result();
            for (int i = 0, total = (int)data.valueCount(); i < total; i++)
            {
                const QString str = tr("Address: %1 Value: %2").arg(data.startAddress() + i)
                        .arg(QString::number(data.value(i), data.registerType() <= QModbusDataUnit::Coils ? 10 : 16));
                ui->textEditRead->append(str);
            }
        }
        else if (reply->error() == QModbusDevice::ProtocolError)
        {
            statusBar()->showMessage(tr("Read response error: %1 (Modbus exception: 0x%2)").
                                     arg(reply->errorString()).
                                     arg(reply->rawResult().exceptionCode(), -1, 16), 5000);
        }
        else
        {
            statusBar()->showMessage(tr("Read response error: %1 (Code: 0x%2)").
                                     arg(reply->errorString()).
                                     arg(reply->error(), -1, 16), 5000);
        }
    
    
        //释放内存
        reply->deleteLater();
    }
    
    void MainWindow::onWriteButtonClicked()
    {
        if (!modbusDevice)
        {
            return;
        }
    
        statusBar()->clearMessage();
    
        QModbusDataUnit writeData = writeRequest();
        QModbusDataUnit::RegisterType type = writeData.registerType();
        //qDebug() << "test: " << writeData.valueCount();
        for (int i = 0, total = (int)(writeData.valueCount()); i < total; i++)
        {
            if (type == QModbusDataUnit::Coils)
            {
                writeData.setValue(i, writeModel->m_coils[i + writeData.startAddress()]);
            }
            else
            {
                //qDebug() << "test: " << writeModel->m_holdingRegisters[i + writeData.startAddress()];
                writeData.setValue(i, writeModel->m_holdingRegisters[i + writeData.startAddress()]);
            }
        }
    
        qDebug() << "写数据内容为:" << writeData.values();
    
        //发送请求报文数据
        auto *reply = modbusDevice->sendWriteRequest(writeData, ui->sbServerAddr->value());
        if (reply)
        {
            if (!reply->isFinished())
            {
                //完毕之后 自动触发槽函数
                connect(reply, &QModbusReply::finished, this, [this, reply]{
                    if (reply->error() == QModbusDevice::ProtocolError)
                    {
                        statusBar()->showMessage(tr("Write Protocaol response error: %1").arg(reply->errorString()), 5000);
                    }
                    else if (reply->error() != QModbusDevice::NoError)
                    {
                        statusBar()->showMessage(tr("Write response error: %1").arg(reply->errorString()), 5000);
                    }
                    else
                    {
                        qDebug() << "写响应的数据: " << reply->result().values();
                    }
    
                    reply->deleteLater();
    
                });
            }
            else
            {
                //广播消息  不需要返回响应
                reply->deleteLater();
            }
        }
        else
        {
            statusBar()->showMessage(tr("Write Error: ") + modbusDevice->errorString(), 5000);
        }
    }
    
    
    //读写按钮槽函数
    void MainWindow::onReadWriteButtonClicked()
    {
        if (!modbusDevice)
        {
            return;
        }
    
        statusBar()->clearMessage();
    
        QModbusDataUnit writeData = writeRequest();
        QModbusDataUnit::RegisterType type = writeData.registerType();
        //qDebug() << "test: " << writeData.valueCount();
        for (int i = 0, total = (int)(writeData.valueCount()); i < total; i++)
        {
            if (type == QModbusDataUnit::Coils)
            {
                writeData.setValue(i, writeModel->m_coils[i + writeData.startAddress()]);
            }
            else
            {
                //qDebug() << "test: " << writeModel->m_holdingRegisters[i + writeData.startAddress()];
                writeData.setValue(i, writeModel->m_holdingRegisters[i + writeData.startAddress()]);
            }
        }
    
        qDebug() << "写数据内容为:" << writeData.values();
    
        //发送请求报文数据
        auto *reply = modbusDevice->sendReadWriteRequest(readRequest(), writeData, ui->sbServerAddr->value());
        if (reply)
        {
            if (!reply->isFinished())
            {
                connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);
    
                //完毕之后 自动触发槽函数
                connect(reply, &QModbusReply::finished, this, [this, reply]{
                    if (reply->error() == QModbusDevice::ProtocolError)
                    {
                        statusBar()->showMessage(tr("Write Protocaol response error: %1").arg(reply->errorString()), 5000);
                    }
                    else if (reply->error() != QModbusDevice::NoError)
                    {
                        statusBar()->showMessage(tr("Write response error: %1").arg(reply->errorString()), 5000);
                    }
                    else
                    {
                        qDebug() << "写响应的数据: " << reply->result().values();
                    }
    
                    reply->deleteLater();
    
                });
            }
            else
            {
                //广播消息  不需要返回响应
                reply->deleteLater();
            }
        }
        else
        {
            statusBar()->showMessage(tr("Write Error: ") + modbusDevice->errorString(), 5000);
        }
    }
    
    void MainWindow::onWriteTableChanged(int index)
    {
        const bool coilsOrHolding = index == 0 || index == 3;
    
        if (coilsOrHolding)
        {
            ui->treeViewWrite->setColumnHidden(1, index != 0);
            ui->treeViewWrite->setColumnHidden(2, index != 3);
            ui->treeViewWrite->resizeColumnToContents(0);
        }
    
        ui->btnReadWrite->setEnabled(index == 3);
        ui->btnWrite->setEnabled(coilsOrHolding);
        ui->groupBox_2->setEnabled(coilsOrHolding);
    }
    
    
    

    writeregistermodel.h文件

    
    #ifndef WRITEREGISTERMODEL_H
    #define WRITEREGISTERMODEL_H
    
    #include <QAbstractItemModel>
    #include <QBitArray>
    #include <QObject>
    
    class WriteRegisterModel : public QAbstractTableModel
    {
        Q_OBJECT
    
    public:
        WriteRegisterModel(QObject *parent = nullptr);
    
        int rowCount(const QModelIndex &parent = QModelIndex()) const override;
        int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
        QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
        bool setData(const QModelIndex &index, const QVariant &value, int role) override;
    
        Qt::ItemFlags flags(const QModelIndex &index) const override;
    
    public slots:
        void setStartAddress(int address);
        void setNumberOfValues(const QString &number);
    
    signals:
        void updateViewport();
    
    public:
        int m_number = 0;
        int m_address = 0;
        QBitArray m_coils;
        QVector<quint16> m_holdingRegisters;
    };
    
    #endif // WRITEREGISTERMODEL_H
    
    

    writeregistermodel.cpp文件

    #include "writeregistermodel.h"
    
    enum { NumColumn = 0, CoilsColumn = 1, HoldingColumn = 2, ColumnCount = 3, RowCount = 10 };
    
    WriteRegisterModel::WriteRegisterModel(QObject *parent)
        : QAbstractTableModel(parent),
          m_coils(RowCount, false), m_holdingRegisters(RowCount, 0u)
    {
    }
    
    int WriteRegisterModel::rowCount(const QModelIndex &/*parent*/) const
    {
        return RowCount;
    }
    
    int WriteRegisterModel::columnCount(const QModelIndex &/*parent*/) const
    {
        return ColumnCount;
    }
    
    QVariant WriteRegisterModel::data(const QModelIndex &index, int role) const
    {
        if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
            return QVariant();
    
        Q_ASSERT(m_coils.count() == RowCount);
        Q_ASSERT(m_holdingRegisters.count() == RowCount);
    
        if (index.column() == NumColumn && role == Qt::DisplayRole)
            return QString::number(index.row());
    
        if (index.column() == CoilsColumn && role == Qt::CheckStateRole) // coils
            return m_coils.at(index.row()) ? Qt::Checked : Qt::Unchecked;
    
        if (index.column() == HoldingColumn && role == Qt::DisplayRole) // holding registers
            return QString("0x%1").arg(QString::number(m_holdingRegisters.at(index.row()), 16));
    
        return QVariant();
    
    }
    
    QVariant WriteRegisterModel::headerData(int section, Qt::Orientation orientation, int role) const
    {
        if (role != Qt::DisplayRole)
            return QVariant();
    
        if (orientation == Qt::Horizontal) {
            switch (section) {
            case NumColumn:
                return QStringLiteral("#");
            case CoilsColumn:
                return QStringLiteral("Coils  ");
            case HoldingColumn:
                return QStringLiteral("Holding Registers");
            default:
                break;
            }
        }
        return QVariant();
    }
    
    bool WriteRegisterModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {
        if (!index.isValid() ||  index.row() >= RowCount || index.column() >= ColumnCount)
            return false;
    
        Q_ASSERT(m_coils.count() == RowCount);
        Q_ASSERT(m_holdingRegisters.count() == RowCount);
    
        if (index.column() == CoilsColumn && role == Qt::CheckStateRole) { // coils
            auto s = static_cast<Qt::CheckState>(value.toUInt());
            s == Qt::Checked ? m_coils.setBit(index.row()) : m_coils.clearBit(index.row());
            emit dataChanged(index, index);
            return true;
        }
    
        if (index.column() == HoldingColumn && role == Qt::EditRole) { // holding registers
            bool result = false;
            quint16 newValue = value.toString().toUShort(&result, 16);
            if (result)
                m_holdingRegisters[index.row()] = newValue;
    
            emit dataChanged(index, index);
            return result;
        }
    
        return false;
    }
    
    Qt::ItemFlags WriteRegisterModel::flags(const QModelIndex &index) const
    {
        if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
            return QAbstractTableModel::flags(index);
    
        Qt::ItemFlags flags = QAbstractTableModel::flags(index);
        if ((index.row() < m_address) || (index.row() >= (m_address + m_number)))
            flags &= ~Qt::ItemIsEnabled;
    
        if (index.column() == CoilsColumn) // coils
            return flags | Qt::ItemIsUserCheckable;
        if (index.column() == HoldingColumn) // holding registers
            return flags | Qt::ItemIsEditable;
    
        return flags;
    }
    
    void WriteRegisterModel::setStartAddress(int address)
    {
        m_address = address;
        emit updateViewport();
    }
    
    void WriteRegisterModel::setNumberOfValues(const QString &number)
    {
        m_number = number.toInt();
        emit updateViewport();
    }
    
    

    06. 程序下载

    6.1 RTUMaster写操作示例(一).rar
    6.2 RTUMasterTest写操作(二).rar

    07. 附录

    7.1 Qt教程汇总
    网址:https://dengjin.blog.csdn.net/article/details/115174639

    展开全文
  • 基于TIA博途通过Modbuspoll库文件实现多个温控设备的modbus轮询读写操作 如果大家在做自动化项目时(PLC做主站),需要同时用到多个485仪表控制设备,需要读/这些设备的数据时,由于modbus这种协议每次只能处理一...

    基于TIA博途通过Modbuspoll库文件实现多个温控设备的modbus轮询读写操作

    0
    如果大家在做自动化项目时(PLC做主站),需要同时用到多个485仪表控制设备,需要读/写这些设备的数据时,由于modbus这种协议每次只能处理一条请求,所以就需要用到modbus轮询的方式来不断地处理读取和写入从站设备。
    如果用Modbus_Master指令来写的话,我们看西门子工程师给出的easyPlus帮助文档中是这样做的,如下图所示,即用第一个请求的DONE位来触发下一个请求,依次类推,从而实现轮询功能。
    1
    但是,当我们存在数量较多的从站设备时,这样去写modbus_master指令有点太繁琐了,这里给大家推荐一个工程师写的modbuspoll轮询库,直接拖拽到程序中组态并设置相关参数即可,省去编辑大量指令的繁琐程序,还是不错的。

    如下图所示,FB20000即为modbuspoll轮询库,我们可以先观察一下这个库的结构和用法。

    展开全文
  • 一、MODBUS读写器功能及寄存器说明 ... 40001:存放读卡器的站号(机号),可读可可以更改站号,只能用06功能码,可用03寄存器来读或连读。 40002:在读写型的MODBUS读写器为无效寄存器。 40003:驱动读...

    QQ:954486673
    微信:13822155058
    淘宝:https://item.taobao.com/item.htm?spm=a1z10.5-c.w4002-17663462238.17.1e61e728eBiooZ&id=41901622939

    一、MODBUS读写器功能及寄存器说明

    • MODBUS读写器适用S50、S70射频卡。
    • MODBUS读写器能通过MODBUS-RTU方式与PLC通信,MODBUS读写器为从站、PLC为主站,波特率为19200,N,8,1。需要实现即时刷卡功能,可用PLC设备每隔0.2秒读卡一次。
    • 寄存器说明:

        40001:存放读卡器的站号(机号),可读可写,写可以更改站号,只能用06功能码,可用03寄存器来读或连读。

        40002:在读写型的MODBUS读写器为无效寄存器。

        40003:驱动读卡器响声,只能用06功能码,可用03寄存器来读或连读。

                    如写为0表示短滴一声,见下表

    0

    短滴一声

    1

    短滴两声

    2

    短滴三声

    3

    长鸣一声

    4

    长鸣两声

    5

    长鸣三声

    6

    一长一短

    7

    一长两短

    8

    一长三短

    9

    两长一短

    10

    两长两短

    11

    两长三短

    其他

    短滴一声

     

    40004至40007寄存器保留,暂不使用

    40008寄存器为读写卡控制寄存器:只能用06功能码写,可用03寄存器来读或连读。

    15~8位

    7~6位

    5位

    4位

    3位

    2位

    1位

    0位

    自定义

    自定义

    为1表示IC卡密码错,对应IC卡的密码要通过密码管理卡在MODUBS读卡器上设定

    为1表示仅读或写指定卡号的卡,指定卡号放在寄存器,40009,

    40010

    写1驱动读卡操作,读寄存器这个位只能读出0

    写1驱动写卡操作,读寄存器这个位只能读出0

     

    读卡成功

    写卡成功

     

       

     

     

     

     

     

     

    40009至40010共2个寄存器存放IC卡卡号(4个字节)。

    40011至40034共24个寄存器对应于IC卡的扇区内48字节内容。

     

    二、数据报文格式

    在数据报文层面,寄存器起始地址都是从0开始。数据报文格式:设备地址+功能码+起始地址+寄存器个数+校验码。其中起始地址是从0开始的。

    如:从站号2读卡器读取40009开始的两个寄存器数据的报文

    发送:

    设备地址  功能码  起始地址  寄存器个数 校验码(CRC16)

       02       03     00 08       00 02    45 FA

    成功返回:

    设备地址  功能码   数据长度          数据信息             校验码(CRC16)

    02       03        04             28 5B 90 BB           9D 33

     

    其中:285B 90BB 为卡的内码数据,寄存器内高字节在前

    所以:

    正码为:5B28BB90,转换成十进制为:1529396112;

    反码为:90BB285B, 转换成十进制为:2428184667;

    三、读、写卡说明

    • 写卡操作:

    第一步,用06或10(十六进制)指令更新40011至40034寄存器内容(顺序对应卡内48字节内容),如果只用到40011到40034中的几个寄存器,其他寄存器可以不理会(不需要更新)。

    第二步,用功能码06(不能用10)

    写值00004到40008寄存器,作用是40011至40034寄存器的数据写到卡中。

    写值00020(十六进制为00014)到40008寄存器,作用是40011至40034寄存器的数据写到指定的卡中。卡号用40009,40010寄存器指定。

     

    第三步,读40008寄存器看是否第0位为1表示写卡成功。读40008寄存器尽量在写40008寄存器后马上操作。

     

    • 读卡操作:

    第一步:用功能码06(不能用10)

    写值00008到40008寄存器,作用是将卡上的内容更新到40011至40034寄存器中。

    写值00024(十六进制为00018)到40008寄存器,是将指定卡上的内容更新到40011至40034寄存器中。卡号放到40009,40010寄存器。

    第二步,读40008寄存器看是否第1位为1表示读卡成功,此时40009、40010、40011至40034中的数据才有效。读40008寄存器尽量在写40008寄存器后马上操作。

    第三步,用03指令读40011至40034寄存器中的内容。

     

    四、IC卡存储块与MODBUS读写器寄存器的对应关系

    • 读写器寄存器与IC卡某扇区(可用管理卡设置读写扇区及卡认证密码)0、1、2块字节的对应关系

    寄存器

    40011

    40012

    40013

    40014

    40015

    40016

    40017

    40018

    块0字节

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    A

    B

    C

    D

    E

    F

    寄存器

    40019

    40020

    40021

    40022

    40023

    40024

    40025

    40026

    块1字节

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    A

    B

    C

    D

    E

    F

    寄存器

    40027

    40028

    40029

    40030

    40031

    40032

    40033

    40034

    块2字节

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    A

    B

    C

    D

    E

    F

     

     

     

     

     

     

     

     

     

    展开全文
  • 软件介绍: MODBUS读写工具设置好仪表地址及通讯所使用的端口波特率后,设置开始寄器及寄存器数量,可进行读写操作
  • 00. 目录 文章目录00. 目录01. 概述02. 开发环境03....QModbusRtuSerialSlave //modbus串口通信方式下的服务器类 QModbusRtuSerialMaster //串口通信方式下的客户端类 QModbusServer // QModbusServer类接
  • Modbus测试工具ModbusPoll与Modbus Slave使用方法

    万次阅读 多人点赞 2017-09-14 13:15:39
    Modbus测试工具ModbusPoll与Modbus Slave使用方法
  • 寄存器中按位定义数据的方法存在于很多厂家设备的MODBUS数据协议中。区别于线圈状态(RW)和离散输入(RO),寄存器中通过定义组合位数据,更有利于高效传输状态信息和设置开关量。 由于位数据被定义在寄存器中,其...
  • 本文介绍了一套实现四种modbus寄存器读写操作的函数与方法,在笔者的项目中有着大量的应用和验证。 需要说明的是,本套库函数是需要配合移植的FreeModbus库一起使用的。对于保持寄存器,由于需要实现掉电保存数据与...
  • Modbus

    2020-12-16 13:44:37
    Modbus Modbus协议:Modbus协议是用于电子控制器上的一种通用语言 ,ModbusRTU /ModbusASCII ModubusTCP ModubusUDP 特点:开放、无版权要求 Modbus协议支持多种通讯接口、Rs232 /Rs285 TCP/ip ModbusRTU报文格式:从...
  • linux 串口modbus操作

    千次阅读 热门讨论 2018-08-18 08:53:34
    标准modbus-RTU协议,分离底层接口,之前使用在STM32上,最近移植到linux,操作都是一样。 //uart.c 串口读写,没有特意去设置起始位什么的,通常都是8.N.1极少数使用奇偶校验了,毕竟通信协议上面都会带CRC。 /*...
  • 通过MODBUS TCP读写PLC源码,是c#代码,可以作为参考

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,158
精华内容 1,263
关键字:

modbus写操作