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();
}