You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TwinCAT3 ST语言Modbus读写周期不一致问题及同步咨询

Modbus读写周期不一致问题排查与解决

问题描述

我在TwinCAT 3中使用ST语言编写了Modbus数据读写程序,任务周期设置为13ms。通过Wireshark日志观测发现,数据读取周期约为25ms,而写入周期约为50ms。程序代码如下:

IF NOT fbmbreadregs.bBusy THEN
    fbMBReadRegs(
        sIPAddr:= '192.168.2.20', 
        nTCPPort:= 1504, 
        nUnitID:= 16#FF, 
        nQuantity:= 14, 
        nMBAddr:= 0, 
        cbLength:= SIZEOF(st_ModbusRead), 
        pDestAddr:= ADR(st_ModbusRead), 
        bExecute:= TRUE, 
        tTimeout:= , 
        bBusy=> , 
        bError=> , 
        nErrId=> , 
        cbRead=> );
    
    
    
    fbBasicPID(
        fSetpointValue:= 50,
        fActualValue:= st_ModbusRead.SOC, 
        bReset:= FALSE, 
        fCtrlCycleTime:=0.013 , 
        fKp:= 0.00005, 
        fTn:= 1, 
        fTv:= 0, 
        fTd:= 0, 
        fCtrlOutput=> nFout, 
        nErrorStatus=>);
    
            
    nPref:=(1/0.001)*(50-nFout);
    
    
ELSE
    fbMBReadRegs(bExecute:= FALSE);
END_IF



IF NOT fbMBWriteRegs.bBusy  THEN
        
        arrPQ[0]:=REAL_TO_WORD(ABS(nPref));
        
    fbMBWriteRegs(
        sIPAddr:= '192.168.2.20', 
        nTCPPort:= 1504, 
        nUnitID:= 16#FF, 
        nQuantity:= 5, 
        nMBAddr:= 16#0014, 
        cbLength:= SIZEOF(arrPQ), 
        pSrcAddr:= ADR(arrPQ), 
        bExecute:= TRUE, 
        tTimeout:= , 
        bBusy=> , 
        bError=> , 
        nErrId=> );

ELSE
    fbMBWriteRegs(bExecute:=FALSE);
END_IF

请问为何二者周期存在差异?如何实现读写操作周期一致?

周期差异原因

  • 异步功能块的串行执行逻辑:TwinCAT的Modbus TCP功能块是异步执行的,当前代码中读写逻辑串行绑定——读操作完成后才会触发PID计算,之后才尝试触发写操作,导致写操作的触发时机始终滞后于读操作,相当于读写请求排队执行,周期自然翻倍。
  • 未设置超时参数:代码中tTimeout参数为空,系统使用默认超时值(通常远大于13ms),若通信出现短暂延迟,功能块会等待超时后才重置bBusy,进一步拉长实际执行周期。
  • 数据依赖导致触发延迟:写操作依赖读操作获取的st_ModbusRead.SOC计算出的nPref,这强制写操作必须等待读操作和PID计算完成后才能触发,本质上让读写操作串行化,写周期至少是读周期的两倍。

实现读写周期一致的方案

方案1:并行触发读写操作(推荐)

调整逻辑,让读写请求在同一任务周期内独立触发,避免互相阻塞,仅在读操作完成时更新PID输入:

// 读操作逻辑:独立判断执行状态
IF NOT fbmbreadregs.bBusy THEN
    fbMBReadRegs(
        sIPAddr:= '192.168.2.20', 
        nTCPPort:= 1504, 
        nUnitID:= 16#FF, 
        nQuantity:= 14, 
        nMBAddr:= 0, 
        cbLength:= SIZEOF(st_ModbusRead), 
        pDestAddr:= ADR(st_ModbusRead), 
        bExecute:= TRUE, 
        tTimeout:= T#50MS, // 明确设置合理超时
        bBusy=> , 
        bError=> , 
        nErrId=> , 
        cbRead=> );
ELSE
    fbMBReadRegs(bExecute:= FALSE);
END_IF

// PID计算:仅在读操作完成时更新输入
IF NOT fbmbreadregs.bBusy THEN
    fbBasicPID(
        fSetpointValue:= 50,
        fActualValue:= st_ModbusRead.SOC, 
        bReset:= FALSE, 
        fCtrlCycleTime:=0.013 , 
        fKp:= 0.00005, 
        fTn:= 1, 
        fTv:= 0, 
        fTd:= 0, 
        fCtrlOutput=> nFout, 
        nErrorStatus=>);
    
    nPref:=(1/0.001)*(50-nFout);
END_IF

// 写操作逻辑:独立判断执行状态,不依赖读操作的bBusy
IF NOT fbMBWriteRegs.bBusy THEN
    arrPQ[0]:=REAL_TO_WORD(ABS(nPref));
    fbMBWriteRegs(
        sIPAddr:= '192.168.2.20', 
        nTCPPort:= 1504, 
        nUnitID:= 16#FF, 
        nQuantity:= 5, 
        nMBAddr:= 16#0014, 
        cbLength:= SIZEOF(arrPQ), 
        pSrcAddr:= ADR(arrPQ), 
        bExecute:= TRUE, 
        tTimeout:= T#50MS, // 明确设置合理超时
        bBusy=> , 
        bError=> , 
        nErrId=> );
ELSE
    fbMBWriteRegs(bExecute:=FALSE);
END_IF

方案2:使用同步Modbus功能块

如果不需要异步执行的灵活性,可以使用TwinCAT提供的同步Modbus功能块(如fbMBReadRegsSync、fbMBWriteRegsSync),这类功能块会在一个任务周期内完成通信(需确保网络延迟远小于13ms),严格遵循任务周期。注意同步块会阻塞任务执行,需谨慎设置超时时间,避免任务周期被拉长。

方案3:固定周期强制触发

通过周期计数器,强制每隔1个任务周期(13ms)触发一次读写请求,忽略临时的bBusy状态(需额外处理通信错误重试):

VAR
    nCycleCounter: UINT := 0;
END_VAR

nCycleCounter := nCycleCounter + 1;

// 每1个周期触发读操作
IF nCycleCounter >= 1 THEN
    nCycleCounter := 0;
    fbMBReadRegs(
        sIPAddr:= '192.168.2.20', 
        nTCPPort:= 1504, 
        nUnitID:= 16#FF, 
        nQuantity:= 14, 
        nMBAddr:= 0, 
        cbLength:= SIZEOF(st_ModbusRead), 
        pDestAddr:= ADR(st_ModbusRead), 
        bExecute:= TRUE, 
        tTimeout:= T#50MS, 
        bBusy=> , 
        bError=> , 
        nErrId=> , 
        cbRead=> );
ELSE
    fbMBReadRegs(bExecute:= FALSE);
END_IF

// 同步触发写操作
IF nCycleCounter = 0 THEN
    arrPQ[0]:=REAL_TO_WORD(ABS(nPref));
    fbMBWriteRegs(
        sIPAddr:= '192.168.2.20', 
        nTCPPort:= 1504, 
        nUnitID:= 16#FF, 
        nQuantity:= 5, 
        nMBAddr:= 16#0014, 
        cbLength:= SIZEOF(arrPQ), 
        pSrcAddr:= ADR(arrPQ), 
        bExecute:= TRUE, 
        tTimeout:= T#50MS, 
        bBusy=> , 
        bError=> , 
        nErrId=> );
ELSE
    fbMBWriteRegs(bExecute:=FALSE);
END_IF

// PID计算逻辑保持不变
fbBasicPID(
    fSetpointValue:= 50,
    fActualValue:= st_ModbusRead.SOC, 
    bReset:= FALSE, 
    fCtrlCycleTime:=0.013 , 
    fKp:= 0.00005, 
    fTn:= 1, 
    fTv:= 0, 
    fTd:= 0, 
    fCtrlOutput=> nFout, 
    nErrorStatus=>);

nPref:=(1/0.001)*(50-nFout);

内容的提问来源于stack exchange,提问作者juanda_

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 22:52:33