做Modbus通讯难免会遇到数据转换的问题,因为Modbus的灵活性,对方的通讯数据可能是int,Dint,Uint,real等各种类型,而REAL还可能是4321、2143、1234等各种高低字节排列。以前做Modbus通讯都是写在一个FC里,读取的数据放在一个非优化访问的DB中,这样可以直接用物理地址访问通讯过来的各个数据,要想实现字节或者字的拆分和组合都比较方便。最近我在做标准功能块编程,也就是想把一个设备的通讯和数据处理做到一个FB功能块中,形成一个可重复调用的指令。由于目标是把数据通讯和数据处理都放在一个FB中,这样通讯数据就需要放在Static数据区的数组里,而对数组操作就不如直接用物理地址方便了。比如原来物理地址DB1.DBW0和DB1.DBW2可以直接通过访问DB1.DBD0而实现两个字组合成一个real或者Dint,使用数组后就不具备这种功能了。
在做一个电表的数据通讯时,遇到的问题就是它的大部分数据比如电压、电流等都是通过int类型传输的,后期通过乘以0.1或者0.01等还原成实际的实数,但是总电量数据是Dint类型的,占用了相邻W28和W29两个字。为了读取方便我是一次性读取全部数据,因为int类型占大多数,读取的数据就采用int类型的数组保存。在还原总电量Dint时我开始采用以下步骤:第一步将int28和29转换为Dint28和29;第二步用高字Dint28*65536+Dint29得到最终电量数据Dint。结果发现有的电表数据正确,有的不正确。后来查看运行结果发现问题就出在数据转换环节,我原本以为用CONVERT将int转换为Dint时的操作是将int放在Dint的低16位,但实际不是这样,符号位是被保留的,这样当低字比较大时显示为负数,用以上方法转换的结果就变小了。看了全部的数据转换指令,都没法将int转换为无符号的数,即使是将int转为UDint都不可以,最后的解决方案还是采用AT的方式,将int28和29传送给一个二维数组的int[0]和int[1],然后将二位数组AT到一个Dint变量中,最后访问该Dint变量得到实际数据。现在知道为什么博图具有AT这种访问功能了,就是因为符号编程数据处理不方便。
另外在做一个鼓风机数据通讯时,鼓风机的大部分数据是使用REAL类型传输,但是格式却不是西门子使用的4321,而是2143,所以当我用AT的方式还原real数据时全部是错的,虽然西门子有SWAP指令,但是SWAP只能实现1234到4321的转换,没法实现2143到4321的转换,最后的解决办法还是灵机一动想到使用循环移位指令,移位16位实现2143转换位4321格式。
以上数据转换问题在绝对寻址的情况下很好处理,但是在FB里做符号寻址就非常不方便了,个人认知也有限,希望介绍一点经验供大家参考,如果有热心同行有更好的解决方案希望留言讨论,不吝赐教。