AN2002


CREATE: 2009/02/15
UPDATE: 2010/07/05
Version 1.1

   AN2002 通讯系统的应用

概述

GUTTA系统的通讯是以MODBUS通讯协议为基础来实现的。PLC程序的上载、下载、监控以及单步调试都是通过扩展MODBUS 13号通讯指令来实现的。使用13号通讯指令时,PLC必须为从站,GUTTA编程软件必须为主站。若PLC站地址未知,且只连接了一台PLC,GUTTA编程软件可以使用广播地址0来与PLC通讯。一般情况下,MODBUS从站不能响应广播地址0,而这里,PLC对广播地址的13号通讯指令依然进行应答。MODBUS 13号通讯指令数据内容可参考《UM4001 GUTTA通讯协议》。

在没有特殊配置的情况下,PLC总是作为MODBUS从站。因此只要连接没有问题,通讯设置没有问题,并且知道从站站号(或者使用广播地址0)。PLC总是可以被GUTTA编程软件找到。在某些PLC程序的配置下,将PLC的某个通讯口配置为主站,可能会导致通讯口无法与GUTTA编程软件通讯。若将PLC的所有通讯口都配置为主站,就会导致PLC程序无法被修改和更新。这个时候可以采用PLC的FLASH编程工具GUTTA Flash Utility来清除PLC程序。PLC程序被清除后,所有通讯口被恢复成默认配置。之后GUTTA编程软件就能轻松的找到PLC了。

GUTTA PLC不需要进行特殊编程,总是默认为从站。若需要PLC做为主站主动发起通讯,则需要通过PLC指令来触发,这就需要编写一段PLC程序。如何编写这段PLC程序,正是下面需要介绍的。

MODBUS通讯协议

MODBUS协议定义了总线上主站(Master)与从站(Slave)之间的通讯格式。MODBUS协议有ASCII和RTU两种格式,两种格式的通讯字段含义是相同的,差别在于字段的传输方法不同、帧开始与帧结束的判断条件不同、数据校验的方法不同。目前GUTTA PLC支持RTU通讯格式(虽然在EC20/EC30系统块中保留了7位ASCII模式的配置,但并未实现)。

MODBUS通讯帧

在RTU通讯模式下,主站需要通过时间来判断RTU帧的开始和结束。主站监视总线上的通讯数据,如果发现总线有超过4.5个字符时间(时间绝对值随字符传送波特率的变化而变化)的空闲,则认为一个帧已经结束(如果存在),同时认为下一个字符为下一帧的开始。在EC20/EC30的系统功能块中,可以手动设置这个时间,使其适应某些特殊的通讯场合。

完整的RTU帧应该是下面的格式(不论是主站发起还是从站应答):

空闲 地址 功能码 数据 CRC校验 空闲时间
  1字节 1字节 N字节 2字节  
地址

MODBUS协议的站地址由一个字节组成,站地址用来指示哪个从设备来应答主站的通讯报文。在总线上,每个从设备必须指定一个唯一的站地址,只有当通讯报文中地址与该从设备地址相同时,该从设备才能应答主站的通讯报文。从设备应答的通讯报文也必须包含该地址,以告知主站,这个通讯报文是哪个从设备应答的。广播报文的地址是零,所有的从站可以根据广播报文进行相应的动作,但是一般不能应答该广播报文。

功能码

功能码指示从设备应该执行什么动作。若应答的功能码最高位被置位,则表示从设备不能够正确执行此功能码。若一致,则表示从设备能够正确执行此功能,并能够返回功能码所需要的数据(如果有)。做为从站,目前GUTTA PLC只实现了部分常用的MODBUS通讯指令:

MODBUS通讯地址和GUTTA PLC地址的对应关系,请参考文档《IN1001 GUTTA内存使用》。新版的GUTTA Ladder Editor自带了MODBUS地址查询工具。

使用MODBUS地址查询工具,需要选择一个数据宽度,然后在右边输入一个符合该数据宽度的PLC变量,该变量实际占用的内存在下方以绿色的叉叉来标示。例如在上面输入变量MB31后,下方对应的图示含义为,MB31占用了8个位,位于MODBUS变量400024的高8位。

在MODBUS通讯协议中,除了位(Coil),协议认为所有的寄存器都是16位的字(Register),这可能与早期MODICON的PLC都采用16位处理器以及PLC指令相关。EC20/EC30系统中,PLC的变量宽度可以是8位、16位、32位;而且不要求对齐。例如MW0、MW1、MD2、MD3都是合法的PLC变量。这将导致这些PLC数据不能够简单的被MODBUS主站访问。在这里建议尽量使用长度对齐的变量地址,例如MW变量的偏移尽量为0、2、4、6等。MD变量的偏移尽量为0、4、8、12等。这样做一方面可以简化连接人机设备(触摸屏)的难度,另一方面,可以优化某些PLC类型的程序执行速度(Cortex-M3内核在访问长度对齐数据要快于非对齐数据)。

数据区

数据是主站需要发送给从站的数据,或者是从站需要返回给主站的数据。数据的具体含义由功能码来定义。特别的,有些功能码不包含数据区,数据区大小N可以为0。

校验码

校验码让接收数据方来检查通讯的传输过程中是否有错误发生。有时因为干扰使得数据传输过程中发生了错误,而校验码使得数据接收方能够判断这种错误并忽略此通讯报文。校验码极大的增加了MODBUS系统的安全性。具体方式是,发送方根据所发送的数据,采用特定的算法生成一个校验码,并将校验码放在发送数据的后面一起发送。接收方接收到通讯报文后,根据前面的数据部分,采用同样的算法也生成一个校验码,然后比较自己生成的校验码和通讯报文中的校验码是否一致,若一致,则表示通讯报文是有效的。在通讯过程中,不论是数据发生了传输错误还是校验码发生了传输错误,都会导致检验不一致。当然,数据和校验码同时发生了传输错误且刚好校验一致的可能性是有的,概率却微乎其微。

在MODBUS中,RTU模式必须采用CRC16校验码。在单片机中实现一般有两种方法,查表法或者运算法,有兴趣的读者可以从网上找到实现的源代码。出于速度的考虑,EC20/EC30绝大部分系统采用查表方式运算CRC16。

MODBUS功能码

01 读保持线圈状态(Read coil status)

描述

本指令读取从设备的离散量输出(0X)状态(ON或者OFF)。不支持广播。

发送

发送数据必须包含需要读取线圈的起始地址和线圈个数。注意线圈地址是从0开始的。线圈1-16在这里用0-15来寻址。

下面是一个从17号从设备读取线圈00020-00056的例子:

发送
段名 数据(16进制格式)
从站地址16#11
功能码16#01
开始地址(高字节)16#00
开始地址(低字节)16#13
数量(高字节)16#00
数量(低字节)16#25
校验码(LRC或者CRC)--

应答

线圈的状态通过数据中的位来传送。数据位1表示线圈为ON,数据位0表示线圈为OFF。第一个数据字节的小端位(LSB)为第一个需要查询的线圈状态,其余的线圈状态紧跟其后。若线圈个数不是8的倍数,多余的位需要被0填充,即最后一个数据字节的大端位(MSB)。同时,报文中包含字节数,用来指示一共有多少个数据字节需要被传送。

下面是一个应答的例子:

应答
段名 数据(16进制格式)
从站地址16#11
功能码16#01
字节数16#05
数据(线圈00027-00020)16#CD
数据(线圈00035-00028)16#6B
数据(线圈00043-00036)16#B2
数据(线圈00051-00044)16#0E
数据(线圈00056-00052)16#1B
校验码(LRC或者CRC)--
02 读输入线圈状态(Read input status)

描述

本指令读取从设备的离散量输入(1X)状态(ON或OFF)。不支持广播。

发送

发送数据必须包含需要读取线圈的起始地址和线圈个数。注意线圈地址是从0开始的。线圈1-16在这里用0-15来寻址。

下面是一个从17号从设备读取线圈10197-10218的例子:

发送
段名 数据(16进制格式)
从站地址16#11
功能码16#02
开始地址(高字节)16#00
开始地址(低字节)16#C4
数量(高字节)16#00
数量(低字节)16#16
校验码(LRC或者CRC)--

应答

线圈的状态通过数据中的位来传送。数据位1表示线圈为ON,数据位0表示线圈为OFF。第一个数据字节的小端位(LSB)为第一个需要查询的线圈状态,其余的线圈状态紧跟其后。若线圈个数不是8的倍数,多余的位需要被0填充,即最后一个数据字节的大端位(MSB)。同时,报文中包含字节数,用来指示一共有多少个数据字节需要被传送。

下面是一个应答的例子:

应答
段名 数据(16进制格式)
从站地址16#11
功能码16#02
字节数16#03
数据(线圈10204-10197)16#AC
数据(线圈10212-10205)16#DB
数据(线圈10218-10213)16#35
校验码(LRC或者CRC)--
03 读保持寄存器(Read holding register)

描述

本指令读取从设备保持寄存器(4X)的二进制值。不支持广播。

发送

发送数据必须包含需要读取寄存器的起始地址和寄存器个数。注意寄存器地址是从0开始的。寄存器1-16在这里用0-15来寻址。

下面是一个从17号从设备读取寄存器40108-40110的例子:

发送
段名 数据(16进制格式)
从站地址16#11
功能码16#03
开始地址(高字节)16#00
开始地址(低字节)16#6B
数量(高字节)16#00
数量(低字节)16#03
校验码(LRC或者CRC)--

应答

寄存器的二进制值通过两个字节来传送。对于每个寄存器:第1个字节为寄存器的高位字节(MSB),第2个字节为寄存器的低位字节(LSB)。

下面是一个应答的例子:

应答
段名 数据(16进制格式)
从站地址16#11
功能码16#03
字节数16#06
高位字节(寄存器40108高字节)16#02
低位字节(寄存器40108低字节)16#2B
高位字节(寄存器40109高字节)16#00
低位字节(寄存器40109低字节)16#00
高位字节(寄存器40110高字节)16#00
低位字节(寄存器40110低字节)16#64
校验码(LRC或者CRC)--

在这个应答中:寄存器40108的值为555(16#022B)、寄存器40109的值为0(16#0)、寄存器40110的值为100(16#0064)。

04 读输入寄存器(Read input register)

描述

本指令读取从设备输入寄存器(3X)的二进制值。不支持广播。

发送

发送数据必须包含需要读取寄存器的起始地址和寄存器个数。注意寄存器地址是从0开始的。寄存器1-16在这里用0-15来寻址。

下面是一个从17号从设备读取寄存器30009的例子:

发送
段名 数据(16进制格式)
从站地址16#11
功能码16#04
开始地址(高字节)16#00
开始地址(低字节)16#08
数量(高字节)16#00
数量(低字节)16#01
校验码(LRC或者CRC)--

应答

寄存器的二进制值通过两个字节来传送。对于每个寄存器:第1个字节为寄存器的高位字节,第2个字节为寄存器的低位字节。

下面是一个应答的例子:

应答
段名 数据(16进制格式)
从站地址16#11
功能码16#04
字节数16#02
高位字节(寄存器30009高字节)16#00
低位字节(寄存器30009低字节)16#0A
校验码(LRC或者CRC)--

在这个应答中:寄存器30009的值为10(16#000A)。

05 写单个线圈(Force single coil)

描述

本指令将从设备的某个保持线圈(0X)状态设置为ON或者OFF。在广播时,与总线相连的所有从设备相同地址上的线圈状态被设置。

发送

发送数据必须包含需要设置线圈的地址。注意线圈地址是从0开始的。线圈1在这里用0来寻址。线圈需要被设置的状态在数据中:数据16#FF00表示需要将线圈设置为ON;数据16#0000表示需要将线圈设置为OFF。其余的值将被忽略。

下面是一个把17号从设备线圈00173设置为ON的例子:

发送
段名 数据(16进制格式)
从站地址16#11
功能码>16#05
开始地址(高字节)>16#00
开始地址(低字节)>16#AC
数据(高字节)>16#FF
数据(低字节)>16#00
校验码(LRC或者CRC)--

应答

若线圈设置成功,应答是发送数据的一份拷贝。

下面是一个应答的例子:

应答
段名 数据(16进制格式)
从站地址16#11
功能码16#05
开始地址(高字节)16#00
开始地址(低字节)16#AC
数据(高字节)16#FF
数据(低字节)16#00
校验码(LRC或者CRC)--
06 写单个寄存器(Preset single register)

描述

本指令将从设备的某个保持寄存器(4X)设置为指定值。在广播时,与总线相连的所有从设备相同地址上的寄存器值被设置。

发送

发送数据必须包含需要设置寄存器的地址。注意寄存器地址是从0开始的。寄存器1在这里用0来寻址。寄存器需要被设置的值在数据中。

下面是一个把17号从设备寄存器40002设置为16#0003的例子:

发送
段名 数据(16进制格式)
从站地址16#11
功能码16#06
开始地址(高字节)16#00
开始地址(低字节)16#01
数据(寄存器40002高字节)16#00
数据(寄存器40002低字节)16#03
校验码(LRC或者CRC)--

应答

若寄存器设置成功,应答是发送数据的一份拷贝。

下面是一个应答的例子:

应答
段名 数据(16进制格式)
从站地址16#11
功能码16#06
开始地址(高字节)16#00
开始地址(低字节)16#01
数据(寄存器40002高字节)16#00
数据(寄存器40002低字节)16#03
校验码(LRC或者CRC)--
15 写多个线圈(Force multiple coils)

描述

本指令将从设备的一段连续保持线圈(0X)状态设置为指定值。在广播时,与总线相连的所有从设备相同地址上的线圈状态被设置。

发送

发送数据必须包含需要设置线圈的地址。注意线圈地址是从0开始的。线圈1在这里用0来寻址。线圈需要被设置的状态在数据中:数据位1表示需要将线圈设置为ON;数据位0表示需要将线圈设置为OFF。

例如我们需要设置17号从设备从00020开始的连续10个线圈的值。数据区第1个被传送的字节(16#CD)表示线圈00020-00027的设置值。小端位表示低地址线圈00020,大端位表示高地址线圈00027。第2个被传送的字节(16#01)表示线圈00028-00029的设置值,小端位表示低地址线圈00028,大端不需要使用的位保留为0。

  MSB    第1个字节    LSB MSB    第2个字节    LSB
1 1 0 0 1 1 0 1   0 0 0 0 0 0 0 1
线圈 27 26 25 24 23 22 21 20   - - - - - - 29 28
发送
段名 数据(16进制格式)
从站地址16#11
功能码16#0F
开始地址(高字节)16#00
开始地址(低字节)16#13
数量(高字节)16#00
数量(低字节)16#0A
字节数16#02
数据(线圈00020-00027)16#CD
数据(线圈00028-00029)16#01
校验码(LRC或者CRC)--

若线圈设置成功,应答包括从站地址、功能码、开始地址、数量。

下面是一个应答的例子:

应答
段名 数据(16进制格式)
从站地址16#11
功能码16#0F
开始地址(高字节)16#00
开始地址(低字节)16#13
数量(高字节)16#00
数量(低字节)16#0A
校验码(LRC或者CRC)--
16 写多个寄存器(Preset multiple registers)

描述

本指令将从设备的一段连续保持寄存器(4X)设置为指定值。在广播时,与总线相连的所有从设备相同地址上的寄存器值被设置。

发送

发送数据必须包含需要设置寄存器的地址。注意寄存器地址是从0开始的。寄存器1在这里用0来寻址。寄存器需要被设置的值在数据中。

下面是一个把17号从设备寄存器40002、40003设置为16#000A、16#0102的例子:

发送
段名 数据(16进制格式)
从站地址16#11
功能码16#10
开始地址(高字节)16#00
开始地址(低字节)16#01
数量(高字节)16#00
数量(低字节)16#02
字节数16#04
数据(寄存器40002高字节)16#00
数据(寄存器40002低字节)16#0A
数据(寄存器40003高字节)16#01
数据(寄存器40003低字节)16#02
校验码(LRC或者CRC)--

应答

若寄存器设置成功,应答包括从站地址、功能码、开始地址、数量。

下面是一个应答的例子:

应答
段名 数据(16进制格式)
从站地址16#11
功能码16#10
开始地址(高字节)16#00
开始地址(低字节)16#01
数量(高字节)16#00
数量(低字节)16#02
校验码(LRC或者CRC)--

EXCH指令介绍

EC20/EC30系统默认支持MODBUS协议。由于PLC程序的上传和下载就是使用的MODBUS协议,故只要能上传或下载程序,那么就能进行MODBUS通讯。这里PLC总是做为从站,根据主站发出的MODBUS通讯请求,返回正确的应答。例如连接触摸屏和PLC,让触摸屏显示和修改PLC中变量,是不需要编写任何通讯程序的。只要正确的设置了PLC的通讯参数,并在触摸屏中正确的配置变量的MODBUS地址即可。

如果需要PLC作为主站主动去访问其他PLC从站,则需要使用EXCH指令。

串口数据交换(EXCH)指令根据输入TBL中给出的通讯参数表格进行一次串口通讯操作,输入PORT标识通讯串口号。您可以在程序中保持任意数目的EXCH指令,但在任何时间最多只能有8条EXCH被放入通讯队列。若通讯队列满,EXCH指令将不进行任何操作(亦不设置错误号)。EXCH指令的使能位输入能流可以是脉冲,EXCH指令将尝试进行单次通讯。EXCH指令的使能位输入能流也可以一直保持,EXCH指令会检查自己是否在通讯队列中,若不在指令尝试添加自己到通讯队列中,否则等待通讯结果。

EXCH指令有两个操作数。

参数表格的具体格式:

偏移量含义格式类型说明
0.0A 队列BIT输出当通讯功能进入队列,A设置为1。当一个通讯功能移出队列(通讯完成或者出错),A设置为0。
0.1D 完成BIT输出当通讯功能进入队列,D设置为0。当一个通讯功能完成,D设置为1。
0.2E 错误BIT输出当通讯功能进入队列,E设置为0。当一个通讯功能错误,E设置为1。
1错误号BYTE输出当E被设置为1时,错误号同时被指定。
2发送数据长度UINT输入发送数据的长度,以字节记。
4发送数据偏移量UINT输入发送数据的首地址偏移量。发送数据必须在M区。
6接收数据长度UINT输入接收数据缓冲的长度,以字节记。
8接收数据偏移量UINT输入接收数据缓冲的首地址偏移量。接收数据必须在M区。

错误号可以是下面的值:

应用实例

使用ModScan32工具

ModScan32是Win-Tech推出的MODBUS通讯工具,这个软件可以通过电脑COM端口发送标准的MODBUS主站请求与从站进行通讯。使用ModScan32,需要至少一个MODBUS从站。可以使用GUTTA Simulator软件模拟器模拟一个MODBUS从站,只需要给GUTTA Simulator软件模拟器绑定一个计算机串口。这里我们使用PLC实验板:CPU-EC20 (8051)来完成这个实验。其它类型的试验板和模拟器的使用基本类似,不再重复介绍。

前面说过,使用EC20/EC30系统,PLC默认就是从站,无需编程,只需要配置通讯。运行GUTTA Ladder Editor软件,双击项目管理窗口的项目,选择PLC类型为CPU-EC20 (8051)。新建一个项目,双击系统块:

编辑系统块中的通讯端口:

由于CPU-EC20 (8051)只有1个串行通讯口,端口1可以忽略,我们只需要配置端口0。

点击确认,保存系统块配置,将这个空白的程序下载到PLC试验板CPU-EC20 (8051)。

启动ModScan32,单击Connection菜单下的Connect命令。

出现通讯端口配置对话框:

其余参数使用默认值,点击确认,保存设置。

在ModSca1窗口中,做如下修改:

按照上面的说明修改后,应该就能建立正常的MODBUS通讯了,ModSca1窗口下的红字** Device NOT CONNECTED! **应该会消失。按下或弹起试验板CPU-EC20 (8051)的I0.0开关,观察I0.0对应的MODBUS变量10001是否有变化。

工具软件:ModScan32.zip


   http://www.plcol.com/technologies/anindex/an2002/ModScan32.zip


PLC程序:Sample1_Lad.vcw


   http://www.plcol.com/technologies/anindex/an2002/Sample1_Lad.vcw

使用触摸屏

由于EC20/EC30默认支持MODBUS从站协议,用触摸屏连接EC20/EC30系统,关键是配置好控件变量的MODBUS通讯地址。这里我们实现一个简单的例子,用触摸屏显示PLC的2个模拟量输入。改变PLC模拟量值,触摸屏上显示的值对应的发生变化。触摸屏上还有一个按键,用于决定当前显示哪个模拟量。例如即按下此按键,显示模拟量通道0;松开此按键,显示模拟量通道1。

为了大家试验方便,这里使用斯耐德的触摸屏开发软件Vijeo-Designer Version 4.10。这个软件包含一个非常优秀的模拟系统,可以在电脑上模拟触摸屏的很多功能。我们将使用Vijeo-Designer Version 4.10的模拟器,效果和使用实际的触摸屏一致。

配置MODBUS从站:PLC

基于最开始提出的要求,我们先给出CPU-EC20 (8051)的PLC程序。和上一节类似,运行GUTTA Ladder Editor软件,双击项目管理窗口的项目,选择PLC类型为CPU-EC20 (8051)。新建一个项目,双击系统块,编辑系统块中的通讯端口:

由于CPU-EC20 (8051)只有1个串行通讯口,端口1可以忽略,我们只需要配置端口0。

点击确认,保存系统块配置,然后根据需要编写PLC程序:

MAIN(INT0)

这个程序很简单,判断当Q3.0为1时,将模拟量通道1(AIW2)的值拷贝给MW20;判断当Q3.1为1时,将模拟量通道0(AIW0)的值拷贝给MW20。

通过软件自带的MODBUS地址查询工具可以知道:

配置MODBUS主站:触摸屏

基于最开始提出的要求,我们只知道,触摸屏需要将400019(MW20)显示出来,同时通过按键控制000025(Q3.0)即可,PLC逻辑会根据Q3.0的值,决定400019(MW20)等于通道0(AIW0)还是通道1(AIW2)。

配置总线

在Vijeo-Designer Version 4.10中,新建一个项目ShowSwitch。要连接外部设备,首先要创建MODBUS总线,在Navigator中,右键单击IO Manager,弹出菜单。

单击New Device Interface…,创建总线,此时出现总线类型选择对话框。

默认配置就是Modbus (RTU),不用修改,直接点击OK按键保存配置。此时Navigator结构发生变化,IO Manager下面多出一些子节点。在Navigator中,右键单击ModbusRTU01,弹出菜单。

单击Configuration…,此时出现总线配置对话框。

根据上前面PLC的配置,做出相应的修改:

点击OK按键保存配置。在Navigator中,右键单击ModbusEquipment01,弹出菜单。

单击Configuration…,此时出现设备配置对话框。

由于我们的PLC子站地址是20,故这里填入20,点击OK按键保存配置。

配置变量

在Navigator的Variables页面中,添加两个变量:

配置页面

在项目的第一个显示页面1:Panel1中,插入两个控件,一个用于显示模拟量,一个用于切换两个通道:

按钮SwBitO_1的配置如下:

数字显示NumDisp_1的配置如下:

模拟测试

在Navigator中,右键单击Target1,弹出菜单。

单击Start Simulation (Build),运行触摸屏软件模拟器:

调整试验板CPU-EC20 (8051)上两个电位器,观察显示数字是否发生变化,从而确认数字对应的电位器是哪一个。按一次按键,调整试验板CPU-EC20 (8051)上两个电位器,观察显示数字是否发生变化,再一次确认字对应的电位器是哪一个,看看对应关系是否发生变化。

触摸屏软件:Vijeo-Designer Version 4.10 Trail


   http://www.plcol.com/technologies/anindex/an2002/VJDInstall.zip


PLC程序:Sample2_Lad.vcw


   http://www.plcol.com/technologies/anindex/an2002/Sample2_Lad.vcw


Vijeo-Designer Version 4.10项目:Sample2_ShowSwitch.zip


   http://www.plcol.com/technologies/anindex/an2002/Sample2_ShowSwitch.zip

使用EXCH指令连接另一台PLC

使用EXCH指令连接另一台PLC,意味着我们需要两台PLC。这里的实验我们用PLC模拟器将电脑模拟成一台PLC。这样对于只有一块PLC实验板的朋友也能进行多机通讯试验了。PLC实验板采用CPU-EC20 (8051)。实验板为主站,电脑模拟的PLC为从站。当I0.0按下时,主站尝试读取从站I0.4 ~ I0.7的值,并将从站I0.4 ~ I0.7的值拷贝到主站Q0.0 ~ Q0.3上以驱动LED灯。同时,主站尝试读取从站MW0、MW2、MW4的值,并将MW0的值显示在LED数码管上。电脑模拟器模拟的从站读取模拟量输入AIW0的值,并将这个值更新到MW0中去。

配置主站

系统块

数据块

DAT_0(DAT0)

根据MB20 ~ MB28我们知道:

根据MB20 ~ MB28我们知道:

DATA_1(DAT1)

根据MB50 ~ MB58我们知道:

根据MB20 ~ MB28我们知道:

程序块

MAIN(INT0)




子程序LED(SBR0)完成的功能就是将LW0的值显示到四位段码LED上。具体内容这里省略。

NETWORK 0 利用100ms定时器T4每200ms产生1个脉冲。在这个脉冲内,向通讯队列中加入2个通讯任务。这2个通讯任务都操作PORT0通讯口。第1个通讯任务的配置表格以MB20为首地址,第2个通讯任务的配置表格以MB50为首地址。

NETWORK 1 将通讯缓冲中得到的(从站)I0.4-I0.7输出到本站的Q0.0-Q0.3。

NETWORK 2 将通讯缓冲中得到的(从站)MW0输出到本站的LED数码管上。需要注意的是:MODBUS通讯中,高位字节(MSB)是先发送的,在缓冲中处于低地址,低位字节(LSB)是后发送的,在缓冲中处于高地址。而EC20/EC30的地址对起方式与之相反(小端对齐格式),需要用SWAP指令交换高低字节。

配置从站

由于PLC默认就是MODBUS从站,从站程序相对简单:

MAIN(INT0)


模拟测试

这里假设试验板CPU-EC20 (8051)通过USB和电脑相连,USB虚拟了一个串行通讯口Prolific USB-to-Serial Comm Port (COM6)。

AccessPort对COM6通讯口的数据监控,仅供参考:

SUDT ACCESSPORT LOG FILE - Monitor mode

Monitor: COM6
Create Time: 2010-07-04, 03:49:18
Computer Name: WANA-001
System version: Microsoft Windows XP Professional Service Pack 3 (Build 2600)

# Time Function Data ( Hex )

1 [00000000] IRP_MJ_CREATE Port Opened - GuttaSimulate.exe
2 [00000000] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 38400
3 [00000000] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: Even, DataBits: 8
4 [00000000] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 38400
5 [00000001] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: Even, DataBits: 8
6 [00000014] IOCTL_SERIAL_SET_BAUD_RATE Baud Rate: 38400
7 [00000014] IOCTL_SERIAL_SET_LINE_CONTROL StopBits: 1, Parity: Even, DataBits: 8
8 [00000245] IRP_MJ_READ Length: 0008, Data: 01 02 00 04 00 04 38 08
9 [00000248] IRP_MJ_WRITE Length: 0006, Data: 01 02 01 00 A1 88
10 [00000253] IRP_MJ_READ Length: 0008, Data: 01 03 00 08 00 04 C5 CB
11 [00000256] IRP_MJ_WRITE Length: 0013, Data: 01 03 08 0C 2B 00 00 00 00 00 00 0E 80
12 [00000261] IRP_MJ_READ Length: 0008, Data: 01 02 00 04 00 04 38 08
13 [00000264] IRP_MJ_WRITE Length: 0006, Data: 01 02 01 00 A1 88
14 [00000271] IRP_MJ_READ Length: 0008, Data: 01 02 00 04 00 04 38 08
15 [00000274] IRP_MJ_WRITE Length: 0006, Data: 01 02 01 00 A1 88
16 [00000279] IRP_MJ_READ Length: 0008, Data: 01 03 00 08 00 04 C5 CB
17 [00000282] IRP_MJ_WRITE Length: 0013, Data: 01 03 08 0C 2B 00 00 00 00 00 00 0E 80
18 [00000287] IRP_MJ_READ Length: 0008, Data: 01 02 00 04 00 04 38 08
19 [00000290] IRP_MJ_WRITE Length: 0006, Data: 01 02 01 00 A1 88

主站PLC程序:Sample3_Master_Lad.vcw


   http://www.plcol.com/technologies/anindex/an2002/Sample3_Master_Lad.vcw


从站PLC程序:Sample3_Slave_Lad.vcw


   http://www.plcol.com/technologies/anindex/an2002/Sample3_Slave_Lad.vcw