由于有一个Modebus
上位机的需要,分析一下QModbus Slave
的源代码,方便后面的开发。
什么是Modbus
Modbus是一种常用的串行通信协议,被广泛应用于工业自动化领域。它最初由Modicon(目前属于施耐德电气公司)于1979年开发,旨在实现PLC(可编程逻辑控制器)和外部设备之间的数据交换。以下是对Modbus的详细解析:
一、Modbus协议的特点
- 免费使用:Modbus协议是免费开放的,用户可以自由使用,无需支付任何版权费用。
- 多种电气接口和传输介质:Modbus支持多种电气接口,如RS-232、RS-485等,以及多种传输介质,如双绞线、光纤、无线等,使得数据传输更加灵活。
- 帧格式简单:Modbus的帧格式简单易懂,方便开发人员快速上手和使用。
- 可靠性好:Modbus协议对数据进行了严格的校验,确保数据传输的可靠性。同时,它还支持主从方式定时收发数据,能够及时检测和恢复通信故障。
二、Modbus的传输方式与格式
Modbus协议可以使用串口和网线(含光纤)等方式进行传输,根据常用传输介质,它主要支持以下三种传输模式:
- ASCII模式:将数据以ASCII码形式进行传输,适用于低速率的串行通信。
- RTU模式:使用二进制格式传输数据,相比ASCII模式,在同样的波特率下可以传输更多的数据,适用于中高速率的串行通信。
- TCP/IP模式:利用TCP/IP协议进行网络通信,称为Modbus TCP/IP,适用于需要远程通信的场合。
三、Modbus协议的应用领域
Modbus协议广泛应用于工业自动化领域,包括但不限于以下几个方面:
- 工业自动化控制:连接PLC、传感器、执行器等设备,实现监控和控制功能。
- 智能家居:连接各种传感器和执行器,实现远程控制和监测。
- 能源监控:连接电表、燃气表、水表等设备,实现能源数据的采集和分析。
- 环境监测:连接各种传感器和仪器,监测环境参数如温度、湿度、气压等。
- 智能交通:连接交通控制设备、车辆检测器等,实现交通信号的控制和管理。
四、Modbus协议的工作原理
Modbus协议是一种使用主从关系实现的请求-响应协议。在主从关系中,通信总是成对发生——一个设备(主设备)发起请求,然后等待另一个设备(从设备)的响应。主站通常是人机界面(HMI)或监控和数据采集(SCADA)系统,从站则是传感器、PLC或可编程自动化控制器(PAC)等。
Modbus协议定义了一系列功能码,用于指定设备执行不同的操作。例如,读取线圈状态(Read Coil Status)功能码用于读取设备中的开关量输出状态;写单个寄存器(Write Single Register)功能码用于写入设备中的单个寄存器数据。在进行数据交换时,主设备会向从设备发送包含功能码和地址等信息的请求帧,从设备则根据请求帧中的信息执行相应的操作,并将结果以响应帧的形式返回给主设备。
五、总结
Modbus协议以其简单性、可靠性和广泛的兼容性,成为了工业自动化领域中最流行的通信协议之一。它支持多种电气接口和传输介质,适用于各种设备和系统之间的数据交换。同时,Modbus协议还具有良好的可扩展性和灵活性,能够满足不同应用场景的需求。
SettingDialog
在SettingDialog
中,是串口的参数配置,从上到下依次是:校验,波特率,数据位,停止位。
源代码
- 头文件
#ifndef SETTINGSDIALOG_H
#define SETTINGSDIALOG_H
#include <QtSerialBus/qtserialbusglobal.h>
#include <QDialog>
#if QT_CONFIG(modbus_serialport)
#include <QSerialPort>
#endif
QT_BEGIN_NAMESPACE
namespace Ui {
class SettingsDialog;
}
QT_END_NAMESPACE
class SettingsDialog : public QDialog
{
Q_OBJECT
public:
// 串口配置数据结构
struct Settings {
#if QT_CONFIG(modbus_serialport)
int parity = QSerialPort::EvenParity; // 校验位初始化为偶校验
int baud = QSerialPort::Baud19200; // 波特率初始化为19200
int dataBits = QSerialPort::Data8; // 数据位初始化为8位
int stopBits = QSerialPort::OneStop; // 停止位初始化为1位
#endif
};
explicit SettingsDialog(QWidget *parent = nullptr);
~SettingsDialog();
Settings settings() const;
private:
Settings m_settings;
Ui::SettingsDialog *ui;
};
#endif // SETTINGSDIALOG_H
- 源文件
#include "settingsdialog.h"
#include "ui_settingsdialog.h"
SettingsDialog::SettingsDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::SettingsDialog)
{
ui->setupUi(this);
ui->parityCombo->setCurrentIndex(1);
#if QT_CONFIG(modbus_serialport)
// 从下拉框获取配置
ui->baudCombo->setCurrentText(QString::number(m_settings.baud));
ui->dataBitsCombo->setCurrentText(QString::number(m_settings.dataBits));
ui->stopBitsCombo->setCurrentText(QString::number(m_settings.stopBits));
#endif
/*
信号和槽绑定,按下确认时修改默认参数,然后隐藏窗口。
*/
connect(ui->applyButton, &QPushButton::clicked, [this]() {
#if QT_CONFIG(modbus_serialport)
m_settings.parity = ui->parityCombo->currentIndex();
if (m_settings.parity > 0)
m_settings.parity++;
m_settings.baud = ui->baudCombo->currentText().toInt();
m_settings.dataBits = ui->dataBitsCombo->currentText().toInt();
m_settings.stopBits = ui->stopBitsCombo->currentText().toInt();
#endif
hide();
});
}
SettingsDialog::~SettingsDialog()
{
delete ui;
}
// 获取配置结果
SettingsDialog::Settings SettingsDialog::settings() const
{
return m_settings;
}
MainWindow
void MainWindow::initActions()
{
ui->actionConnect->setEnabled(true);
ui->actionDisconnect->setEnabled(false);
ui->actionExit->setEnabled(true);
ui->actionOptions->setEnabled(true);
connect(ui->connectButton, &QPushButton::clicked,
this, &MainWindow::onConnectButtonClicked);
connect(ui->actionConnect, &QAction::triggered,
this, &MainWindow::onConnectButtonClicked);
connect(ui->actionDisconnect, &QAction::triggered,
this, &MainWindow::onConnectButtonClicked);
connect(ui->connectType, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &MainWindow::onCurrentConnectTypeChanged);
connect(ui->actionExit, &QAction::triggered, this, &QMainWindow::close);
connect(ui->actionOptions, &QAction::triggered, m_settingsDialog, &QDialog::show);
}
连接函数
void MainWindow::onConnectButtonClicked()
{
// 判断是否连接
bool intendToConnect = (modbusDevice->state() == QModbusDevice::UnconnectedState);
statusBar()->clearMessage();
if (intendToConnect) {
// 确定连接方式 设置串口连接参数
if (static_cast<ModbusConnection>(ui->connectType->currentIndex()) == Serial) {
modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
ui->portEdit->text());
#if QT_CONFIG(modbus_serialport)
modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
m_settingsDialog->settings().parity);
modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
m_settingsDialog->settings().baud);
modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
m_settingsDialog->settings().dataBits);
modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
m_settingsDialog->settings().stopBits);
#endif
} else {
// 设置网络连接参数
const QUrl url = QUrl::fromUserInput(ui->portEdit->text());
modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());
}
modbusDevice->setServerAddress(ui->serverEdit->text().toInt());
if (!modbusDevice->connectDevice()) {
statusBar()->showMessage(tr("Connect failed: ") + modbusDevice->errorString(), 5000);
} else {
ui->actionConnect->setEnabled(false);
ui->actionDisconnect->setEnabled(true);
}
} else {
// 如果已经连接则断开
modbusDevice->disconnectDevice();
ui->actionConnect->setEnabled(true);
ui->actionDisconnect->setEnabled(false);
}
}