Qt Modbus 报文即QModbusRequest与sendRawRequest的使用

序言

记录下学习Modbus时,研究代码,思考验证了一下午后,终于豁然开朗。

环境

Qt5.12

:建议不要使用这个版本去学习或者使用Modbus,除非你不是用Qt的Modbus库,原因点这了解Qt 难找的崩溃原因或错误原因

理论解释,以RTU模式为例

写单个保持寄存器——查询报文

以写单个保持寄存器为例,下图就是查询报文的组成。
写入单个保持寄存器
而如何编写该报文呢?

我们主要用到这么两个:QModbusRequest类与sendRawRequest函数。

QModbusRequest用以构成报文中间的功能码及以下到差错校验之前的所有数据。

就比如上图的中间报文:0x06 0x00 0x95 0x04 0xB0

换成QModbusRequest就是利用其构造函数:功能码+对应的字节指令,功能码在QModbusRequest有对应枚举

int Addr = 3;
QModbusRequest message( QModbusRequest::WriteSingleRegister, quint16( 0x0095 ), quint16( 0x04B0 ) );
或
QModbusRequest message( QModbusRequest::WriteSingleRegister, QByteArray::fromHex( "009504B0" ) );

modbusRtuSerialMaster.sendRawRequest( message, Addr )

QModbusRtuSerialMaster中保存着波特率、数据位、停止位、奇偶校验、端口号等数据,并且内含RTU的CRC校验函数,会在sendRawRequest中自行校验,其它继承了QModbusClient的一样有效,只不过由于多态效果,含有的也有可能是LRC校验,只看使用类。

sendRawRequest将实参与QModbusRtuSerialMaster中的RTU数据与方法合成的报文发送到了对应设备上。

简单来说,就是QModbusRequest保存功能码及以下到差错校验之前的所有数据,sendRawRequest里会自行添加帧头与差错校验,并加上实参的从机地址,就发送了完整的报文。
.

写单个保持寄存器——响应报文

至于反馈的响应报文
响应报文1
响应报文2
sendRawRequest返回的QModbusReply中的rawResult()返回的QModbusResponse,这报文数据就储存在这里,使用modbusResponse.data().toHex()即可得到起始地址与变更数据。

报文字节类型要求

相信大家对字节命令有所疑惑,那我简单讲解下。

QModbusRequest中,其构造函数如下

QModbusRequest::QModbusRequest(const QModbusPdu &pdu)
QModbusRequest::QModbusRequest(QModbusPdu::FunctionCode code, const QByteArray &data = ...)
QModbusRequest::QModbusRequest(QModbusPdu::FunctionCode code, Args... data)

第一个重载没什么好说的,就是把一个写好的中间报文,到处可传,重复利用。

第二个重载,其以QByteArray作为字节指令存储的载体,QByteArray都知道,是以字节形式存储的,所以使用QByteArray也再好不过,例子如下。

QModbusRequest message( QModbusRequest::WriteSingleRegister, QByteArray::fromHex( "009504B0" ) );

其是以报文顺序存储的字节顺序,00是起始地址(高位),95是起始地址(低位),04是变更数据(高位),B0是变更数据(低位)。

第三个重载,其并没有注明类型是什么,这是因为可以让我们以普通类型存储,比如说unsigned char或unsigned short,举例如下。

QModbusRequest message( QModbusRequest::WriteSingleRegister, quint16( 0x0095 ), quint16( 0x04B0 ) );
或
QModbusRequest message( QModbusRequest::WriteSingleRegister, quint8( 0x00 ), quint8( 0x95 ), quint8( 0x04 ), quint8( 0xB0 ) );

报文要求的是以字节,而quint16实际为unsigned short,其为两个字节,无符号,因为字节指令基本都是以无符号存储,除了quint16,还可以使用quint8单字节无符号即unsigned char,但是除了这两个类型,其他都不能用。

官方原文:
Note: Usage is limited quint8 and quint16 only. This is because QDataStream stream operators will not only append raw data, but also e.g. size, count, etc. for complex types.

有道翻译:
注:仅限使用quint8和quint16。 这是因为QDataStream流操作符不仅会追加原始数据,还会追加复杂类型的大小、计数等。
.

报文大小端要求

Modbus作为一个行业标准,自然标定了固定的存储方式,那就是大端存储,从报文顺序就可见一二。

设备读取报文,从帧头读到帧尾,字节存储是以低地址连续存到高地址。

那么读完功能码之后,首先读到的就是高位起始地址,便是以低位内存地址存储高位有效字节,以高位内存地址存储低位有效字节,这很明显是大端模式。

所以如果你不是使用Qt,而是使用其他的平台,切记要记得这种报文的顺序。

过些时候我再写个Qt Modbus RTU的用法
未完待续

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
以下是使用Qt Modbus库编写的发送Modbus RTU报文的示例代码: ```cpp #include <QModbusDataUnit> #include <QModbusRtuSerialMaster> #include <QSerialPort> #include <QDebug> int main() { // 创建串口对象 QSerialPort serialPort; serialPort.setPortName("/dev/ttyS0"); serialPort.setBaudRate(QSerialPort::Baud9600); serialPort.setDataBits(QSerialPort::Data8); serialPort.setParity(QSerialPort::NoParity); serialPort.setStopBits(QSerialPort::OneStop); serialPort.setFlowControl(QSerialPort::NoFlowControl); // 创建Modbus RTU主站对象 QModbusRtuSerialMaster modbusMaster; modbusMaster.setConnectionParameter(QModbusDevice::SerialPortNameParameter, "/dev/ttyS0"); modbusMaster.setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity); modbusMaster.setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud9600); modbusMaster.setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8); modbusMaster.setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop); modbusMaster.setConnectionParameter(QModbusDevice::SerialFlowControlParameter, QSerialPort::NoFlowControl); modbusMaster.setTimeout(1000); modbusMaster.setNumberOfRetries(3); // 连接Modbus设备 if (!modbusMaster.connectDevice()) { qDebug() << "连接Modbus设备失败!"; return -1; } // 构造Modbus数据单元 QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, 0, 1); unit.setValue(0, 1234); // 发送Modbus RTU报文 if (auto *reply = modbusMaster.sendWriteRequest(unit, 1)) { if (!reply->isFinished()) { reply->deleteLater(); qDebug() << "发送Modbus RTU报文失败!"; return -1; } if (reply->error() != QModbusDevice::NoError) { qDebug() << "Modbus RTU报文错误:" << reply->errorString(); return -1; } qDebug() << "发送Modbus RTU报文成功!"; reply->deleteLater(); } else { qDebug() << "Modbus RTU请求发送失败!"; return -1; } // 断开Modbus设备连接 modbusMaster.disconnectDevice(); return 0; } ``` 上述代码中,首先创建了一个串口对象,并设置了串口参数。然后创建了Modbus RTU主站对象,设置了连接参数和超时重试次数。接着连接Modbus设备,并构造了一个Modbus数据单元。最后发送Modbus RTU报文,并在发送完成后断开Modbus设备连接。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

溪渣渣_梁世华

打赏?我甚至没有任何收费的章节

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值