1.概述Modbus通讯协议是施耐德电气开发并对外完全公布其细节的开放式通讯协议,得到众多厂商的广泛支持,从而有大量的第三方设备支持Modbus通讯,目前Modbus协议已经成为事实上的工业通讯标准,并被我国列为国标之一。在目前工业控制中使用Modbus协议越来越普遍的情况下,一台PLC与多台支持Modbus协议的设备进行通讯的应用也成为目前的一种趋势,在这种应用中通过一条或数条Modbus总线,连接多个设备,替代老式设备中的模拟量加数字量的控制方式,既能更加灵活地完成控制要求,又能节约大量的模拟量加数字量,从而降低设备的成本。虽然在PLC中对Modbus通讯编程非常简单,但如果在Modbus总线上有多个子站需要通讯时,PLC中程序的篇幅会非常长,并占用很多PLC的内部寄存器资源。2在施耐德电气的PLC编程软件中的更好的实现方法在这里以施耐德电气的Twido系列PLC为例进行介绍。基于编程语言的相似性,以下程序实现方法在施耐德电气的Micro及Premium系列PLC(PL7Pro平台下)很容易移植。先看一下在Twido中实现与一个Modbus子站(以施耐德电气的ATV31变频器为例)通讯的程序段:在上面的示范程序中,主要为Modbus通讯所需的数据区填写相应的内容,详细说明请参考Twido的手册或其帮助文件。数据区准备完成后需要用下面指令将该数据区发送出去:如果与多个Modbus子站通讯时,就要编写多个与上述程序段长度相同而内容略有不同的程序段。在重复做这部分程序时,使程序的篇幅大大加长,占用大量的内部资源(如内部字MW),并使得PLC的扫描周期加长,这样一来,使编程者需要考虑如何优化这类的程序,使之更加简洁高效。分析上面的通讯程序,会得到几个提示:1、不同子站Modbus通讯的数据区除子站地址部分外是完全一样的;2、数据区发送出去的交换指令格式是完全一样的3、在某一时刻,PLC只能做一条读或写操作(广播方式除外)4、读操作读回的数据在数据区起始位置的偏移量为6处开始(请参考相应资料)从上面几点分析着手,完全可以将与多个Modbus子站通讯优化,只要处理好在某一时刻将要与之通讯的子站地址填写好,在通讯数据区用交换指令发送出去之后,及时将读回来的数据取出并保存好即可。下面对实现这一功能的程序的部分程序段做一说明,在这个程序中以读施耐德电气的3台ATV31变频器的状态及输出频率,并通过通讯控制ATV31的运行频率。为减小本文的长度,在这个程序ATV31的启停及正反转控制用端子实现,在实际应用中,用通讯完成变频器的启停及正反转控制也是完全没有问题的。与每一个子站通讯的时间长度控制,其中M2由系统时钟位S5产生:在程序中需要使用一个计数器来控制PLC与某一台ATV31的具体的通讯:此计数器的作用在后面的程序中可以体现出来。填写通讯数据区中每个子站不相同的部分,这部分数据必须存放两个连续的数据区中,以使后面的数据取出程序能使用间接寻址的方式,这也是优化程序的一个比较关键之处。在这一例子程序中,将变频器的输出频率值的控制简化了,使3台变频器的速度全部为30Hz,实际使用时,可能参考后面的读操作返回数据的保存方式,运用间接寻址方式编写出相应的代码:根据调度计数器的当前值,将与之对应的子站数据调入(使用了间接寻址):通讯数据区中相同部分的填写(注意其中MW202及MW232由上面程序完成了):将通讯数据区用交换指令发送出去,MW481用来控制读或写操作:下面是比较关键的一步,将通讯读回的数据保存起来,如1号变频器的状态及输出频率分别放在MW301及MW331中,2号变频器数据放入MW302及MW332,3号变频器数据放入MW303及MW333。程序中%MW2060处用来指定读操作有正确返回数据时,才将相应数据保存起来;%MW481=1的处理是为防止与前一子站通讯的数据影响与下一子站的通讯数据,同时配合%MW2060处的指令,使下一次与同一站点的通讯能地在有正确的返回数据后才被保存。下面为本程序的指令表,将之复制到一文本文件中,导入到Twidosoft新建的程序中,并将通讯端口配置好就可以正常使用。在我编写的一个由TwidoPLC实现将22台TeSysU马达控制器的状态及电流等数据上传到上一级网络的程序中,使用本文介绍的方法,可以使程序减小到用常规方式编写的程序的二十分之一,并使程序中使用的MW字的使用量减少为可以使程序减小到用常规方式编写的程序的二十分之一,同时使程序长度大为减小,增加程序的可读性,加快PLC的扫描周期,效果非常明显。(*初始化,0.1秒及1秒脉冲产生*)LD1MPSAND(%S0OR%S1OR%S13)ST%S0ST%M0MRDAND%S4ST%M1MRDAND%S5ST%M2MPPAND%S6ST%M3(*单条通讯时间长短控制*)BLK%C0LD[%C0.V=%C0.P]RLD%M2CUEND_BLK(*控制与PLC通讯的子站点及通讯类型的调度*)BLK%C1LD[%C1.V=%C1.P]OR%M0RLD%C0.DCUEND_BLK(*3台VSD的读的地址及读命令*)LD1[%MW401:=16#0103][%MW402:=16#0203][%MW403:=16#0303](*3台VSD的写的地址及写命令*)LD1[%MW431:=16#0110][%MW432:=16#0210][%MW433:=16#0310](*为便于简化说明本程序,三台VSD的LFR设为一样,并只通过通讯控制VSD的LFR并不控制其运行停止*)(*如需要控制VSD的运行,同样通过间接寻址将控制值通过C0.V的值分别送入*)LD1[%MW236:=300](*根据通讯分配计数器C0的值判定对通讯端口发送读数据区或写数据区*)LD1[%MW481:=%C1.VREM2][%MW482:=%C1.V/2](*根据通讯分配计数器C0的值对通讯端口发送读数据区或写数据区写入地址信息*)(*MW481会决定发送读还是写指令,所以在任意时间MW202与MW232的内容一样没有影响*)LD1MPSAND[%MW481=0][%MW202:=%MW401[%MW482]]MPPAND[%MW481=1][%MW232:=%MW431[%MW482]](*读不同站号的VSD的ETA及RFR数据区相同部分*)LD1[%MW200:=16#0106][%MW201:=16#0300][%MW203:=16#0C81][%MW204:=2](*写不同站号的VSD的LFR数据区相同部分*)LD1[%MW230:=16#010A][%MW231:=16#0007][%MW233:=16#2136][%MW234:=16#0001][%MW235:=16#0002](*通讯,STAT作通讯状态指示,可去掉*)LD1MPSAND%MSG2.DMPSAND[%MW481=0][EXCH2%MW200:9]MPPAND[%MW481=1][EXCH2%MW230:10]MPPANDN%MSG2.DST%S69(*得到3台VSD的ETA及RFR*)LD1MPSAND[%MW481=0]AND[%MW2060][%MW301[%MW482]:=%MW207][%MW331[%MW482]:=%MW208]MPPAND[%MW481=1][%MW205:4:=0]