上一个网文我们使用MODSCAN32软件和串口助手调试了Modbus协议,我们之前还分享过使用Qt编写自己的串口助手,今天我们在上次分享的串口助手的源码基础上,使用Qt编写一个Modbus从机程序,让其与MODSCAN32软件实现数据交互。
实现目标
熟悉Modbus协议0x03和0x06功能码
熟悉QTableView控件的使用
Qt编写的Modbus从设备与ModScan32软件进行数据交互
结果展示
素材获取
在上面视频中有源码的获取方式,欢迎关注小哈哥的视频号,更多精彩敬请期待。
读保持寄存器功能码0x03
当主机发送0x03功能码时,表明主机想要获取从机某些寄存器里的内容。
举例: 请求读寄存器108~110:
由上图我们可知,寄存器108的内容为02 2B,即十进制的555,寄存器109~110的内容分别为十六进制的00 00 和 00 64,或十进制的0和100。
写单个寄存器功能码0x06
0x06功能码是主机用来向从机的某个寄存器写数据,一次只能操作一个寄存器。
使用功能码0x06写单个保持寄存器。
举例: 请求将十六进制00 03写入寄存器2。
写多个保持寄存器功能码0x10
将十六进制 00 0A 和 01 02 写入从寄存器 2 开始的两个寄存器的实例:
注意:上面截图的实例中,未包含地址位和校验码。
0x03和0x06功能码调试
主从机寄存器地址设置不一致
上面主从机寄存器地址设置不一致的返回:MODSIM32收到的数据包:01 03 00 00 00 06 C5 C8
MODSIM32返回的数据包:01 83 02 C0 F1
主从机寄存器地址一致
MODSIM32收到的数据包:01 03 00 00 00 06 C5 C8
MODSIM32返回的数据包:01 03 0C 01 00 00 00 00 00 00 00 00 00 00 00 6E B3
主机修改值
使用Modbus功能码0x06:写单路寄存器,实现对从机单个寄存器值的修改。
修改地址2的寄存器值为0x00FF
MODSCAN32发送的数据包:01 06 00 01 00 FF 98 4A
MODSIM32返回的应答包:01 06 00 01 00 FF 98 4A
主机发送的报文格式:
主机发送 | 字节数 | 发送的信息 |
---|---|---|
从机地址 | 1 | 01 |
功能码 | 1 | 06 |
起始地址 | 2 | 0x0001 |
写入数据 | 2 | 0x00FF |
CRC码 | 2 | 0x984A |
从机响应返回的报文格式,与主机发送的报文格式及数据内容完全相同。
修改地址6的寄存器值为0xFF00
从机修改值
修改地址4的寄存器值为0x0008
由上我们可以看出来,从机修改完值无需上传数据包(因为Modbus是主从模式,从机不能主动上传数据),等待下次主机查询寄存器状态的时候,上报即可。
注意:上面演示使用的ModSim32软件作为从机,MODSCAN32软件作为主机。
基于Qt的Modbus从机程序
有了上面的演示,我们知道了主机发送不同功能码的时候,我们应该返回给主机什么样的数据内容,下面我们在之前我们分享的《 使用Qt打造属于自己的串口调试助手 》的基础上,完成0x03和0x06功能码的应答,最终程序的界面显示如下。
因为Modbus协议是主从模式,从机不会主动发数据包至主机,从机只要解析接收到的数据包,然后根据协议规定返回对应的内容即可。
所以我们的Qt程序,主要工作就是解析收到的串口数据,因此,在串口接收函数中,解析收到的数据包具体需要做如下工作:
将接收到的前6个字节数据进行CRC计算;
CRC计算的结果跟接收到的第7、第8字节比较,如果一致,证明数据包没有问题;
如果CRC正确,那么根据接收到的第2字节进行判断,当第2字节为0x03时,为Modbus读寄存器的操作;当第2字节为0x06时,为Modbus写单寄存器的操作;
如果功能码为0x03,则获取TableView第2列内的所有数据,作为返回数据包的寄存器值,然后将地址位、功能码、数据长度、寄存器值这些进行CRC校验,然后组成一个数据包发送至主机;
如果功能码为0x06,则将收到数据包的数据位组成一个WORD类型的整形数,根据寄存器地址,将其赋值到TableView中对应的单元格中。
Qt实现的Modbus从机程序大家可以参考本文提供的源码。
总结
经过我们这两次Modbus协议的分享,大家可以试试其他功能码的发送与应答,自己随意玩起吧。