简介:本资源是一套完整可用的西门子S7-300 PLC实现MODBUS TCP通信的工程源代码,面向工业自动化领域的新手工程师及有一定PLC编程经验的开发人员,解决现场设备与上位系统(如SCADA、HMI或第三方控制器)基于标准以太网协议进行数据交互的实际需求。压缩包共699个文件,总大小2.17MB,包含218个DBF数据库文件(存储寄存器映射与配置参数)、141个MDX索引文件(支持快速数据检索)、73个PG程序块(含FB/FC功能块与OB组织块)、65个DBT文本描述文件(辅助理解变量含义),以及PDF说明文档、S7项目配置文件(S7P/S7L)和日志锁文件等,结构完整、模块清晰,可直接导入STEP 7环境编译运行。已有1023人学习下载,配套元数据文件(如S7hCompressedMetadata.cmf、VerbHomo.dat等)表明其具备完整的通信协议栈配置与变量语义定义,便于快速掌握MODBUS TCP主从站建模、地址映射规则与异常处理逻辑。 从收到这个需求开始,我就在想,西门子S7-300这套老古董级但又无处不在的PLC,到底是怎么和MODBUS TCP这种“现代”协议握手成功的。你别说,这几年调过的项目里,十次有八次是让S7-300去和ABB机器人、上位机、变频器或者第三方的数据采集系统做MODBUS TCP通讯。很多刚入门的工程师一听到“MODBUS TCP”就以为要在PLC里写一堆Socket程序,其实完全不是这么回事。西门子在STEP7的标准库里早就提供了现成的功能块,你要做的就是配置好IP、填对参数、处理数据一致性,剩下的事库函数都帮你干了。这篇博文就把我这几年用S7-300做MODBUS TCP通讯的完整思路、源代码片段、调试踩坑经验全拿出来聊聊,适合正在做PLC联网、机器人联调、上位机数据采集的工程师参考。
1. 项目需求梳理与方案选型
1.1 S7-300的以太网通讯能力盘点
先明确一个概念:不是所有S7-300都自带以太网口。老一批CPU比如CPU 314、315-2DP,本体上只有一个MPI口和一个DP口,想走MODBUS TCP,必须先加一块CP343-1或者CP343-1 Lean这样的通讯处理器模块。而带PN后缀的CPU,比如CPU 315-2PN/DP,本体集成了一个PROFINET接口,这个接口除了走PROFINET,也能拿来跑开放式以太网通讯,也就是TCP或UDP,MODBUS TCP自然也能跑。
模块选型上,CP343-1和CPU集成PN口在功能上都能实现MODBUS TCP,但有几点差别:
- CP343-1是独立通讯模块,带自己的处理器,CPU负载压力小,而且接口数量多,有些型号还支持两个以太网口。
- CPU集成PN口不用额外硬件,但所有通讯任务都走CPU的通讯堆栈,点位多、轮询快的时候会占用扫描周期。
- CP343-1 Lean是精简版,价格低,但某些库函数版本不支持,我记得早期固件对MODBUS TCP库的支持不太友好,选型时要注意固件版本。
如果你的项目是改造老设备,CPU还是老型号,那老老实实加CP模块。如果是新项目,我一般会直接建议用带PN口的CPU,成本低、接线少、调试方便。
1.2 为什么选择MODBUS TCP而不是Profinet或MODBUS RTU
这个问题几乎每个客户都问过。Profinet是西门子自家的工业以太网协议,性能确实强,但问题是它对端设备必须也支持Profinet。ABB机器人支持Profinet吗?支持,但通常需要额外买通讯选项或者加网关,价格不便宜,配置也麻烦。而MODBUS TCP是一个公开的标准协议,几乎所有工业设备、上位机组态软件、机器人控制器都会原生支持,不需要额外授权,不需要专用硬件网关。
MODBUS RTU和MODBUS TCP的区别,简单说就是:RTU走串口(RS232/RS485),TCP走网口;RTU有CRC校验,TCP把校验交给了TCP/IP协议栈,取而代之的是在报文前面加了一个MBAP头。TCP的传输速率和距离都甩RTU几条街,而且可以直接复用企业以太网网络,维护方便。
我做过的项目里,有一种比较典型的场景:S7-300 PLC控制一条产线,产线上有ABB机器人、扫码枪、还有一台老式的三菱变频器。机器人需要从PLC读取当前工件的加工参数,变频器则需要根据PLC的指令启动和调节频率。机器人这边用MODBUS TCP是最省事的,变频器那边如果只有RS485口,那就不能直接走TCP了,要不加一个串口服务器转成MODBUS TCP,要不就让PLC用自带的Modbus RTU协议去读取变频器频率。这部分后续我也会提到。
1.3 硬件与软件环境准备
我这次是以CPU 315-2PN/DP为例,搭配STEP7 V5.6软件环境。如果你的项目用的是TIA Portal也可以,但库的位置和调用方式有些区别,我会尽量讲通用的方法。
需要的硬件和软件清单:
- S7-300 PLC一台,建议CPU 315-2PN/DP及以上型号,固件版本最好在3.3以上。
- 以太网线、交换机。
- 装有STEP7 V5.5/V5.6的电脑,PG/PC接口选择TCP/IP。
- 调试工具:Modbus Poll(客户端调试用)、Modbus Slave(从站模拟用)、Wireshark(抓包分析)。
- 如果有ABB机器人,还需要RobotStudio和在机器人控制柜里配置MODBUS TCP通讯。
这里有个容易被忽略的点:S7-300的MODBUS TCP库函数,在STEP7的“库”里不是默认显示的。你需要把库文件从安装目录下复制出来,或者用西门子官方提供的“SIMATIC NET”库。老版本的STEP7可能还要求安装一个“MODBUS TCP”的附加包。如果调用库的时候发现找不到FB65,别慌,先在SIMATIC Manager里把库路径添加好。
2. MODBUS TCP协议核心要点与参数细节
2.1 报文结构:MBAP头 + PDU
虽然我们用的是西门子封装好的库函数,不用自己拼报文,但调试的时候如果不懂报文结构,出了问题真的很难查。MODBUS TCP的报文由两部分组成:MBAP头(7个字节) + PDU(功能码+数据)。
MBAP头的结构是固定的,我举个例子说明。假设我们要读取从站地址1,起始地址0,读取10个保持寄存器:
- 事务处理标识符(Transaction ID,2字节):用于区分同一时刻不同的请求。客户端每次发起请求时生成一个唯一的ID,服务端会在响应中原样返回。我们调试时经常看到设备报错,就是因为这个ID对不上。
- 协议标识符(Protocol ID,2字节):固定为0x0000,表示这是MODBUS协议。
- 长度(Length,2字节):从单元标识符开始到报文结束的字节数。比如上面读保持寄存器的请求,PDU有1字节功能码+2字节起始地址+2字节寄存器数量=5字节,再加上1字节单元标识符,Length=6。
- 单元标识符(Unit ID,1字节):即MODBUS从站地址。这里填1。
PDU部分则是传统MODBUS协议的内容,包括功能码和数据。对于读保持寄存器(功能码03),PDU就是:03 + 起始地址高字节 + 起始地址低字节 + 寄存器数量高字节 + 寄存器数量低字节。
所以完整请求报文就是:00 01 00 00 00 06 01 03 00 00 00 0A,其中00 01是事务ID,00 00是协议ID,00 06是长度,01是单元ID,03是功能码,00 00是起始地址,00 0A是10个寄存器。
2.2 常用功能码与数据地址映射
在S7-300里做从站或主站,你需要在PLC的数据区和MODBUS地址之间建立映射关系。我常用的功能码不多,但每个都要弄清楚:
- 01(读线圈):对应PLC的位地址,比如M0.0。
- 02(读离散输入):对应PLC的I点输入。
- 03(读保持寄存器):对应PLC的数据字,比如DB1.DBW0。
- 04(读输入寄存器):同样对应PLC的模拟量输入字。
- 05(写单个线圈):对PLC的M点或Q点写位。
- 06(写单个保持寄存器):对PLC的数据字写单个字。
- 0F(写多个线圈):批量写位。
- 10(写多个保持寄存器):批量写字。
S7-300的MODBUS库通常会提供一个“数据地址”的概念,你需要把MODBUS的寄存器地址映射到具体的PLC数据区地址。这个映射关系在库函数里有对应参数,比如MODE、DATA_ADDR、DATA_LEN、DATA_PTR等。实际项目中,我喜欢用DB块来存储通讯数据,因为DB块的数据结构清晰,而且寻址灵活。调试时,通过Modbus Poll去读DB里的数据,能直观看到每个字对应的物理量。
2.3 TCP连接机制与通讯周期
MODBUS TCP底层走的是TCP/IP协议,所以通讯建立的时候必然有三次握手。这个机制在PLC和上位机之间是透明的,但理解它有助于排查故障。
三次握手的过程可以说是经典:客户端先发送SYN包,服务端收到后回复SYN+ACK包,客户端再回复ACK包,到此连接建立成功。之后双方就可以在这个TCP连接上发送MODBUS TCP报文。S7-300的库函数在建立连接时,会占用CPU的一个TCP连接资源,如果你的PLC还要和其他设备建立多个TCP连接,要注意CPU的连接资源上限。
这里有个实操经验:S7-300作为MODBUS TCP服务器(从站)时,它默认监听502端口。上位机或者机器人作为客户端去连接它。连接建立后,不要频繁地断开重连,因为每次断开重连都要重新走三次握手,而且PLC内部要重新分配连接资源,频繁重连容易出现TIME_WAIT状态的连接堆积,导致后续连接失败。
轮询周期也要注意。我一般会把轮询间隔设置在100ms以上。如果你用1ms的循环去读PLC数据,就算PLC扛得住,上位机的CPU也会扛不住,而且对网络带宽的占用毫无意义。工业现场的数据实时性要求没那么变态,100ms足够绝大多数场景了。
2.4 为什么需要在PLC中实现主站和从站两种角色
很多人问,我PLC到底做主站还是从站?这取决于谁主动发起请求。如果上位机、机器人或者触摸屏是主动去读PLC的数据,那PLC就做从站(Server),用FB66。如果PLC需要主动去读取其他设备的数据,比如读一台仪表、读一台变频器,那PLC就做主站(Client),用FB65。
实际项目里,我的做法通常是:对外通讯时PLC宁可做从站,让上位机或者机器人来读。因为从站逻辑简单,PLC不需要维护连接状态,出错率低。但有一种场景必须让PLC做主站:比如PLC要去读变频器的运行频率,而变频器那边没有主动上报数据的机制,只能由PLC定时去查询。这种情况下,PLC就是MODBUS TCP客户端。
3. 程序源代码实现(核心实操)
3.1 获取官方库函数
在STEP7中,西门子提供了一个专门用于MODBUS TCP的库,里面包含FB65(主站功能块)和FB66(从站功能块)。如果你的软件里没有,可以在安装目录下找到“MODBUS TCP”库文件并加载。打开SIMATIC Manager,在左侧的库列表中右键添加,或者在项目里插入一个外部源文件。
库函数要在OB1中调用,或者放在循环中断OB中调用。调用前,你需要给FB65和FB66分别创建背景数据块。背景DB的数据块编号可以自由分配,但要注意不要和程序中已有的DB冲突。我用FB65的时候习惯用DB100,FB66用DB101,好记又好查。
3.2 调用FB65实现主站通讯(附源代码)
以FB65为例,它的管脚参数大致是这样的(不同软件版本可能有差异,以你手上的库为准):
- REQ:BOOL,上升沿触发一次读写操作。
- R:BOOL,复位错误信号。
- ADR:DWORD,远程设备的IP地址,以十六进制表示。比如192.168.0.10,在STL里要填写为
DW#16#C0A8000A。 - MB_ADR:BYTE,从站地址。
- MODE:BYTE,功能模式。通常0表示读保持寄存器,1表示写保持寄存器。
- DATA_ADDR:WORD,MODBUS数据地址,即要操作的寄存器起始地址。
- DATA_LEN:WORD,数据长度(字或位)。
- DATA_PTR:POINTER,指向PLC本地数据区的指针,用来存放读取或写入的数据。
- DONE:BOOL,一次操作完成标志。
- ERROR:BOOL,错误标志。
- STATUS:DWORD,错误状态码。
下面是一段STL调用示例:
CALL "MODB_MS", DB100 REQ := M0.0 // 触发信号 R := M0.1 // 复位信号 ADR := DW#16#C0A8000A // 192.168.0.10 MB_ADR := B#16#1 // 从站地址1 MODE := B#16#0 // 读保持寄存器 DATA_ADDR := W#16#0 // MODUS地址从0开始 DATA_LEN := W#16#A // 读取10个字 DATA_PTR := P#DB50.DBX0.0 // 数据放到DB50前10个字 DONE := M0.2 ERROR := M0.3 STATUS := MD10这里需要注意的是,DATA_PTR的指针写法。在STL中,P#DB50.DBX0.0表示指向DB50的数据块字节0开始的地址。如果你想把读到的数据放在一个全局数组里,建议先建一个DB50,里面定义一个长度为10的WORD数组,这样数据指针直接指过去,清晰明了。
M0.0触发后,FB65会去发起TCP连接,然后发送MODBUS请求。如果一切正常,DONE位会有一个扫描周期的脉冲,STATUS为0。如果错误,ERROR位为1,STATUS里会有错误代码。
3.3 调用FB66实现从站通讯(附源代码)
FB66是MODBUS TCP从站功能块。作为从站时,PLC不需要主动去连接谁,它只需要把某个数据区映射出来,等待远程客户端来读。
FB66的管脚类似,但要简单很多:
- EN_R:BOOL,从站功能使能。
- R:BOOL,复位。
- ADR:DWORD,本地PLC的IP地址?还是远程允许访问的IP?从站模式一般不需要指定远程IP,有的库版本里ADR是本地IP,有的版本里没有这个参数。我用的时候通常填本地IP,也就是PLC自己的IP地址。
- MB_ADR:BYTE,从站地址。
- MODE:BYTE,功能模式。
- DATA_ADDR:WORD,映射的起始MODBUS地址。
- DATA_LEN:WORD,映射的数据长度。
- DATA_PTR:POINTER,指向PLC数据区的指针。
- DONE、ERROR、STATUS:状态输出。
示例代码:
CALL "MODB_SLV", DB101 EN_R := M1.0 // 从站功能使能 R := M1.1 // 复位 ADR := DW#16#C0A80001 // 本机IP 192.168.0.1 MB_ADR := B#16#1 // 从站地址1 MODE := B#16#0 DATA_ADDR := W#16#0 // MODBUS地址从0开始 DATA_LEN := W#16#A // 映射10个字 DATA_PTR := P#DB60.DBX0.0 // 数据区在DB60 DONE := M1.2 ERROR := M1.3 STATUS := MD20从站模式需要特别注意的是:数据区一定要保持“长期有效”,也就是说被你映射的DB60,里面的数据在程序里可以被其他逻辑正常读写,但不要在某个扫描周期里被临时清零。另外,从站功能块使能后,PLC会持续监听502端口,如果你发现客户端连不上,第一步就去查防火墙和端口是否被占用。
3.4 数据一致性处理与组织方式
在实际项目中,我从来不会直接让外部设备读取PLC内部所有的DB。因为如果通讯数据和设备控制逻辑混在一起,很容易因为数据覆盖导致设备误动作。
我的习惯是:建立独立的通讯数据DB,里面只放需要对外交换的数据。比如建立DB50,里面定义:
- DBW0:当前设备状态字。
- DBW2:当前产品数量。
- DBW4:目标速度。
- DBW6:实际速度。
- DBW8~DBW18:预留参数区。
外部设备只能读写这个通讯DB,PLC内部的核心控制逻辑用的数据,要么放到别的DB,要么在通讯DB里做好“镜像”。比如PLC内部有一组速度设定值存在DB10里,外部需要修改这个速度时,通讯层先把值写到DB50的DBW4,然后PLC程序里做一步赋值:L DB50.DBW4, T DB10.DBW20。这样即使通讯数据被外部写乱,你也能通过源DB和镜像DB的对比迅速定位问题。
这种“通讯镜像区”的做法,是我在所有MODBUS TCP项目里都会坚持的。看起来多写了几行赋值代码,但现场联调的时候能省下大量排查故障的时间。
3.5 一个完整的循环OB示例
如果你需要PLC定时去读变频器数据,可以在OB35(循环中断,默认100ms)里调用FB65,这样每隔100ms自动发起一次读操作。
CALL "MODB_MS", DB100 REQ := M0.0 R := M0.1 ADR := DW#16#C0A80014 // 变频器1的IP 192.168.0.20 MB_ADR := B#16#1 MODE := B#16#0 DATA_ADDR := W#16#10 // 读取变频器运行频率地址 DATA_LEN := W#16#2 // 读2个字 DATA_PTR := P#DB51.DBX0.0 DONE := M0.2 ERROR := M0.3 STATUS := MD12但要注意,OB35的执行周期是固定的,如果通讯链路不稳定,FB65一直报错,OB35还是按周期调用,并不会自动重连。所以我会在OB1里加一段复位逻辑:检测到ERROR位上升沿后,给R信号一个脉冲,把错误状态清除,同时拉低REQ,等待下一个周期再次触发。这就是一个简单的自动重连机制。
4. 联调实操:从Step7到Modbus Poll再到ABB机器人
4.1 PLC的IP地址设置与组态下载
这是整个项目里最简单又最容易出问题的一步。在STEP7的硬件组态里双击CPU模块,打开以太网接口属性,设置IP地址、子网掩码,然后新建一个Ethernet子网,把PLC和上位机放在同一个网段里。
最好在PLC的“IP地址设置”里就固定IP,不要用DHCP。PLC一旦断电重启,IP如果变了,所有上位机、机器人全部断联,排查起来非常痛苦。我建议每个设备的IP都做成一张表贴在现场电柜门上,方便后期维护。
下载程序的时候,如果PG/PC接口设置错误,或者网线没插好,STEP7会提示“IP地址不可访问”。这种时候先ping一下PLC的IP,通了再下载。如果ping不通,检查网线、交换机、电脑防火墙。有时候电脑的防火墙会拦截STEP7的下载连接,建议把防火墙临时关掉试试。
4.2 用Modbus Poll测试通讯
Modbus Poll是调试MODBUS TCP客户端最方便的工具。它的界面很简单,左边是功能码和设置区,右边是数据表格。
配置步骤:
- 打开Modbus Poll,点击Connection -> Connect。
- 选择Modbus TCP/IP模式,填写PLC的IP地址,端口默认502。
- 设置从站地址为1,功能码选03(读保持寄存器),起始地址填0,数量填10。
- 点击OK,如果PLC的FB66已经使能,数据表格里会立刻显示从DB60读取到的值。
我调试的时候,喜欢在PLC的DB60里写入几个固定值,比如DBW0设为1234,DBW2设为5678,然后用Modbus Poll去读。如果读到的值和预设值一致,说明通讯成功。如果显示超时或者错误,就要从网络层开始排查。
Modbus Poll还有一个好处:它可以直接显示响应时间。正常情况下的响应时间应该在几毫秒到几十毫秒之间,如果响应时间突然飙到几百毫秒甚至超时,多半是PLC的通讯负载太高,或者网络里有广播风暴。
4.3 与ABB机器人MODBUS TCP通讯实例
很多自动化产线会把S7-300 PLC和ABB机器人做MODBUS TCP通讯。ABB机器人控制柜支持的Modbus TCP功能,通常是在RobotStudio的项目里配置通讯参数。
实际操作中,ABB机器人的程序里会用到类似于ModbusConnect、ModbusRead、ModbusWrite的指令。你需要做的,就是让机器人去连接PLC的IP地址,端口502,从站地址和PLC里设置的一致。
举个例子,机器人要从PLC读取一个“启动信号”和一个“工件类型号”,然后根据这些数据来决定走什么动作流程。机器人侧:
- 建立一个Modbus客户端连接,目标IP是192.168.0.1(PLC),端口502。
- 使用读保持寄存器功能,从地址0开始读若干个寄存器。
- 将读到的值赋给机器人内部的变量。
这里的坑在于:PLC的数据字和机器人的数据格式可能不一致。S7-300是高位在前(大端模式),而ABB机器人默认也是大端,一般情况下没问题。但如果你用的是C#上位机或者LabVIEW,可能会有字节序的问题,后面我会详细讲。
4.4 用Wireshark抓包验证三次握手与报文
如果你的通讯出了问题,但又不知道是PLC的问题还是客户端的问题,用Wireshark抓包是最直接的。
在电脑上开启Wireshark,选择连接PLC的网卡,然后过滤条件输入tcp.port == 502,你就能看到所有和PLC的MODBUS TCP通讯。
正常的流程是:客户端发起SYN包,PLC回复SYN+ACK,客户端回复ACK,然后开始发送MODBUS请求。如果只看到SYN包没有后续,说明PLC没有监听502端口,或者有防火墙拦截。如果三次握手成功但请求没有响应,多半是PLC的从站功能块没有使能,或者单元ID、功能码设置不对。
抓包的时候,重点看MODBUS报文的Length字段和事务ID。有时候上位机发过来的请求Length算错了,PLC会直接丢弃这个报文,这就是为什么很多通讯故障的根因在上位机而不在PLC。
4.5 与三菱PLC或变频器通讯的对比
提到三菱PLC读取写入变频器频率,这其实也是MODBUS协议的一个典型应用。三菱的FX2N或者FX3U PLC,配上FX3U-485ADP-MB模块,就可以走MODBUS RTU去控制变频器频率。但和西门子S7-300走MODBUS TCP相比,两者有一个本质区别:三菱PLC做主站时,它本身是“主动”去查询变频器,而西门子S7-300做从站时,是等待外部客户端来读取。对于整个控制系统来说,通讯的角色决定了你排查问题的方向。
如果你在现场同时遇到西门子PLC和台达/三菱/施耐德变频器,要注意这些变频器的MODBUS寄存器定义各不相同。比如有些变频器的运行频率地址是40001,有些是40010,必须看对应的通讯手册,不能想当然套用。
5. 常见问题与排查技巧(避坑速查)
5.1 连接建立不上的7个原因
我用表格总结几条最常踩的坑:
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 客户端提示TCP连接超时 | PLC没监听502端口 | 确认FB66已调用并且EN_R为TRUE |
| 能ping通但MODBUS请求无响应 | FB66的单元ID和客户端不一致 | 确认MB_ADR参数两边一致 |
| 第一次通讯成功,重连后失败 | TCP连接没有完全关闭 | 检查TIME_WAIT状态,等待几秒再连 |
| 数据读到全0 | 数据地址映射错误 | 检查DATA_ADDR、DATA_LEN和DATA_PTR |
| 通讯偶尔超时 | 网络中有广播风暴或负载过高 | 检查交换机,划分VLAN,降低轮询频率 |
| 下载配置后PLC一直报SF灯 | 库函数调用不正确或DB冲突 | 查看诊断缓冲区,检查背景DB编号 |
| 电脑无法访问PLC | 电脑IP和PLC不在同一网段 | 修改电脑IP,或者检查子网掩码 |
5.2 STATUS错误码怎么看
FB65和FB66的STATUS输出是一个DWORD,正常情况下为0。出错时它里面会有一个错误代码,很多新手一看到错误码就懵了。我告诉你一个笨但有效的方法:打开STEP7的在线帮助,在“错误消息”章节里搜索对应的错误码,或者直接用十六进制在西门子支持网站搜。
常见的错误码比如16#8200左右大概率是TCP连接建立失败,16#80A1之类可能是参数错误。但不同库版本错误码含义有差异,最好以你安装版本的对应用户手册为准。我在电脑上会存一份常用的西门子MODBUS TCP错误码表,现场排查时直接CTRL+F搜,比翻书快得多。
5.3 数据错位和字序问题
S7-300的数据存储是大端模式,MODBUS协议本身也规定高位在前,所以西门子PLC直接和标准MODBUS TCP通讯,一般不会出现字节序问题。但如果你用的是C#或者LabVIEW,这些语言默认的小端模式可能和PLC不一样。
比如你在PLC的DB60.DBW0里存了一个十六进制值0x1234,在Modbus Poll里面看到的是0x1234,这没问题。但如果你用C#的BitConverter去解析字节流,可能读出来的是0x3412。解决办法就是自己做字节交换,或者在上位机解析时注意字节序。
5.4 通讯卡死掉线后的恢复策略
PLC程序跑着跑着,通讯突然断了,最烦人。我遇到过的掉线原因有两个:一个是上位机异常退出,TCP连接没有正常关闭,PLC侧还认为连接在,这时新的连接无法建立。另一个是PLC侧的超时设置太短,响应稍微慢一点就主动断开了。
我的恢复策略有三招:
- 在PLC程序里做看门狗,如果通讯超过N秒没有数据更新,就触发一个报警输出。
- 如果通讯中断,不要只靠在FB65/66上反复触发,最有效的方法是给通讯功能块断电重启。具体做法是把EN_R或者REQ拉低,等待几个扫描周期,再重新置位。
- 如果条件允许,在PLC里做一个定时重连逻辑,比如每5秒自动触发一次连接请求,直到连上为止。
5.5 调试工具选择心得
Modbus Poll和Modbus Slave我几乎每天都用。但要注意,这两个工具都是商业软件,有试用期,不是免费的。对于那些想快速测试通讯的工程师,也可以用一些开源工具,比如ModbusPal,或者用Python写一个简单的MODBUS TCP客户端,都能达到目的。
Wireshark则是排查网络层问题的利器。说实话,有些通讯问题你盯着梯形图看半天也看不出毛病,抓包一看就知道是报文格式错了、长度不对、还是时序问题。
抓包时我会习惯性地把“显示过滤器”里加上stp看看有没有生成树协议的报文,用来判断交换机上是否形成了环路。曾经有个项目就是现场有人乱插网线搞出了网络风暴,导致PLC通讯非常不稳定。
5.6 一个具体案例:C#上位机读取S7-300数据
既然热词里有人关注C#对西门子PLC数据采集,我多说一句。如果你用C#写上位机去读S7-300的数据,有两种方式:一种是直接走S7协议,使用Snap7库,速度快;另一种是让PLC作为MODBUS TCP从站,C#通过MODBUS TCP协议去读。
用MODBUS TCP的好处是通用性强,不用装任何西门子专用库,但坏处是需要在PLC里专门做一个数据映射区。而Snap7则是直接访问PLC的DB块、M区、I/Q区,数据不需要额外映射,编程更灵活。两者我都用过,如果只是简单采集几十个点位,MODBUS TCP完全够用;如果点位很多、数据量很大、刷新要求高,Snap7会更适合。
6. 写在最后的几个操作体会
做S7-300和MODBUS TCP通讯,技术上并不复杂,真正让你头疼的往往是一些底层细节。我自己的体会是,一定要先把数据区规划好,再动手写逻辑。每次联调之前,我都会画一张简单的地址映射表,明确哪些寄存器是哪台设备在读写,然后拿着这个表去和机器人工程师、上位机工程师对配置。没有这样一张表,十有八九会在现场为了一个地址错位扯皮半天。
还有一个我强烈推荐的小技巧:所有参与MODBUS通讯的PLC,我都习惯在程序启动时先往通讯数据区写入一套完整的默认值,然后再使能从站功能块。这样做的目的是,即使外部客户端在PLC还没准备好之前就发来读请求,它读到的也是有效数据,而不是乱七八糟的随机值。别小看这一点,很多上位机在启动阶段就因为读到了错误数据而报故障,等PLC初始化完成之后又恢复正常,排查起来特别费劲。把默认值先写好,能省掉一大半这种“幽灵故障”。
最后再说一个通讯周期和看门狗的经验:别太迷信100ms轮询能实时监测所有数据。网络哪怕偶尔抖动一下,你轮询得再快也白搭。更稳的做法是让客户端用“订阅+主动上报”的思路,但MODBUS TCP本身不支持主动上报,所以要靠PLC程序在数据变化时置一个变化标志位,客户端通过轮询读这个标志位,从而知道数据是否更新。这个做法在S7-300里只需要一个M点加几条比较指令就能实现,但用了之后,整个通讯系统的稳定性和可读性都会上一个台阶。
本文还有配套的精品资源,点击获取