网站建设不备案后果,免费做头像网站有哪些,西安市干部教育网站建设,建设银行山西招聘网站Qt的Modbus协议-RTU从站实现 1、Modbus协议1.1 modbusRTU 协议格式1.2 读寄存器功能码0x032、Qt实现Modbus RTU协议2.1 添加相关的库2.2 添加相关的头文件2.3 设置串口参数2.4 一秒读取一次从机数据2.5 处理读取数据2.6 大小端排序 3、文件3,1 头文件3.2 cpp文件 4、总结 1、Mo… Qt的Modbus协议-RTU从站实现 1、Modbus协议1.1 modbusRTU 协议格式1.2 读寄存器功能码0x032、Qt实现Modbus RTU协议2.1 添加相关的库2.2 添加相关的头文件2.3 设置串口参数2.4 一秒读取一次从机数据2.5 处理读取数据2.6 大小端排序 3、文件3,1 头文件3.2 cpp文件 4、总结 1、Modbus协议
Modbus协议是一种工业自动化领域广泛应用的串行通信协议由Modicon公司现施耐德电气于1979年推出现已成为工业设备通信的业界标准13。
1.1 modbusRTU 协议格式
设备地址设备的通讯地址、站号。 功能码对数据帧的功能编号。 寄存器存放某类数据的内存区域。一个设备可能有多种寄存器不同的寄存器存放不同类别的数据。 寄存器地址某个数据在寄存器里的编号。不同的设备定义不同。
1.2 读寄存器功能码0x03 这个意思是设备地址为04功能码是03寄存器起始地址为 0x80查询数据分别为 0x1234和 0x5678;
2、Qt实现Modbus RTU协议
2.1 添加相关的库
QT core gui serialport serialbus2.2 添加相关的头文件
#include QSerialPort
#include QtSerialBus
#include QModbusDataUnit
#include QModbusClient2.3 设置串口参数
// 函数功能初始化Modbus RTU串行通信连接
void mainInterface::setupModbusConnectxion()
{// 创建Modbus RTU主设备实例modbusDevice new QModbusRtuSerialMaster(this);// 配置串口通信参数标准Modbus RTU设置modbusDevice-setConnectionParameter(QModbusDevice::SerialPortNameParameter, COM1); // 使用COM1端口modbusDevice-setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud115200); // 波特率115200modbusDevice-setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8); // 8位数据位modbusDevice-setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop); // 1位停止位modbusDevice-setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity); // 无校验位// 连接错误信号槽Lambda表达式处理错误connect(modbusDevice, QModbusDevice::errorOccurred, [](QModbusDevice::Error error) {qDebug() Modbus Error: error; // 打印错误类型如超时、校验错误等});// 尝试建立物理连接if (!modbusDevice-connectDevice()) {qDebug() 无法连接; // 连接失败可能端口被占用或参数错误} else {qDebug() 成功连接; // 连接成功可开始发送Modbus请求}
}2.4 一秒读取一次从机数据
利用定时器来达到1秒钟读取一次 //初始化定时器dataTimer new QTimer(this);connect(dataTimer, QTimer::timeout, this, mainInterface::readFlowData);dataTimer-start(1000); // 每秒读取一次读取数据 // 创建Modbus数据读取单元// 参数说明// 1. HoldingRegisters - 读取保持寄存器类型,对应功能码0x03// 2. 4104 - 起始寄存器地址对应设备文档中的4104或0x1008// 3. 2 - 读取2个寄存器32位数据通常需要2个16位寄存器QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, 4104, 2);// 发送读取请求到从设备设备地址为4if (auto *reply modbusDevice-sendReadRequest(readUnit, 4)) {// 检查请求是否已完成立即完成可能表示错误if (!reply-isFinished()) {// 连接完成信号到处理槽函数connect(reply, QModbusReply::finished, this, mainInterface::handleReadResult);} else {// 立即完成的异常情况释放reply对象delete reply;}} else {// 发送请求失败输出错误信息qDebug() 读取错误: modbusDevice-errorString();}
}2.5 处理读取数据
// 函数功能处理Modbus读取请求的返回结果
void mainInterface::handleReadResult()
{// 获取发送信号的QModbusReply对象即触发此槽的reply对象auto reply qobject_castQModbusReply *(sender());if (!reply){return; // 转换失败则直接返回}// 检查Modbus通信是否无错误if (reply-error() QModbusDevice::NoError) {// 获取返回的寄存器数据单元const QModbusDataUnit unit reply-result();// 准备存储转换结果的变量QString dataStr;float flowValuee 0;// 将两个寄存器的值转换为浮点数// 参数说明// unit.value(0) - 第一个寄存器的值高16位// unit.value(1) - 第二个寄存器的值低16位// 0 - 可能的转换选项如字节序flowValuee convertToFloat(unit.value(0), unit.value(1), 0);// 在UI上显示带一位小数的流量值ui-flowlabel-setText(QString::number(flowValuee, f, 1));}else {// 输出错误信息到调试窗口qDebug() 读取数据错误 reply-errorString();}// 安全释放reply对象Qt的内存管理机制reply-deleteLater();
}2.6 大小端排序
/*** brief 将两个16位寄存器值转换为32位浮点数* param reg1 第一个寄存器值高/低16位取决于字节序* param reg2 第二个寄存器值高/低16位取决于字节序* param isBigEndian 字节序标志true表示大端模式false表示小端模式* 大端模式高字节在低地址低字节在高地址* 小端模式低字节在低地址高字节在高地址* return 转换后的32位浮点数*/
float mainInterface::convertToFloat(quint16 reg1, quint16 reg2, bool isBigEndian)
{// 使用联合体实现二进制数据到浮点数的类型转换union {quint32 i; // 32位整型用于存储组合后的寄存器值float f; // 32位浮点数用于最终输出} converter;// 根据字节序组合两个16位寄存器if (isBigEndian) {// 大端模式reg1为高16位reg2为低16位converter.i (reg1 16) | reg2;} else {// 小端模式reg2为高16位reg1为低16位converter.i (reg2 16) | reg1;}// 通过联合体直接返回浮点数表示return converter.f;
}3、文件
3,1 头文件
#ifndef MAININTERFACE_H
#define MAININTERFACE_H#include QWidget
#include QDateTime
#include QTimer
#include QSerialPort
#include QtSerialBus
#include QModbusDataUnit
#include QModbusClient
#include QProcess
#include windows.h
#include QMessageBoxnamespace Ui {
class mainInterface;
}class mainInterface : public QWidget
{Q_OBJECTpublic:explicit mainInterface(QWidget *parent nullptr);~mainInterface();typedef union /*申明一个联合体*/
{
float dataf;
uint32_t data32;
uint16_t data16[2];
uint8_t data8[4];
} data_t;private slots:void on_exitPushButton_clicked();void readFlowData();void updateDateTime();void handleReadResult();void on_keyboardPushButton_clicked();void on_historyPushButton_clicked();void on_setPushButton_clicked();void on_helpPushButton_clicked();private:Ui::mainInterface *ui;QTimer *dataTimer;QTimer *clockTimer;QModbusClient *modbusDevice;void setupModbusConnection();float parseFloatFromRegisters(const QVectorquint16 registers);quint16 swapBytes(quint16 value);QProcess *keyboardProcess;bool keyboardVisible;float convertToFloat(quint16 reg1, quint16 reg2, bool isBigEndian);};#endif // MAININTERFACE_H3.2 cpp文件
#include maininterface.h
#include ui_maininterface.hmainInterface::mainInterface(QWidget *parent) :QWidget(parent),ui(new Ui::mainInterface)
{ui-setupUi(this);keyboardVisible false;this-setWindowFlag(Qt::FramelessWindowHint); //去边框// this-setAttribute(Qt::WA_TranslucentBackground); //半透明背景//初始化定时器dataTimer new QTimer(this);connect(dataTimer, QTimer::timeout, this, mainInterface::readFlowData);dataTimer-start(1000); // 每秒读取一次//更新时间clockTimer new QTimer(this);connect(clockTimer, QTimer::timeout, this, mainInterface::updateDateTime);clockTimer-start(1000); // 每秒更新一次时间// 初始化键盘进程keyboardProcess new QProcess(this);keyboardVisible false;//初始化Modbus连接setupModbusConnection();//初始化时间显示updateDateTime();}mainInterface::~mainInterface()
{delete ui;
}//退出
void mainInterface::on_exitPushButton_clicked()
{QMessageBox quitMes; //创建退出弹窗对象quitMes.setWindowTitle(关闭界面); //弹窗标题quitMes.setWindowIcon(QIcon(:/widdgetMainInterface/exit.png)); //设置窗口图标quitMes.setIcon(QMessageBox::Warning); //弹窗图片quitMes.setText(请确认是否退出); //弹窗文本quitMes.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); //设置Ok和Cancle两个按钮quitMes.setButtonText(QMessageBox::Ok, 确认); //Ok改为确认quitMes.setButtonText(QMessageBox::Cancel,取消); //Cancle改为取消int result quitMes.exec(); //显示信息框等待用户交互//如果用户选择了Okif(result QMessageBox::Ok){this-close(); //关闭界面}else //用户取消什么都不做{}this-close();
}//设置串口参数
void mainInterface::setupModbusConnection()
{modbusDevice new QModbusRtuSerialMaster(this);// 设置Modbus RTU参数modbusDevice-setConnectionParameter(QModbusDevice::SerialPortNameParameter, COM1);modbusDevice-setConnectionParameter(QModbusDevice::SerialBaudRateParameter,QSerialPort::Baud115200);modbusDevice-setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);modbusDevice-setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);modbusDevice-setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);// 连接错误信号connect(modbusDevice, QModbusDevice::errorOccurred, [](QModbusDevice::Error error) {qDebug() Modbus Error: error;});// 尝试连接设备if (!modbusDevice-connectDevice()) {qDebug() 无法连接;} else {qDebug() 成功连接;}
}//读取数据
void mainInterface::readFlowData()
{if (!modbusDevice || modbusDevice-state() ! QModbusDevice::ConnectedState){return;}// 读取流量寄存器 (地址4104-4105, 对应0x1008-0x1009)QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, 4104, 2);// QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, 0, 2);if (auto *reply modbusDevice-sendReadRequest(readUnit, 4)) { // 设备地址4if (!reply-isFinished()) {connect(reply, QModbusReply::finished, this, mainInterface::handleReadResult);} else {delete reply;}} else {qDebug() 读取错误: modbusDevice-errorString();}
}//更新时间
void mainInterface::updateDateTime()
{QDateTime currentTime QDateTime::currentDateTime();QString ymdTimeStr currentTime.toString(yyyy年MM月dd日);QString hmsTimeStr currentTime.toString(hh:mm:ss);ui-currentTimeymdlabel-setText(ymdTimeStr);ui-currentTimehmslabel-setText(hmsTimeStr);}//大小端排序
//大端模式高字节在低地址低字节在高地址
//小端模式低字节在低地址高字节在高地址
float mainInterface::convertToFloat(quint16 reg1, quint16 reg2, bool isBigEndian)
{union {quint32 i;float f;} converter;if (isBigEndian) {converter.i (reg1 16) | reg2;} else {converter.i (reg2 16) | reg1;}return converter.f;
}//处理读取数据
void mainInterface::handleReadResult()
{auto reply qobject_castQModbusReply *(sender());if (!reply){return;}if (reply-error() QModbusDevice::NoError) {const QModbusDataUnit unit reply-result();// 将数据值转换为字符串QString dataStr;float flowValuee 0;flowValuee convertToFloat(unit.value(0),unit.value(1),0);// 显示带一位小数的结果ui-flowlabel-setText(QString::number(flowValuee, f, 1));}else {qDebug() 读取数据错误 reply-errorString();}reply-deleteLater();}//高低字节交换
quint16 mainInterface::swapBytes(quint16 value)
{return ((value 8) 0xFF00) | ((value 8) 0x00FF);}//转换数据
float mainInterface::parseFloatFromRegisters(const QVectorquint16 registers)
{// 根据协议文档每个寄存器内部进行字节交换quint16 low swapBytes(registers[0]);quint16 high swapBytes(registers[1]);//组合为32位整数// qint32 combined (static_castquint32(high)16) | low;quint32 combined (qFromBigEndian(high) 16) | qFromBigEndian(low);// 重新解释为浮点数float result;memcpy(result, combined, sizeof(result));return result;}//软键盘
void mainInterface::on_keyboardPushButton_clicked()
{if (!keyboardVisible) {bool started false;// 方法1: 使用QProcess尝试启动keyboardProcess-start(osk.exe);if (keyboardProcess-waitForStarted(1500)) {keyboardVisible true;started true;qDebug() 软键盘已启动 (QProcess);}// 方法2: 如果QProcess失败尝试完整路径if (!started) {QString oskPath C:\\Windows\\System32\\osk.exe;keyboardProcess-start(oskPath);if (keyboardProcess-waitForStarted(1500)) {keyboardVisible true;started true;qDebug() 软键盘已启动 (完整路径);}}// 方法3: 如果前两种方法失败使用Windows API绕过重定向if (!started) {qDebug() 尝试使用Windows API启动软键盘;// 禁用文件系统重定向PVOID oldValue;Wow64DisableWow64FsRedirection(oldValue);// 使用ShellExecute启动HINSTANCE result ShellExecuteW(0,Lopen,Losk.exe,0,0,SW_SHOWNORMAL);// 恢复文件系统重定向Wow64RevertWow64FsRedirection(oldValue);if (reinterpret_castint(result) 32) {keyboardVisible true;started true;qDebug() 软键盘已启动 (Windows API);} else {qDebug() Windows API启动失败, 错误码: reinterpret_castint(result);}}if (!started) {QMessageBox::warning(this, 错误, 无法启动软键盘。请确保系统支持屏幕键盘(osk.exe)。);}else {// 关闭软键盘if (keyboardProcess-state() QProcess::Running) {keyboardProcess-terminate();if (keyboardProcess-waitForFinished(1000)) {keyboardVisible false;qDebug() 软键盘已关闭;} else {qDebug() 无法关闭软键盘: keyboardProcess-errorString();}} else {keyboardVisible false;}}}}//历史记录
void mainInterface::on_historyPushButton_clicked()
{QMessageBox::information(this,历史记录,查看历史记录);
}//设置
void mainInterface::on_setPushButton_clicked()
{QMessageBox::information(this, 系统设置, 系统设置功能);
}//帮助
void mainInterface::on_helpPushButton_clicked()
{QMessageBox::information(this, 帮助, 流量监控系统帮助文档\n\n1. 系统每秒自动更新流量数据\n2. 点击键盘图标调用系统软键盘\n);
}仓库地址
4、总结
以上就是Qt的Modbus协议-RTU从站实现的整个过程了浏览过程中如若发现错误欢迎大家指正有问题的欢迎评论区留言或者私信。最后如果大家觉得有所帮助可以点一下赞谢谢大家祝大家天天开心顺遂无虞