其下发的命令格式为:域名+功能码+起始地址+输出数量+字节数+输出值。
3、基本数据单元的编程
经过上面的分析,我们发现不论是在什么样的物理链路上实现的应用数据,器基本数据段都是相同的。其实加上域名段的格式也是相同的,所以我们就将
域名+PDU一起作为最基本的数据单元来实现。
对于基本数据单元的实现由分为2种情况:一是作为主站(客户端)时,对从站(服务器)的下发命令;二是作为从站(服务器)时,对主站(客户端)命令的响应。所以我们将这两种情况分别封装为2个基础函数:
(1)、作为RTU主站(TCP客户端)时,生成读写RTU从站(TCP服务器)对象的命令:
uint16_t GenerateReadWriteCommand(ObjAccessInfo objInfo,bool *statusList,uint16_t *registerList,uint8_t *commandBytes)
参数分别是PDU单元的基本信息,写对象的对应数据,以及生成的命令字节。而返回值则是生成的命令的长度。
(2)、作为从站(服务器)时,生成主站读访问的响应。对于响应因为写操作的响应实际上就是复制主站(客户端)的命令的一部分,所以我们实际需要生成的响应是包括0x01、0x02、0x03、0x04功能码的情形。
uint16_t GenerateMasterAccessRespond(uint8_t *receivedMessage,bool *statusList,uint16_t *registerList,uint8_t *respondBytes)
参数分别是接收到的信息,读取的对象的数据,以及返回的响应消息。而返回值则是返回的响应消息的长度。
4、RTU应用数据单元的编程
对于RTU应用数据单元来说,其报文格式就是:“域名+PDU+CRC”,而域名+PDU我们在上一节中已经实现了,所以要实现RTU的数据单元实际上我们只需要加上CRC校验就已经完成了。
对于RTU数据单元的实现由分为2种情况:一是作为主站时,对从站的下发命令;二是作为从站时,对主站命令的响应。所以我们将这两种情况分别封装为2个基础函数:
(1)、作为RTU主站时,生成读写RTU从站对象的命令:
/*生成读写从站数据对象的命令,命令长度包括2个校验字节*/
uint16_t Synthe
ticReadWriteSlaveCommand(ObjAccessInfo slaveInfo,bool *statusList,uint16_t *registerList,uint8_t *commandBytes)
参数分别是从站基本信息,下发的数据列表,以及最终生成的命令数组。返回值是是命令的长度。
(2)、作为从站时,生成主站读访问的响应:
/*生成从站应答主站的响应*/
uint16_t SyntheticSlaveAccessRespond(uint8_t *receivedMessage,bool *statusList,uint16_t *registerList,uint8_t *respondBytes)
参数分别是接收到的信息,返回的数据列表,生成的响应信息列表。返回值是响应信息列表的长度。
5、TCP应用数据单元的编程
而对于TCP应用数据单元来说,与RTU类式,起报文格式是:“MBAP头+PDU”,而PDU单元就是前面定义的,所以只需要加上MBAP头部就可以了,事实上MBAP头部的实现格式是固定的。
对于TCP应用数据单元的实现同样分为2中情况:一是作为客户端时,对服务器的下发命令;二是作为服务器时,对客户端命令的响应。所以我们将这两种情况分别封装为2个基础函数:
(1)、作为TCP客户端时,生成读写TCP服务器对象的命令:
/*生成读写服务器对象的命令*/
uint16_t SyntheticReadWriteTCPServerCommand(ObjAccessInfo objInfo,bool *statusList,uint16_t *registerList,uint8_t *commandBytes)
(2)、作为(服务器时,生成客户端读写访问的响应:
/*合成对服务器访问的响应,返回值为命令长度*/
uint16_t SyntheticServerAccessRespond(uint8_t *receivedMessage,bool *statusList,uint16_t *registerList,uint8_t *respondBytes)
6、结束语
其实到这里我们对Modbus基本协议已经基本实现,甚至使用这些基本操作也能实现Modbus的通讯。事实上很多人在应用写的Modbus通讯协议比这还要简单,也能实现部分的Modbus通讯功能。当然这不是我们的目标,否则就不需要专门开发库了,我们要进一步封装,让其更通用也更易用才是我们需要的。