
上周调一套S7-1500和第三方视觉系统的通讯程序里TSEND_C一触发ERROR灯就亮STATUS停在16#80A3。当时我第一反应是网线没插好但抓包、查连接资源、折腾了大半天才发现问题根本不是物理链路而是连接建立时序上的隐性坑。这种经历估计搞西门子通讯的都遇过TSEND_C看起来就是一个发送指令可一旦报错你面对的不只是一条错误代码而是连接建立、数据发送、底层网络三个环节的交叉问题。这篇文章我就把TSEND_C的STATUS错误代码按实际经验拆开结合具体排错链路整理一套能直接上手的排查方法。1. TSEND_C不应该只被当成“发送”它是连接状态机的一部分1.1 管脚职责以及为什么BUSY常1不一定说明坏了很多人第一次用TSEND_C都会把它当成一个高级版的“MOVE”指令给REQ一个脉冲数据就从DATA飞出去了。这么理解短期能用但一旦碰到现场通讯抖动就会陷入“乱改参数、到处加延时”的被动状态。TSEND_C的本质其实是把TCON建立连接、TSEND发送数据、以及连接维护逻辑合并到一个块里所以它内部自带一个连接状态机。关键管脚的功能区分我列在下面这些在排错时是判断故障层次的依据管脚 | 方向 | 含义 | 排错关注点 REQ | IN | 发送请求 | 应该用上升沿不能常1 ID | IN | 连接资源号 | 必须与CONNECT DB里的ID一致 LEN | IN | 发送数据长度单位字节 | 与实际数据缓冲区长度的关系 DATA | IN/OUT | 指向待发送数据区 | 必须是DB里的绝对地址或指针 DONE | OUT | 发送完成一次 | 发送成功时为TRUE BUSY | OUT | 发送尚未完成 | 为TRUE时表示功能块正在工作 ERROR | OUT | 错误标志 | 同一周期内STATUS有效 STATUS | OUT | 错误代码 | 记录后先查这里不要凭ERROR灯的亮灭猜有一个容易误判的地方BUSY1。很多人看到BUSY一直为1就认为程序卡死了其实在连接刚建立、或者对方接收窗口处理慢的时候BUSY在几个扫描周期内保持TRUE是正常的。真正需要警惕的是BUSY长时间不落同时DONE和ERROR都是0这时候更要关注的是连接是否还活着而不是盲目复位指令。1.2 CONNECT参数背后的连接描述DB藏着大部分错误根源TSEND_C和普通发送指令最大的不同就是它带一个CONNECT参数。这个参数通常指向一个连接描述DB里面存的是接口ID、连接ID、主动/被动建立标志、本地端口、远程IP、远程端口等。TIA Portal在创建连接时会自动生成这个DB它长得像这样TYPE TCON_IP_v4 STRUCT InterfaceID : CONN_OUC : 1; // CPU网口对应的接口号 ID : CONN_ANY : 1; // 连接号 ConnectionType : BYTE : 16#11; // TCP ActiveEstablished : BOOL : TRUE; // TRUE客户端FALSE服务器 LocalPort : WORD : 2000; RemotePort : WORD : 5000; RemoteAddress : ARRAY[1..4] OF BYTE : (192,168,0,10); END_STRUCT END_TYPE注意这个ID不是随便填的。PLC内部通过ID来分配和管理连接资源如果ID冲突、ID超出CPU连接资源范围、或者ID为0TSEND_C一启动就会直接报块参数错误。我处理过的项目里有不少人从旧项目复制程序CONNECT DB跟着复制过来结果ID和当前项目的连接表对不上通讯自然起不来。所以排错第一步永远是先确认CONNECT DB里的ID有没有改错。至于是不是手动维护一个连接描述DB我建议新手不要手动造直接在TIA的连接组态里创建让软件帮你生成这样字段类型不容易错。1.3 排错要看的不是ERROR灯而是STATUSERROR组合很多时候现场工程师一看到灯亮就慌了其实TSEND_C的状态判断要组合着看。ERROR1且STATUS非零说明这次调用产生了错误如果ERROR0哪怕STATUS显示的不是0也要先怀疑是不是数据保持问题。更常见的一个反向场景ERROR1但STATUS存的值是上次的错误因为同一周期内没有把STATUS赋给新变量。所以在线监视时我习惯把调用TSEND_C的周期和STATUS刷新时间对齐必要时在STATUS后面加一个跳变记录把错误代码连同时间戳存到DB里方便事后分析。另一个经验是STATUS代码的高低字节是有规律的不是一串无规律数字。把这套规律掌握之后哪怕遇到没见过的错误代码也能快速缩小排查范围。这个放到下一节仔细讲。2. 错误代码的结构与高频代码逐个拆解2.1 高字节决定错误来源低字节才是具体原因TSEND_C通过STATUS返回的十六进制错误代码高字节的信息价值比低字节更大。虽然不同固件版本细节有差异但大致可以按这个经验分类STATUS高字节范围 | 对应问题层次 | 优先排查方向 16#80xx | 块参数或指令内部逻辑 | CONNECT DB、ID、DATA指针、LEN长度 16#81xx | 连接建立/关闭逻辑 | 主动/被动建立标志、连接资源、伙伴端是否监听 16#82xx | 底层协议栈或网络返回 | 对端复位、超时、路由、防火墙、抓包看TCP 16#83xx | 操作系统/底层资源问题 | CPU连接资源耗尽、固件限制就像HTTP状态码一样4xx和5xx的含义完全不同。TSEND_C排错也可以先看高字节确定是“你自己参数给错了”还是“网络层根本没打通”然后再去看低字节。这个习惯能帮你省掉大量瞎试的时间。2.2 高频错误代码表这些代码我在现场真正遇到过下面列几个我在不同项目里实际碰到过的STATUS值并给出当时的排查场景。这里要说明不同TIA版本和CPU固件对具体代码的文本描述可能略有出入在线帮助里查到的才是最准确的我的表格主要告诉你“遇到这个代码优先往哪想”。STATUS代码 | 字面方向 | 我在现场遇到的情况 | 最可能的原因 16#80A1 | 块参数无效 | 一调用就报错根本进不去发送流程 | CONNECT DB类型不匹配、ID为0、DATA地址指向了Input区 16#80A2 | 连接描述不存在或异常 | 复制指令后没改连接号 | CONNECT DB的ID被占用或ID在PLC连接表中不存在 16#80A3 | 连接建立过程中 | 间歇性通讯失败REQ一触发就报 | 连接还没建立完成就发数据或上一轮连接没释放 16#80C4 | 连接已经存在 | 重新连接时反复报错 | 连接已建立又用相同ID重复建立 16#80C5 | 连接被终止 | 通讯一段时间后掉线 | 对端主动关闭连接、网络断线、看门狗超时 16#80E1 | 数据指针不可访问 | 数据发不出去DONE一直为0 | DATA指向的地址无效例如指向了系统自带DB未定义的地址 16#80E2 | 数据指针对齐错误 | 换了数据区后开始报错 | DATA地址偏移不是4的倍数或跨DB访问未用绝对寻址2.3 16#80A3和16#80C5是最容易误判的两个代码有些错误代码看似简单实际背后能扯出一堆问题。16#80A3就是典型的“看着像网络问题其实是逻辑时序问题”。它表示连接还在建立中而你这时候就发起了数据发送请求。尤其当你把REQ设成常1或者用了一个大周期定时器去触发TSEND_C连接刚发出建立请求下一个扫描周期数据就跟着到了这时候TSEND_C只能告诉你“我还没准备好”。另外16#80C5意味着连接已经断了。这个代码在现场往往是因为对端程序重启、对端网线断开、或者交换机端口被关闭。但更深层的问题是TSEND_C不会因为连接断开就自动把连接资源清掉当你试图用同一个ID再次建立连接时它可能会先撞上残留的旧连接导致状态码变成16#80C4或16#80A3。很多工程师遇到这种情况只会反复触发REQ结果就是反复报错越试越乱。3. 一次完整的16#80A3排错链路复盘3.1 现场重现S7-1500周期向视觉系统发数据项目结构不复杂S7-1500走TCP客户端主动连接一台视觉控制器服务器端TSEND_C负责把位置数据发给视觉系统。程序里REQ使用了2秒定时器正常情况下每隔两秒发一次上位机能收到数据。现场运行大概两小时后视觉控制器偶尔报“数据缺包”随即上位机主动断开连接PLC这边再发起发送时TSEND_C的STATUS就显示16#80A3ERROR灯周期性地闪。一开始我怀疑是网线或交换机问题但视觉控制器和PLC能ping通用上位机测试端口也能建立连接物理链路基本排除。随后我在线监视发现CPU重启后前几分钟通讯一切正常只要视觉控制器侧一旦断开连接PLC就再也恢复不了。3.2 四步排查从STATUS到物理链路一层层缩小范围第一步查CONNECT DB。我打开连接描述DB确认接口ID、本地端口、远程IP、远程端口都没问题ActiveEstablished是TRUE客户端主动建连ID是1。因为这个项目从创建以后就没动过连接配置这一层暂时排除。第二步查连接资源是否被残留连接占满。打开CPU的诊断缓冲区看到里面有一条“连接资源被占用连接ID1”的信息。这就说明视觉控制器断开后S7-1500侧的TCP连接并没有彻底销毁TSEND_C每次重新触发时内部状态机还在上一轮连接中断的阴影里于是反复报16#80A3。第三步看对端侧状态。视觉控制器的日志显示它确实主动关闭过连接但之后没有收到PLC的SYN报文。也就是说PLC自己以为连接还在建立中压根没有重新发起TCP握手。结合前一步判断问题在PLC侧的连接状态没复位。第四步抓包确认。虽然已经基本定位我还是在交换机镜像口抓了包。抓包结果里PLC在视觉控制器断开后的一段时间内根本没有发出任何TCP SYN包。这验证了“连接建立过程卡在中间状态”的判断而不是网络丢包。3.3 根治方式以及一段防止复发的SCL写法问题根因找到了处理办法就是让TSEND_C在连接断开后能主动把旧连接资源释放掉再进行下一次连接。TIA的TSEND_C不像单独使用TCON那样方便地控制断开所以在实际项目中我给发送逻辑加了一个简单的“连接异常复位”状态当ERROR1且STATUS为连接断开类错误时调用一次TDISCON_C或者重新初始化连接描述DB对应的连接资源清掉残留连接后再把发送请求重新置位。更规范的做法是在组态里把发送逻辑写成状态机。下面是我项目里常用的SCL骨架供参考VAR sendTrig : R_TRIG; sendReqRaw : Bool; sendDone : Bool; sendBusy : Bool; sendError : Bool; sendStatus : Word; errorLatch : Word; resetConn : Bool; END_VAR sendTrig(CLK : sendReqRaw); // 上升沿触发发送避免REQ常1 TSEND_C_DB( REQ : sendTrig.Q, ID : 1, LEN : 16, DATA : SendData, DONE sendDone, BUSY sendBusy, ERROR sendError, STATUS sendStatus, CONNECT : TCON_DB ); // 锁存最后一个非零错误码 IF sendError THEN errorLatch : sendStatus; END_IF; // 当出现连接断开/正在建立类错误时先给一个复位信号 IF errorLatch WORD#16#80A3 OR errorLatch WORD#16#80C5 OR errorLatch WORD#16#80C4 THEN resetConn : TRUE; errorLatch : WORD#16#7000; // 清掉锁存值避免重复复位 END_IF; // 复位连接后在下一个周期再把发送请求打开 IF resetConn THEN sendReqRaw : FALSE; // 这里调用TDISCON_C或对连接资源执行一次断开 resetConn : FALSE; END_IF;这段逻辑的核心思想是对“连接断开类错误”做一次性锁存和一次性复位而不是让REQ像打点一样不停去撞墙。调试到现在这套逻辑在好几个项目里都稳定跑了一年多没再出现过16#80A3卡死的情况。4. 连接资源和背景DB多连接项目中最隐蔽的坑4.1 连接资源到底是多少又是怎么被占满的S7-1200和S7-1500的可用连接资源是有限的。S7-1200根据固件不同一般只有8到16个TCP连接资源S7-1500多一些但也不是无限多。你组态里的HMI连接、PN IO通信、Web服务器、PUT/GET、以及TSEND_C/TRCV_C建立的所有TCP连接全都在抢这同一份资源。当连接资源被占满后TSEND_C的STATUS可能返回16#80A2或16#80A3而不是一个“资源不足”这样直观的描述。所以排查时不要只盯着TSEND_C本身还要看有没有其他指令比如一大堆背景DB只改了实例名却没改ID或者调试过程中反复下载不同连接配置导致连接资源一直没被释放。常用的检查方式是把CPU转在线查看“诊断缓冲区”里的资源条目或在TIA的设备组态里看“连接资源”占用情况。4.2 复制功能块时CONNECT DB是最容易被带错的还有一类隐蔽问题很少在通讯功能测试时暴露而是等到现场才爆发从其他项目或另一台PLC里直接复制TSEND_C的调用函数块实例化成新DB但CONNECT参数指向的连接描述DB还是旧的。如果新旧项目的连接ID一样表面看着没事一旦ID重复或接口号不对就会出现“某台设备通另一台设备不通”的诡异现象。我习惯的做法是每次复制连接相关指令后强制检查三处连接描述DB的编号、ID值、以及InterfaceID对应的实际网口。特别是在S7-1500带有多个PN接口的模块上InterfaceID1和InterfaceID2对应不同的物理网口填错了就会一直往错误的方向发送数据查半天IP和防火墙都没有用。4.3 在线修改连接DB后不下载是现场“改了没生效”的元凶在线监视时直接把CONNECT DB里某个参数改了比如把RemotePort从5000改成6000然而忘记下载到CPU然后通讯还是失败。这个坑几乎每个工程师都踩过但很容易被忽略因为TIA的在线监视界面看起来“已经改了”实际CPU里运行的还是旧值。遇到这种情况我的诊断方法是在DB里加一个“StructVersion”字段每次修改连接描述DB结构时把它手动递增一次。通讯出现异常时先远程读一下这个字段确认PLC里跑的确实是当前版本。简单粗暴但非常管用。5. 我把这些经验固化成了一张检查表5.1 编程侧检查清单调用TSEND_C之前必须核对我把每次新建或排查TSEND_C时都会过一遍的检查点整理成了清单虽然不是所有项目都需要全部核但照着走一遍能挡掉绝大多数低级问题。REQ是否用了上升沿而不是常1或长周期布尔量。ID是否为一个大于0的值且在CPU连接表中唯一。CONNECT DB是否为当前项目自动生成的连接描述DBID和InterfaceID是否与实际连接匹配。主动/被动建立标志是否准确PLC是客户端ActiveEstablished就必须是TRUEPLC是服务器就设FALSE不能两边都设成主动。DATA和LEN是否匹配LEN指字节数不是字或双字数。DATA指向的数据区是否为DB块中的地址且地址起始偏移建议按4字节对齐。发送缓冲区是否稳定存在不能在发送过程中被其他逻辑修改内容。5.2 网络侧检查清单通讯失败时按顺序查如果编程侧检查都正常STATUS还在报错那就得把视角从PLC程序挪到网络链路。下面是我在现场会按顺序操作的步骤先确认IP和网段PLC和对端IP必须能通且不在冲突IP段。确认对端端口监听状态用对端设备的netstat或抓包工具看TCP端口是否LISTENING。检查防火墙Windows防火墙、工业防火墙、交换机ACL都可能把SYN包直接丢掉表现就是PLC侧一直报连接建立超时。用Wireshark或PLC侧诊断抓包看有没有SYN包发出有没有SYN-ACK返回。如果跨了VLAN或路由器检查对应网关、VLAN ID、广播域是否一致。5.3 遇到STATUS非零的通用处理顺序最后再给一个所有场景都能套用的处理顺序记录STATUS代码和当前时间不要急着复位。看诊断缓冲区整体信息确认是不是连接资源、固件、或底层网络报出来的辅助信息。确认是重复报错还是偶尔报错——重复报错大概率是参数或资源问题偶尔报错大概率是网络抖动或对端复位。不要连续快速触发REQ先让连接状态稳定下来。保存当前项目现场必要时在线比对CPU里实际运行的程序和组态防止“改了没下载”。最后严格按照检查清单把程序侧和网络侧各过一遍。我现在每到一个新现场调TSEND_C都默认先按这套顺序来不再靠肉眼猜。这套方法不光适用于TSEND_CTRCV_C和TCP通讯指令的排查思路也大同小异只是发送和接收的侧重点不同STATUS代码的分类逻辑基本通用。希望这篇整理能帮你少走一点弯路。