序言
记录下学习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
里会自行添加帧头与差错校验,并加上实参的从机地址,就发送了完整的报文。
.
写单个保持寄存器——响应报文
至于反馈的响应报文
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的用法