文章目录
1.QT Modbus类
Qt的Modbus RTU库提供了一组API,用于实现Modbus RTU通信协议的主机(Master)和从机(Slave)功能。以下是Qt Modbus RTU库中的主要API及其功能的简要介绍:
QModbusRtuSerialMaster类:
- QModbusRtuSerialMaster是用于创建Modbus RTU主机的类。
- setConnectionParameter:配置串口参数,如串口名称、波特率、数据位等。
- connectDevice:连接到Modbus从机设备。
- sendReadRequest:发送读取请求以从Modbus从机读取数据。
- sendWriteRequest:发送写入请求以向Modbus从机写入数据。
- sendCustomRequest:发送自定义Modbus请求。
- disconnectDevice:断开与Modbus从机的连接。
- error:获取Modbus通信的错误状态。
- errorString:获取错误状态的字符串描述。
QModbusResponse类:
- QModbusResponse是Modbus RTU响应的基类。
- error:获取响应的错误状态。
- errorString:获取错误状态的字符串描述。
- rawResult:获取原始响应数据。
QModbusDataUnit类:
- QModbusDataUnit用于表示Modbus RTU数据单元,包括读取或写入的寄存器类型、起始地址和数据。
- HoldingRegisters:保持寄存器类型。
- InputRegisters:输入寄存器类型。
- Coils:线圈类型。
- DiscreteInputs:离散输入类型。
- startAddress:获取数据单元的起始地址。
- valueCount:获取数据单元中数据值的数量。
- value:获取或设置数据单元中特定位置的数据值。
QModbusReply类:
- QModbusReply是Modbus RTU请求的响应。
- error:获取响应的错误状态。
- errorString:获取错误状态的字符串描述。
- result:获取响应中的数据单元结果。
- finished:信号,指示请求已完成。
QModbusClient类(可选):
- QModbusClient是Qt中的Modbus RTU客户端类,提供更高级别的Modbus RTU主机功能。
QModbusServer类(可选):
- QModbusServer是Qt中的Modbus RTU服务器类,用于实现Modbus RTU从机功能。
以上是Qt Modbus RTU库的一些核心API,用于实现Modbus通信的常见任务。您可以根据您的具体需求使用这些API来创建Modbus RTU主机或从机,并进行读取和写入操作。在实际使用中,根据Qt版本可能会有一些差异,因此建议查阅相关Qt文档以获取更详细的信息和示例代码。
2.Modbus主机和从机概念
Modbus是一种串行通信协议,常用于工业环境中设备之间的通信。在Modbus协议中,有两种角色:主机(Master)和从机(Slave)。它们之间的主要区别在于通信过程中的作用和职责。
-  Modbus主机(Master): - 控制角色:Modbus主机控制着数据交换过程,它发起请求并接收响应。
- 发起请求:主机发送命令给从机,这些命令可以是读取数据、写入数据、诊断等。
- 处理响应:主机接收并处理从机的响应数据。
- 通常只有一个主机:在一个Modbus网络中,通常只有一个主机控制所有的通信。
 
-  Modbus从机(Slave): - 被动角色:从机响应主机的请求,不会主动发起通信。
- 执行请求:从机根据主机的命令执行操作,如返回数据或更改其内部寄存器的值。
- 多个从机:一个Modbus网络可以有多个从机,每个从机都有一个唯一的地址。
- 简单设备:从机通常是传感器、执行器、驱动器等较为简单的设备。
 
在Modbus协议中,主机和从机之间的通信是不对称的。主机总是发起通信,而从机只是对主机的请求做出响应。这种模式使得Modbus非常适合于工业自动化和控制系统,其中一个中央控制器(主机)需要与多个现场设备(从机)进行通信。
组网
Modbus 协议支持几种不同的组网形状(拓扑结构)。这些拓扑结构的选择取决于具体的应用需求、物理环境和设备类型。以下是Modbus最常见的组网形状:
- 总线(Bus)拓扑: 
  - 所有设备都连接到一个共享的通信线路上。
- 每个设备都通过其唯一的地址来识别。
- 这种拓扑简单且易于实施,但线路故障可能影响整个网络。
 
-  星形(Star)拓扑: -  每个从机通过独立的线路直接连接到主机。 
-  这种拓扑可以降低线路故障对整个网络的影响,但可能需要更多的布线。 
 
-  
3.树形(Tree)或分支(Branched)拓扑:
- 类似于总线拓扑,但在主通信线路上可以有分支。
- 这种拓扑更灵活,可以更好地适应复杂的物理环境。
从机示例
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QModbusRtuSerialMaster>
#include <QSerialPort>
#include <QDebug>
#include <QVBoxLayout>
class ModbusMasterWidget : public QWidget {
public:
    ModbusMasterWidget(QWidget *parent = nullptr) : QWidget(parent) {
        modbusMaster = new QModbusRtuSerialMaster(this);
        // 配置串口
        modbusMaster->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM1");
        modbusMaster->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
        modbusMaster->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud19200);
        modbusMaster->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
        modbusMaster->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
        QVBoxLayout *layout = new QVBoxLayout(this);
        QPushButton *readButton = new QPushButton("读取Modbus数据", this);
        layout->addWidget(readButton);
        connect(readButton, &QPushButton::clicked, this, &ModbusMasterWidget::onReadButtonClicked);
    }
private slots:
    void onReadButtonClicked() {
        if (!modbusMaster->connectDevice()) {
            qDebug() << "无法连接Modbus主机!";
            return;
        }
        const int serverAddress = 1; // Modbus从机地址
        QModbusDataUnit readRequest(QModbusDataUnit::HoldingRegisters, 0, 10); // 读取保持寄存器
        if (auto *reply = modbusMaster->sendReadRequest(readRequest, serverAddress)) {
            if (!reply->isFinished()) {
                connect(reply, &QModbusReply::finished, this, &ModbusMasterWidget::onReadFinished);
            } else {
                delete reply; // 同步请求已完成
            }
        } else {
            qDebug() << "无法发送Modbus请求!";
        }
    }
    void onReadFinished() {
        auto *reply = qobject_cast<QModbusReply *>(sender());
        if (!reply) {
            return;
        }
        if (reply->error() == QModbusDevice::NoError) {
            const QModbusDataUnit unit = reply->result();
            for (uint i = 0; i < unit.valueCount(); i++) {
                qDebug() << "寄存器" << unit.startAddress() + i << "的值:" << unit.value(i);
            }
        } else {
            qDebug() << "Modbus错误:" << reply->errorString();
        }
        reply->deleteLater();
    }
private:
    QModbusRtuSerialMaster *modbusMaster;
};
int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    ModbusMasterWidget widget;
    widget.show();
    return app.exec();
}
主机示例
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QModbusRtuSerialMaster>
#include <QSerialPort>
#include <QDebug>
#include <QVBoxLayout>
class ModbusMasterWidget : public QWidget {
public:
    ModbusMasterWidget(QWidget *parent = nullptr) : QWidget(parent) {
        modbusMaster = new QModbusRtuSerialMaster(this);
        // 配置串口
        modbusMaster->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM1");
        modbusMaster->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
        modbusMaster->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud19200);
        modbusMaster->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
        modbusMaster->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
        QVBoxLayout *layout = new QVBoxLayout(this);
        QPushButton *writeButton = new QPushButton("设置Modbus数据", this);
        layout->addWidget(writeButton);
        connect(writeButton, &QPushButton::clicked, this, &ModbusMasterWidget::onWriteButtonClicked);
    }
private slots:
    void onWriteButtonClicked() {
        if (!modbusMaster->connectDevice()) {
            qDebug() << "无法连接Modbus主机!";
            return;
        }
        const int serverAddress = 1; // Modbus从机地址
        QModbusDataUnit writeRequest(QModbusDataUnit::HoldingRegisters, 0, 1); // 写入单个保持寄存器
        writeRequest.setValue(0, 1234); // 设置寄存器的值
        if (auto *reply = modbusMaster->sendWriteRequest(writeRequest, serverAddress)) {
            if (!reply->isFinished()) {
                connect(reply, &QModbusReply::finished, this, &ModbusMasterWidget::onWriteFinished);
            } else {
                delete reply; // 同步请求已完成
            }
        } else {
            qDebug() << "无法发送Modbus写入请求!";
        }
    }
    void onWriteFinished() {
        auto *reply = qobject_cast<QModbusReply *>(sender());
        if (!reply) {
            return;
        }
        if (reply->error() == QModbusDevice::NoError) {
            qDebug() << "写入操作成功完成";
        } else {
            qDebug() << "Modbus写入错误:" << reply->errorString();
        }
        reply->deleteLater();
    }
private:
    QModbusRtuSerialMaster *modbusMaster;
};
int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    ModbusMasterWidget widget;
    widget.show();
    return app.exec();
}










