☰
S7-1200之间PUT/GET通信详解:组态、参数与避坑指南
2026/10/15 5:35:04 网站建设 项目流程

简介:西门子S7-1200 PUT&GET教程是一份面向PLC工程师与自动化初学者的实操性文档,聚焦S7-1200之间通过PUT/GET指令实现S7通信,覆盖同一项目内与跨项目两种典型场景。资料基于Step7 V13软件,详细讲解站点组态、S7连接建立、连接属性查看、数据块创建及非优化块访问设置,并给出PUT、GET指令的调用方法和参数含义,同时说明不同项目下需借助PROFINET IO协议及开放对方程序接口的注意事项。资源包为1个doc文件,大小4.28MB,正文含完整配置步骤、连接属性截图说明及程序示例,便于按图对照操作。该篇教程已被超过1.1万人浏览学习,尤其适合需要快速上手S7-1200站间数据交换、解决连接配置或编程疑问的读者,可帮助你从新建项目到在线监控完整走通两个CPU的数据读写流程。

1. 为什么S7-1200之间互传数据,我第一反应是PUT&GET

产线改造时遇到一个典型需求:两台西门子S7-1200分别控制前后两段设备,中间要交换产量、配方号、故障代码和几个设备状态字,算下来也就几十个字节。有人建议加分布式IO模块,有人建议走Modbus TCP自己做轮询,而我最先落笔的方案是基于S7协议的PUT&GET。原因很简单:S7-1200之间用PUT&GET不用改硬件、不用买网关,博途里把两个PLC连到同一个网络,组一条S7连接,然后在程序里调用两个通信块就能把远程DB里的数据读回来或者写过去。它适合数据量不大、实时性要求不苛刻的点对点通信;不适合当高速IO用,也不适合传大文件。这篇就把从组态到块参数,再到翻车记录的完整过程写出来。

2. 先选型再动手:PUT&GET和三种替代方案的边界在哪里

2.1 PUT&GET到底做了一件什么事

PUT&GET是S7协议里的两个服务原语,GET读远程数据,PUT写远程数据。底层走以太网,承载在ISO-on-TCP之上,说白了就是TCP 102端口上跑S7协议。主动方的S7-1200 CPU作为客户端发起连接请求,远程CPU作为服务器接受连接并响应读写操作。远程那台CPU不需要调用任何通信块,也不需要为这个连接专门编写应答程序,它只需要做两件事:允许来自远程对象的PUT&GET通信访问,以及数据区能被绝对寻址。

很多人第一次用这个功能时会被“连接”两个字绕晕,以为跟TCP客户端那样要在程序里指定IP和端口。S7-1200的常规做法是:在网络视图里把两台PLC的Profinet接口连到同一个子网,然后选中两个接口创建S7连接。这个连接组态里已经包含了本地ID、伙伴ID、伙伴的IP地址,程序里的PUT&GET块只需要引用这个本地ID即可,不需要在代码里拼IP。

我习惯把连接表里的本地ID改成16#0100这种一眼能认出来的值,程序里多个ID并存的时侯不会看花眼。远程CPU的地址信息由连接组态管理,改IP不用动程序,这一点比在代码里写死IP要好维护得多。

2.2 PUT&GET、Modbus TCP、PROFINET IO、TSEND_C怎么选

方式数据量典型场景实时性组态复杂度适用场景
PUT&GET几十字节到一两百字节一般,受PLC扫描周期影响低,网络视图一条连接+两个块S7 PLC之间的状态/参数交换
Modbus TCP按寄存器批量读写一般,需要自己管理功能码中,要处理字节序和寄存器映射与第三方设备、触摸屏、上位机通信
PROFINET IO周期性IO数据,几字节到几百字节高,等时同步级别可达毫秒级高,要组设备、分配IO地址分布式IO、驱动器、阀岛
TSEND_C/TRCV_C大块自定义数据取决于发送触发方式中,需要组连接描述DB与PC、MES系统交换JSON/报文

PUT&GET最大的优势是省事:不用像Modbus那样维护一张寄存器映射表,也不用自己拼请求帧。数据存在哪就指向哪,PLC工程师的思路直接搬到程序里。它最大的限制是只能在S7体系内用,跨到第三方PLC或者PC端就使不上劲。

实时性方面要泼盆冷水:PUT&GET的请求是在OB1或者中断OB里调用完成的,一次调用要等远程CPU响应,这个时间受两边扫描周期和网络延迟影响,通常能做到几十毫秒级,但别拿它当硬实时通道。如果要求几个毫秒内的确定性传输,老老实实上PROFINET IO。

2.3 哪些前提不满足,直接别用PUT&GET

首先要看被动侧S7-1200的固件版本。固件V4.0以后对PUT&GET的支持比较完整,可以直接在网络视图组态S7连接,老固件要么菜单位置完全不同,要么默认限制远程访问,踩起来非常难受。博途版本太老的话,“允许来自远程对象的PUT&GET通信访问”这个开关的位置也不一样,V15以后一般在CPU属性的“防护与安全—连接机制”里,V13/V14可能在“属性—常规”里找,先确认版本再动手。

其次要确认两侧IP确实通了。PUT&GET连不上有一半原因是IP没通,或者交换机端口做了隔离,把TCP 102端口挡掉了。先ping,再用TCP调试工具简单测一下目标端口通不通,能省下大量翻车的排查时间。

最后是数据量预期管理。PUT&GET一次调用支持多个数据区(通常四个,ADDR_1到ADDR_4),但总长度有限制,不适合传几百个字的连续数组。数据量大时要么拆成多轮请求,要么换TSEND_C走自定义TCP。我做项目时凡是要传超过几十个连续字的,直接放弃PUT&GET。

3. 最小可复现:一台S7-1200把另一台S7-1200的DB10读回来

3.1 建网络、组S7连接,让两台PLC互相认识

我以两台S7-1200为例做个完整的最小工程。第一台叫“客户端”,IP设192.168.0.1,它主动去读数据;第二台叫“服务器”,IP设192.168.0.2,它只是被动提供数据。两台PLC放到博途的同一个项目里,在“设备组态”里分别给它们添加Profinet接口并设置IP。

设置完IP后,切到“网络视图”,把两台PLC的Profinet接口用鼠标拖一条线连起来,表示它们在同一个子网里。然后选中这条连线,右键,选择“创建S7连接”。博途会生成一条连接记录,打开连接表能看到本地ID,我一般改成16#0100。伙伴地址会自动填上服务器的IP,不需要手写。

连接组态这一步很多人会漏,直接在程序里拖PUT&GET块然后填ID,编译能过但运行时报连接无法建立,因为ID对应的连接根本不存在。

// 连接表里生成的连接参数示意 // 本地ID: 16#0100 // 伙伴地址: 192.168.0.2 // 连接类型: S7连接 // 主动建立连接: 客户端侧为TRUE

这段不是代码,只是把连接表的逻辑写出来方便对照。连接组态里的“主动建立连接”选TRUE,表示由客户端PLC主动发起TCP连接。被动侧不需要建连接,不需要调用任何通信块。

3.2 服务器侧:勾选访问许可,创建能通信的DB

服务器PLC打开“设备组态”,点CPU,在属性里找到“防护与安全—连接机制”,勾选“允许来自远程对象的PUT/GET通信访问”。这个开关默认是不勾的,不勾的话客户端连上来会被直接拒绝,STATUS返回0x80C8。

接下来创建数据块DB10,用于存放被读取的数据。在项目树里双击“添加新块”,选DB,编号填10。这里关键一步:DB属性里有个“优化的块访问”选项,必须取消勾选,也就是创建成非优化DB。S7-1200默认新建的DB都是优化访问,优化DB只能用符号名访问,无法用ANY指针按偏移绝对寻址,而PUT&GET的ADDR指针依赖的是绝对地址。不取消这个勾选,后面ADDR填P#DB10.DBX0.0,程序直接报地址不可访问。

DB10里我放两个INT变量,名字随便起,比如Var_Int_0和Var_Int_1,让编译器给它们分配偏移DBW0和DBW2。加上一个REAL变量放在DBD4。编译下载,服务器侧就准备好了。

// DB10(非优化访问) // 偏移 类型 名称 // 0.0 INT Var_Int_0 // 2.0 INT Var_Int_1 // 4.0 REAL Var_Real

3.3 客户端侧:OB1里调用GET块

客户端PLC里,在项目树“指令—通信—S7通信”下找到GET块,拖到OB1里。GET块是FB,调用时会自动生成背景DB,名字默认类似GET_DB。把管脚按下面的方式接好。

// 在OB1中调用GET块,管脚赋值示意 "GET_DB"(REQ := "CommReq", // 上升沿触发一次读取 ID := 16#0100, // 与连接表里的本地ID一致 ADDR_1 := P#DB10.DBX0.0 INT 2, // 读远程DB10从DBW0开始的两个INT RD_1 := "Local_Rcv[0]", // 本地接收变量 DONE => "GetDone", // 一次读取完成 ERROR => "GetError", // 错误标志 STATUS => "GetStatus"); // 状态字,用于诊断

REQ是BOOL型请求信号,建议用边沿触发,不要直接接TRUE或常1,否则GET块每个扫描周期都会发起一次读请求,通信会变得很拥挤。ID填16#0100,这个值必须和网络视图连接表里看到的本地ID完全一致,不一致时根本找不到连接。

ADDR_1就是让无数人头疼的地址指针。写法是P#DB号.DBX字节偏移 数据类型 长度。P#DB10.DBX0.0 INT 2的意思是,从远程PLC的DB10的字节偏移0开始,读取2个INT(4字节)。如果我想读DB10里那个REAL,就应该写P#DB10.DBX4.0 REAL 1。数据类型和长度一定要和远程DB里的变量定义对上,否则读出来的数据是错位的。

RD_1是读回的本地存储区,我习惯在客户端建一个专门的通信DB,里面放一个数组或者几个独立变量来接数据。这样程序里哪里用到通信结果,直接引用这个DB,逻辑清晰。

3.4 第一次运行:怎么确认GET是通的

编译下载后,把两个PLC都切到RUN。打开监控表,把GetDone、GetError、GetStatus、Local_Rcv[0]这几个变量加进去。如果一切正常,GetDone会随着每次触发变成TRUE然后复位,GetError保持FALSE,GetStatus保持16#0000,Local_Rcv[0]里出现的值应该和服务器DB10里的Var_Int_0一致。

如果GetStatus不是0,别急着改程序,先查状态字。最常见的是0x80C8,这个值代表连接建立失败,十有八九是服务器侧没勾选允许PUT/GET访问,或者网络视图里根本没建S7连接。我调试时先检查这两点,能解决八成问题。

4. 反向写数据:PUT块的参数设置和触发节奏

4.1 从读变成写,代码结构几乎一样

GET能读,PUT就能写。客户端写数据到服务器DB20,比如写一个REAL和两个INT作为配方参数。PUT块的调用管脚和GET块对称,区别是输入用SD_1提供本地源数据,ADDR_1指向远程目标地址。

// 在OB1中调用PUT块,管脚赋值示意 "PUT_DB"(REQ := "CommReqPut", // 上升沿触发一次写入 ID := 16#0100, // 同一个S7连接 ADDR_1 := P#DB20.DBX0.0 REAL 1, // 目标:远程DB20.DBD0 SD_1 := "Local_Send[0]", // 本地要发出去的数据 ADDR_2 := P#DB20.DBX4.0 INT 1, // 目标:远程DB20.DBW4 SD_2 := "Local_Send[1]", // 本地第二个数据 DONE => "PutDone", // 写入完成 ERROR => "PutError", STATUS => "PutStatus");

这里演示了一次调用同时写两个数据区的写法。每个ADDR_x必须配一个SD_x,ADDR_1对应SD_1,ADDR_2对应SD_2,数量不匹配编译报错。注意ADDR_1和ADDR_2的目标偏移不要重叠,否则后写的会把先写的覆盖掉。

远程DB20同样必须是非优化访问,否则ADDR指针没法寻址。一个典型错误是本地建了DB20并填好数据,却没在服务器PLC上建DB20,运行以后DONE一直不来,STATUS报地址错误。建块的常识问题,排查时容易忽略。

4.2 数据长度和字节序,两个隐形杀手

ADDR指针里的长度不是随便写的,它决定了S7协议访问远程数据区的字节数。P#DB20.DBX4.0 INT 1这种写法,长度由INT这个类型决定,占2字节。如果远程DB20的DBW4里其实放的是一个DWORD,你只写了INT 1,读回来或者写过去的数值就会缺一半,而且后面数据的偏移全部错位。

我整理的习惯是:宁可把ADDR里的数据类型和远程DB变量的类型写得完全一致,也不图省事统一用BYTE加数字。BYTE N这种写法在S7-1200上也能用,但要看懂它,比如P#DB20.DBX0.0 BYTE 4,配合SD_1是一个长度为4的字节数组。用类型化的写法更直观,程序里别人一看就知道远程是什么类型。

字节序方面,S7-1200的数据是大端字节序,高位字节在前。同为S7 PLC之间通信不用操心这个问题,但如果对端是第三方设备或者经过协议网关中转,数值出现明显不对时,第一个怀疑字节序反了。常用的处理办法是在发送侧或者接收侧用SWAP指令做高低字节交换,放在哪一侧取决于你手里那台设备的数据定义。

4.3 REQ边沿触发:别让通信块每个周期都在干活

PUT和GET的REQ如果恒为TRUE,意味着每个扫描周期都在发起一次通信请求。通信块本身有重入保护,但CPU需要花时间去等待远程响应,程序扫描周期会被拖慢,严重时CPU的通信负载告警。正确做法是用上升沿产生一个单脉冲请求,DONE返回TRUE时把请求复位。

// 边沿触发FC的SCL实现 // 输入: Start 手动或逻辑触发信号 // 输出: Req 给PUT/GET的REQ脉冲 IF "Start" AND NOT "LastState" THEN "Req" := TRUE; ELSE "Req" := FALSE; END_IF; "LastState" := "Start";

逻辑很简单,检测Start从0变1的那一个扫描周期,把Req置TRUE,其他时间都置FALSE。这样PUT/GET只在Start刚点亮的瞬间执行一次。多个数据区需要顺序读写时,可以用DONE信号去触发下一轮请求,做一个简单的轮询状态机,别一口气把所有REQ都置TRUE。

5. PUT/GET通信避坑:从连不上到数据错位的6条实战记录

避坑一:STATUS返回0x80C8,连接一直建不上。现象:GET块ERROR为TRUE,Status为16#80C8,DONE永远不来。原因:服务器侧没勾选“允许来自远程对象的PUT/GET通信访问”,或者网络视图里根本没生成S7连接。解决:前者在CPU属性—防护与安全—连接机制里勾上,编译下载;后者回到网络视图,在两个PLC接口之间创建S7连接,确认程序里ID和连接表一致。改完要全部编译、下载,只下程序不下硬件组态,这个开关不会生效。

避坑二:连接正常但读回来的数据全是0或者错位。现象:DONE有,STATUS为0,但接收变量值不对。原因:远程DB是优化访问,ANY指针绝对寻址失效,或者ADDR里的偏移与远程DB变量实际偏移对不上。解决:把远程DB属性里的“优化的块访问”勾掉,重新编译。变量偏移在DB编辑器里打开“偏移”列查看,ADDR的字节偏移必须等于变量的字节偏移。我遇到过一个项目,远程DB加了变量没重新编译下载,本地ADDR按新偏移写,远程实际还是旧偏移,读回来全部串行。

避坑三:REQ一直为TRUE,PLC扫描周期明显变慢。现象:通信正常后CPU执行时间暴涨,通信负载报警。原因:PUT/GET请求被每个扫描周期重复触发,CPU花大量时间等待通信响应。解决:所有REQ都改为上升沿触发,一次通信完成后用DONE信号做后续逻辑的启动条件,别让REQ长期保持TRUE。

避坑四:程序里有两个PUT块,ID填了同一个连接值。现象:第二个PUT块状态一直报连接忙,第一个正常。原因:一个连接ID同时只能有一个通信请求在处理,两个块共享同一连接导致互斥。解决:要么在网络视图里创建第二条S7连接,分配不同的本地ID,程序里两个PUT各用各的ID;要么用一个块按顺序轮询多个数据区,不要平行地开多个块抢同一条连接。

避坑五:IP通了,ping能通,但通信时好时坏。现象:首次通信正常,重新下载程序或者设备断电重启后偶发连不上,过几分钟又恢复。原因:要么是连接表里的主动建立连接标志被改成了FALSE,客户端不主动发起连接,双方干等;要么是现场交换机的端口隔离了TCP 102端口,或者在防火墙上没有放行。解决:检查连接组态里的“主动建立连接”,客户端侧必须是TRUE;现场排查交换机端口配置,确认102端口双向可达。

避坑六:PUT/GET成功了,但远程PLC一停机,本地通信块就一直报错。现象:远程设备断电,客户端STATUS变成0x80C8,远程恢复后状态字回不到0,必须手动触发一次REQ。原因:S7连接在断开后需要重新建立,老版本博图下PUT/GET对断线重连的处理不够积极。解决:在程序里做一个周期性的“心跳”触发,每隔几百毫秒给REQ一个上升沿,远程恢复后自动重连。这个方法也适合作为通信监视信号,心跳成功说明链路活着。

6. 用STATUS字快速定位故障,和我的验收习惯

STATUS字是PUT/GET排错时最值得看的东西,别一上来就怀疑程序逻辑。我调试时先看ERROR和STATUS,再决定是查连接还是查地址。常用值整理如下:

STATUS含义优先排查方向
16#0000无错误,通信成功不需要处理
16#80C8连接拒绝或建立失败服务器侧访问许可、S7连接组态、IP连通性
16#80A4连接中断或超时物理链路、远程CPU停机、TCP 102端口
16#8085地址参数错误ADDR指针的DB号、偏移、长度定义
16#809F数据长度超出允许范围单次读写字节数,拆小数据区

完整的状态字表很长,博途在线帮助里搜“PUT/GET状态字”可以看到全部列表,上面这几个是我在项目里碰到概率最高的。遇到不在表里的值,别瞎猜,去查帮助,比在论坛翻旧帖快得多。

通信稳定性方面我有个个人习惯:把PUT/GET放到循环中断OB里调用,比如OB30,设定周期100ms或200ms。这样通信请求的频率是固定的,不受主程序逻辑长短波动影响,心跳和轮询都好做。放到OB1里的缺点是,主程序某一路逻辑耗时不同,实际扫描周期一直在变,通信的节拍也跟着变,现场不好预估。

还有一个验收习惯:做完通信功能后,我会把远程PLC的网线拔掉,观察本地STATUS能否在预期时间内变成错误代码,再插回去看能否自动恢复。这个测试能验证断线重连逻辑,比只测正常通信可靠得多。曾经有一次项目验收前没做这个测试,结果现场远程设备断电重启,通信再也回不来,只能临时加心跳和重连逻辑,那次的教训至今记得。后来所有PUT/GET项目我都把断线重连当必测项,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询