文章目录

ST通信指令应用:T_SEND与T_RECV在ST中的异步通信处理

发布于 2026-03-18 15:29:53 · 浏览 6 次 · 评论 0 条

在S7-1200/1500 PLC编程中,使用结构化文本(ST)实现以太网TCP异步通信时,T_SENDT_RECV 是两个核心系统函数块。它们不依赖循环扫描周期同步执行,而是通过触发信号完成状态位协同工作,从而在单个OB(如OB1)中安全、高效地管理多路并发通信任务。以下为零依赖、可直接复用的实操指南。


一、先决条件确认

  1. 硬件与固件

    • CPU型号为 S7-1200(固件 V4.0+)或 S7-1500(任意版本);
    • 已在TIA Portal中配置好以太网接口IP地址,并启用“允许从远程伙伴访问”;
    • 目标服务器(如PC、HMI、另一台PLC)已开启TCP监听端口(例如 5000),且防火墙放行该端口。
  2. 通信资源预留

    • 每个 T_SEND / T_RECV 实例占用1个“通信连接资源”;
    • S7-1200最多支持8个并发TCP连接(含TCON),S7-1500默认支持64个(可扩展至256);
    • 禁止在同一个DB中重复声明多个未命名的 T_SEND 实例——必须为每个实例分配独立的背景DB。
  3. 数据类型准备

    • 定义发送缓冲区:SendBuffer : ARRAY[0..999] OF BYTE(最大1000字节,实际长度由LEN参数控制);
    • 定义接收缓冲区:RecvBuffer : ARRAY[0..999] OF BYTE
    • 声明连接ID变量:ConnID : UINT := 1(值范围 1–65535,全局唯一,不可为0)。

二、建立TCP连接(TCON)

T_SENDT_RECV 不自动建连,需先调用 TCON 函数块完成三次握手。

  1. 声明TCON背景DB
    在DB中定义结构体变量 MyTCON : TCON,其成员包括:

    • ID:连接标识符(UINT,与后续T_SEND/T_RECVID一致);
    • CONNECT:指向连接描述结构体的指针(P#MyConnectionData);
    • DONEERRORSTATUS:状态输出位。
  2. 配置连接描述结构体
    在同一DB中声明:

    MyConnectionData : TSAP_CONN_REQ := (
      remote_address := (ip_address := '192.168.0.100', port := 5000),
      local_address := (port := 0),  // 0 = 自动分配本地端口
      connection_type := 1,          // 1 = TCP
      active_establishment := TRUE   // TRUE = PLC主动发起连接
    );
  3. 触发连接
    设置 MyTCON.ENTRUE
    等待 MyTCON.DONE = TRUEMyTCON.ERROR = FALSE
    MyTCON.STATUS <> 0,查TIA Portal在线帮助中的STATUS代码表定位故障(如 16#01F4 = 目标主机拒绝连接)。

⚠️ 注意:TCON 只需执行一次。连接建立后保持活跃,直到调用 TDISCON 或CPU重启。


三、T_SEND异步发送全流程(关键步骤)

T_SEND 在使能信号上升沿触发发送请求,立即返回,不阻塞扫描周期。

  1. 声明T_SEND实例及背景DB
    新建FB(如 FB_Send_ASCII),在其背景DB中声明:

    tSendInst : T_SEND;
    SendBuffer : ARRAY[0..255] OF BYTE;
    SendLen : UINT := 0;
  2. 构造待发送数据(以ASCII字符串为例)
    将字符串逐字符写入SendBuffer

    SendBuffer[0] := 16#48;  // 'H'
    SendBuffer[1] := 16#65;  // 'e'
    SendBuffer[2] := 16#6C;  // 'l'
    SendBuffer[3] := 16#6C;  // 'l'
    SendBuffer[4] := 16#6F;  // 'o'
    SendLen := 5;
  3. 调用T_SEND
    在FB的主体逻辑中编写:

    tSendInst(
      ID := ConnID,
      DATA := ADR(SendBuffer),
      LEN := SendLen,
      DONE => bSendDone,
      ERROR => bSendError,
      STATUS => wSendStatus
    );
  4. 处理发送结果

    • bSendDone = TRUE:表示数据已成功提交至TCP协议栈(不保证对方收到);
    • bSendError = TRUE:检查 wSendStatus
      • 16#80A2 = 连接已断开;
      • 16#80A3 = 发送缓冲区满(需降低发送频率或增大缓冲区);
    • 重发策略:若失败,清零 bSendDonebSendError 后,在下一扫描周期重新触发(即再次调用T_SEND,无需重连)。

四、T_RECV异步接收全流程(防丢包设计)

T_RECV 持续轮询接收缓冲区,每次仅提取当前已到达的全部字节,不等待完整报文。

  1. 声明T_RECV实例及背景DB
    在FB(如 FB_Recv_ASCII)背景DB中声明:

    tRecvInst : T_RECV;
    RecvBuffer : ARRAY[0..255] OF BYTE;
    RecvLen : UINT := 0;
    BytesReceived : UINT := 0;
  2. 调用T_RECV

    tRecvInst(
      ID := ConnID,
      DATA := ADR(RecvBuffer),
      MAX_LEN := SIZEOF(RecvBuffer),
      DONE => bRecvDone,
      ERROR => bRecvError,
      STATUS => wRecvStatus,
      LEN => BytesReceived
    );
  3. 解析接收数据(关键!)
    T_RECV 不提供报文边界识别。必须自行约定帧格式。推荐使用定长头+变长体

    • 前2字节为 LEN(高位在前,大端序),表示后续有效数据长度;
    • 例如收到 00 05 48 65 6C 6C 6FLEN=5,有效数据为 48 65 6C 6C 6F("Hello")。

    解析逻辑:

    IF bRecvDone AND (BytesReceived >= 2) THEN
      // 提取LEN字段(大端)
      nExpectedLen := WORD_TO_UINT(RecvBuffer[0] * 256 + RecvBuffer[1]);
      // 判断是否收齐整帧
      IF BytesReceived >= (2 + nExpectedLen) THEN
        // 复制有效数据到应用区
        FOR i := 0 TO nExpectedLen-1 DO
          AppData[i] := RecvBuffer[2 + i];
        END_FOR;
        // 更新已处理字节数,为下次接收腾出空间
        MoveBlock(
          SRC_ADDR := ADR(RecvBuffer[2 + nExpectedLen]),
          DST_ADDR := ADR(RecvBuffer[0]),
          SIZE := BytesReceived - (2 + nExpectedLen)
        );
        BytesReceived := BytesReceived - (2 + nExpectedLen);
      END_IF;
    END_IF;
  4. 错误应对

    • bRecvError = TRUEwRecvStatus = 16#80A2:连接中断,应停止调用T_RECV并尝试重连;
    • BytesReceived = 0:无新数据,属正常现象,无需处理。

五、双工通信协调策略(避免资源冲突)

同一连接ID上,T_SENDT_RECV同时运行,但需规避以下风险:

风险类型 表现 解决方案
发送抢占接收 T_SEND 正在提交大数据包时,T_RECV 被调用导致内部缓冲区竞争 T_SENDBUSY输出为TRUE期间,跳过本次T_RECV调用(T_SEND.BUSY在数据提交完成前保持TRUE
接收缓冲区溢出 T_RECV未及时读取,新数据覆盖旧数据 设置MAX_LEN ≤ 接收缓冲区大小;在bRecvDoneTRUE立即处理数据,不延迟至下周期
连接状态不同步 T_SEND发现连接断开,但T_RECV仍在调用 所有通信函数块共用同一ConnID,并在任一模块检测到STATUS=16#80A2时,统一触发重连流程

示例协调逻辑(在主程序OB1中):

// 全局连接状态
bConnOK := (MyTCON.DONE AND NOT MyTCON.ERROR) OR (tSendInst.BUSY OR tRecvInst.BUSY);

IF NOT bConnOK THEN
  // 断连处理:复位所有函数块使能位,延时1s后重新TCON
  tSendInst.EN := FALSE;
  tRecvInst.EN := FALSE;
  MyTCON.EN := FALSE;
  IF TON_Restart.Q THEN
    MyTCON.EN := TRUE;
  END_IF;
ELSE
  // 正常通信:仅当非BUSY时调用T_RECV
  IF NOT tSendInst.BUSY THEN
    tRecvInst.EN := TRUE;
  ELSE
    tRecvInst.EN := FALSE;
  END_IF;
  tSendInst.EN := bSendTrigger; // 由应用逻辑控制
END_IF;

六、调试与诊断技巧

  1. 在线监控必看变量

    • TCON.STATUS:连接阶段问题定位;
    • T_SEND.STATUS / T_RECV.STATUS:实时查错(TIA Portal中右键变量→“转到声明”可查看状态码文档);
    • T_SEND.LEN 输入值 vs T_SEND.DONE 后实际发出字节数(若小于输入值,说明被流控截断)。
  2. Wireshark抓包验证

    • 过滤规则:ip.addr == 192.168.0.100 && tcp.port == 5000
    • 关键观察点:
      • SYN → SYN-ACK → ACK(建连);
      • TCP Payload长度是否与T_SEND.LEN一致;
      • 是否出现TCP Retransmission(表明网络丢包或对方处理慢)。
  3. 资源泄漏自检

    • 若多次重连后通信失败,检查:
      • TCON.ID 是否重复使用(必须每次新建连接时递增ID);
      • T_SEND/T_RECV 的背景DB是否被其他逻辑意外覆写。

七、性能优化清单(实测有效)

  • ✅ 将SendBufferRecvBuffer声明在全局DB中,避免FC/FB局部变量频繁分配;
  • T_SEND.LEN 设置为固定值(如256),避免每次计算长度带来的周期抖动;
  • ✅ 使用MOVE指令替代循环赋值填充SendBuffer(如 MOVE(ADR('Hello'), ADR(SendBuffer), 5));
  • ✅ 对高频小数据(如每100ms发1字节),改用T_CONFIG配置UDP连接,降低TCP握手开销;
  • ✅ 在S7-1500中,启用“优化块访问”并勾选“异步执行”,使T_SEND/T_RECV在后台线程运行,彻底脱离OB1周期限制。

八、典型错误代码对照表

现象 错误代码(STATUS) 根本原因 修正动作
无法建连 16#01F4 目标IP不可达或端口未监听 pingtelnet 192.168.0.100 5000验证网络通路
发送失败 16#80A3 TCP发送窗口满 降低发送频率,或增加T_SEND调用间隔至≥50ms
接收截断 BytesReceived < LEN字段值 报文未一次性到达 T_RECV后添加重试逻辑:若BytesReceived < 2,10ms后再次调用
连接闪断 16#80A2 频繁出现 网络不稳定或对方主动关闭 TCON后添加心跳机制:每30秒发送1字节保活包

T_SENDT_RECV 的本质是将TCP协议栈能力暴露为ST可调度的异步服务。掌握其触发-响应-状态闭环逻辑,即可在不引入复杂状态机的前提下,构建稳定工业级通信链路。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文