ST怎么写函数调用结果:Result := MyFunction(Input1, Input2);

发布于 2026-03-15 06:16:33 · 浏览 2 次 · 评论 0 条

在结构化文本(Structured Text,ST)编程中,函数调用是实现模块化、可复用逻辑的核心手段。Result := MyFunction(Input1, Input2); 这一行代码看似简单,但其背后涉及语法规范、数据类型匹配、执行时序、错误处理、调试验证等完整工程实践链条。以下为零基础到工业现场级的全流程实操指南,所有步骤均可直接在主流PLC开发环境(如TIA Portal、Codesys、Unity Pro)中验证。


一、理解这行代码的四个关键角色

ST语言遵循“赋值语句 = 左值 := 右值;”结构。拆解 Result := MyFunction(Input1, Input2);

  1. Result:左值(L-value),即接收返回值的变量。它必须已声明,且数据类型必须与函数声明的返回类型严格一致(例如函数返回 INT,则 Result 也必须是 INT 或兼容类型)。
  2. :=:ST专用赋值运算符(不可写作 ===)。它是阻塞式同步操作:CPU执行至此,会等待 MyFunction 完全执行完毕并返回结果后,才将值写入 Result
  3. MyFunction:函数名。它不是内置指令,而是你或他人定义的用户自定义函数(UDF),必须存在于当前项目符号表中,且已在调用前完成编译。
  4. (Input1, Input2):实参列表。每个参数必须与函数声明中的形参顺序、数量、类型一一对应。若类型不匹配(如传入 REAL 给期望 INT 的形参),编译器将报错,无法下载到PLC。

✅ 正确示例(在TIA Portal中):
函数声明:FUNCTION MyFunction : INT
VAR_INPUT
a : INT;
b : INT;
END_VAR
MyFunction := a + b;
调用处:Result := MyFunction(10, 25);Result 值为 35

❌ 常见错误:

  • Result := MyFunction(10.5, 25); → 编译失败:实参 10.5REAL,形参 aINT,类型不兼容。
  • Result := MyFunction(10); → 编译失败:少传1个参数。
  • Result = MyFunction(10, 25); → 编译失败:误用 = 而非 :=

二、从零开始:定义一个可调用的函数

函数不能凭空调用。你必须先创建它。以下是标准五步法:

  1. 新建函数块:在项目树中右键 Program BlocksAdd New Block → 选择 Function → 输入名称 MyFunction → 点击 OK
  2. 声明返回类型:在函数编辑区顶部,将默认的 FUNCTION MyFunction : VOID 改为明确类型,例如 FUNCTION MyFunction : BOOL(返回布尔值)或 FUNCTION MyFunction : DINT(返回双整数)。
  3. 声明输入参数:在 VAR_INPUT 区域逐行定义:
    VAR_INPUT
      Input1 : REAL;
      Input2 : REAL;
      Enable : BOOL := TRUE;  // 可选使能端,带默认值
    END_VAR

    注意:Enable 参数加了 := TRUE,表示若调用时未传该参数,其默认值为 TRUE。这是ST支持的可选参数机制,但仅适用于带默认值的 VAR_INPUT

  4. 编写函数体逻辑:在 BEGINEND_FUNCTION 之间写计算逻辑。必须给函数名赋值(即返回值):
    BEGIN
      IF Enable THEN
        MyFunction := ROUND((Input1 + Input2) * 0.5);  // 计算平均值并取整
      ELSE
        MyFunction := 0;  // 禁用时返回0
      END_IF;
    END_FUNCTION

    关键规则:函数名 MyFunction 在此处既是函数标识符,也是隐式声明的返回变量。你必须在函数体内至少一次对其赋值,否则编译警告“Function return value not assigned”。

  5. 保存并编译:按 Ctrl + B 编译。无错误后,该函数即可在任何ST程序段中被调用。

三、调用函数的三种合法场景及写法

函数调用位置决定其行为特征。务必根据控制逻辑需求选择:

场景1:在主程序(OB1)中作为独立计算语句

最常见用法,用于周期性计算。

PROGRAM PLC_PRG
VAR
  Result : INT;
  Temp1 : REAL := 100.3;
  Temp2 : REAL := 200.7;
END_VAR

// 每个扫描周期执行一次
Result := MyFunction(Temp1, Temp2, TRUE);

✅ 特点:简单直接;Result 值在每个扫描周期更新。
⚠️ 注意:若 MyFunction 内部含延时(如 TON)、状态机等非纯计算逻辑,其行为将依赖于调用频次(即PLC扫描周期)。

场景2:作为条件表达式的一部分(嵌套调用)

用于简化判断逻辑。

IF MyFunction(SensorValue, Setpoint) > 50 THEN
  **启动** 报警灯;
ELSE
  **关闭** 报警灯;
END_IF;

✅ 特点:无需中间变量;函数返回值直接参与比较。
⚠️ 注意:此时函数仍会执行,但返回值未被显式保存。适合只关心真/假或阈值判断的场合。

场景3:在函数块(FB)内作为局部计算

实现高内聚逻辑封装。

FUNCTION_BLOCK MotorControl
VAR
  SpeedCmd : REAL;
  MaxSpeed : REAL := 1500.0;
  LimitOk : BOOL;
END_VAR

// 在FB的代码区
LimitOk := MyFunction(SpeedCmd, MaxSpeed) = 1;  // 返回1表示限幅通过

✅ 特点:逻辑隔离,便于复用和测试;LimitOk 是FB内部变量,不影响全局命名空间。


四、类型匹配:绕不开的硬性规则

ST是强类型语言。函数调用时的类型检查发生在编译期,而非运行期。这意味着:

  • 形参 Input1 : INT 只接受 INTSINTUSINT(子范围整数)等可隐式转换的整数类型。
  • 形参 Input1 : REAL 接受 REALLREAL(长实数),但不接受 INT —— 即使数值相同,也需显式转换。

正确的类型转换写法:

需求 写法 说明
INT 转为 REAL Result := MyFunction(REAL#Input1, REAL#Input2); 使用 # 强制类型转换符,REAL# 表示“将后面的值解释为REAL”
REAL 转为 INT(截断) Result := MyFunction(INT#Temp1, INT#Temp2); INT# 截去小数部分,10.910
REAL 转为 INT(四舍五入) Result := MyFunction(ROUND(Temp1), ROUND(Temp2)); ROUND() 是标准库函数,返回 DINT,需再转 INTINT#ROUND(Temp1)

❗ 绝对禁止:Result := MyFunction(Input1, Input2); 其中 Input1STRING 类型,而函数期望 INT —— 编译器立即报错:“Cannot convert STRING to INT”。


五、调试:如何确认函数真的执行了?

光看语法正确不够,必须验证运行时行为:

  1. 在线监控:下载程序后,在TIA Portal中打开 PLC_PRG,将光标停在 Result := MyFunction(...); 行,右键 → MonitorMonitor Once。观察 Result 值是否随输入变化实时更新。
  2. 断点调试:右键该行 → Toggle Breakpoint。运行时CPU会在此暂停,可查看 Input1Input2 当前值,并单步进入 MyFunction 内部(需函数源码已下载)。
  3. 添加日志标记:在函数体首尾加入临时输出:
    BEGIN
      DebugFlag := TRUE;  // 全局BOOL变量,连接到HMI指示灯
      MyFunction := ... ;
      DebugFlag := FALSE;
    END_FUNCTION

    若HMI灯闪烁,证明函数被调用;若常亮,说明卡在函数内(如死循环)。


六、高级技巧:处理复杂返回值

当函数需返回多个值时,ST不支持多返回值,但有三种工业级解决方案:

方案1:使用结构体(STRUCT)作为返回类型

定义结构体类型:

TYPE MotorStatus :
STRUCT
  Ready : BOOL;
  FaultCode : WORD;
  ActualSpeed : REAL;
END_STRUCT
END_TYPE

函数声明改为:

FUNCTION GetMotorState : MotorStatus
VAR_INPUT
  AxisID : BYTE;
END_VAR

调用:

Status := GetMotorState(1);
IF Status.Ready THEN
  **启动** 轴1;
END_IF;

方案2:使用函数块(FB)替代函数(FC)

FB自带背景数据块(DB),可保存内部状态,并通过输出引脚暴露多个值:

FUNCTION_BLOCK FB_CalcAverage
VAR_OUTPUT
  AvgValue : REAL;
  Valid : BOOL;
  Count : INT;
END_VAR

调用:

fbAvg(IN := SensorValue);
Result := fbAvg.AvgValue;

方案3:通过VAR_IN_OUT参数“返回”额外值

FUNCTION CalcWithStats : REAL
VAR_INPUT
  Data : ARRAY[0..99] OF REAL;
END_VAR
VAR_IN_OUT
  MinVal : REAL;
  MaxVal : REAL;
END_VAR

调用:

Min := 0.0; Max := 0.0;
Avg := CalcWithStats(MyArray, Min, Max);  // Min/Max被函数修改

✅ 推荐顺序:优先用STRUCT(清晰、安全)→ 次选FB(需状态保持)→ 谨慎用VAR_IN_OUT(易引发副作用)。


七、性能与安全红线

在自动化系统中,函数滥用会导致严重后果:

  • 严禁在函数内调用非可重入函数:如 ADR()(取地址)、MOVE(内存块移动)、TIME_TO_DT(时间转日期)等。这些函数依赖全局状态,多处并发调用可能冲突。
  • 避免递归调用FUNCTION F : INT; BEGIN F := F(1)+1; END_FUNCTION → 编译器禁止,因ST不支持递归。
  • 循环调用检测:若 F1 调用 F2F2 又调用 F1,TIA Portal会在编译时报“Cyclic reference detected”,必须解耦。
  • 实时性保障:单个函数执行时间应 < 1ms(典型PLC扫描周期为10ms)。若函数含复杂浮点运算或大数组遍历,需用 TIME 类型变量测量:
    StartTime := TON_TIME();
    Result := HeavyCalculation(Inputs);
    Elapsed := TON_TIME() - StartTime;
    IF Elapsed > T#5ms THEN
      **触发** 性能告警;
    END_IF;

八、错误处理:让函数更健壮

ST本身无异常机制,但可通过返回约定实现容错:

  1. 返回状态码:函数返回 INT0=成功,负数=错误码:

    FUNCTION SafeDivide : INT
    VAR_INPUT
      Numerator, Denominator : REAL;
    END_VAR
    VAR_OUTPUT
      Quotient : REAL;
    END_VAR
    BEGIN
      IF Denominator = 0.0 THEN
        SafeDivide := -1;  // 除零错误
      ELSE
        Quotient := Numerator / Denominator;
        SafeDivide := 0;   // 成功
      END_IF;
    END_FUNCTION

    调用:

    ErrorCode := SafeDivide(10.0, 0.0, Quotient);
    IF ErrorCode <> 0 THEN
      **记录** 错误日志;
    END_IF;
  2. 使用标准库的 ERROR 类型:Codesys支持 ERROR 枚举,可返回预定义错误(如 ERR_DIV_BY_ZERO)。

  3. 前置校验:在调用前由调用方检查输入有效性,比在函数内处理更高效:

    IF Input1 >= 0 AND Input2 <= 100 THEN
      Result := MyFunction(Input1, Input2);
    ELSE
      Result := -999;  // 无效输入占位符
    END_IF;

Result := MyFunction(Input1, Input2); 不是一行孤立的代码,而是整个自动化软件架构的最小执行单元。它的正确性,取决于你是否严格遵守类型契约、是否理解执行上下文、是否为边界条件预设防御逻辑。现在,你可以打开PLC编程软件,新建函数、声明变量、输入这一行,并亲眼看到 Result 如何从0变为真实物理量——这就是电气自动化的起点。

评论 (0)

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

扫一扫,手机查看

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