文章目录

ST静态变量 VAR_STATIC:如何在FB多次调用中保持数据记忆

发布于 2026-03-20 00:05:31 · 浏览 2 次 · 评论 0 条

在结构化文本(ST)编程中,VAR_STATIC 声明的变量是功能块(FB)实现“数据记忆”的核心机制。它让同一个 FB 实例在多次调用之间,自动保留上一次执行结束时的变量值——这正是自动化逻辑中状态保持、计数累计、定时延续、故障锁定等关键功能的底层支撑。

以下内容不依赖任何图形界面或调试工具,仅通过纯文本描述与标准 ST 语法,手把手说明 VAR_STATIC 的作用原理、声明位置、使用边界、常见陷阱及可验证的实操案例。


一、先明确:VAR_STATIC 不是“全局变量”,也不是“局部变量”

ST 中变量作用域由声明位置严格决定:

  • VAR_INPUT / VAR_OUTPUT / VAR_IN_OUT:每次调用都传入新值或传出当前值,调用结束后不保留
  • VAR(默认局部变量):每次调用时重新分配内存并初始化(若未显式赋初值,则为零或 FALSE),调用结束后释放/重置
  • VAR_STATIC仅在 FB 第一次实例化时分配内存并初始化一次;后续所有调用共享同一块内存地址,值自动延续

关键结论:
VAR_STATIC 是 FB 实例级的“持久化存储单元”。每个 FB 实例(例如 MotorCtrl1MotorCtrl2)拥有独立的 VAR_STATIC 区域,互不影响。


二、正确声明 VAR_STATIC:位置、语法、初始化规则

VAR_STATIC 必须位于 FB 的声明区(即 FUNCTION_BLOCK MotorCtrl 之后、END_FUNCTION_BLOCK 之前),且只能出现在 FB 内部——函数(FC)和程序组织单元(POU)中禁止使用。

正确写法示例(以控制电机启停+运行计时的 FB 为例):

FUNCTION_BLOCK MotorCtrl
VAR_INPUT
    Start: BOOL;
    Stop: BOOL;
    ResetFault: BOOL;
END_VAR
VAR_OUTPUT
    Running: BOOL;
    RunTime_s: TIME;
    Fault: BOOL;
END_VAR
VAR_STATIC
    internalState: INT := 0;          // 初始化为 0(可选,但强烈建议显式初始化)
    lastStart: BOOL := FALSE;         // 记录上一次 Start 信号状态
    accumulatedTime: TIME := T#0s;    // 累计运行时间,初始为 0 秒
    faultLock: BOOL := FALSE;         // 故障锁存位
END_VAR
VAR
    timer: TON;                        // 普通局部定时器,每次调用新建
END_VAR

⚠️ 注意事项:

  • VAR_STATIC 块必须完整闭合,不可跨块混写(如不能把一部分写在 VAR_INPUT 后面)。
  • 初始化表达式(:= ...仅在 FB 首次创建时执行一次。后续调用不会重复赋值。
  • 不支持复杂初始化(如调用函数、访问外部变量),只允许常量、字面量或编译期可确定的表达式(如 T#5s, 16#FF, TRUE)。

三、核心行为验证:三次调用中变量如何变化?

假设 MotorCtrl 实例名为 M1,连续三次调用(无中断),输入信号如下:

调用序号 Start Stop ResetFault 上次 Running 本次 Running internalState 变化 accumulatedTime 变化
第1次 TRUE FALSE FALSE TRUE 0 → 1 T#0s → T#200ms(假设定时器走200ms)
第2次 FALSE FALSE FALSE TRUE TRUE 1 → 1(不变) T#200ms → T#400ms
第3次 FALSE TRUE FALSE TRUE FALSE 1 → 0 T#400ms → T#400ms(停止累加)

关键事实:

  • internalStateaccumulatedTime 未被重置,其值从第1次延续到第3次;
  • timer(普通 VAR)在第2次调用时是全新实例,其 ETT#0s 重新开始计时(除非你手动保存 ETVAR_STATIC);
  • lastStart 在第1次后变为 TRUE,第2次检测到 Start 仍为 FALSE,因此可触发边沿判断逻辑(如 R_TRIG(CLK := Start AND NOT lastStart))。

四、典型应用场景与代码片段

场景1:上升沿检测(避免 FC 中无法保存状态的问题)

// 在 FB 的 VAR_STATIC 中声明:
lastInput: BOOL := FALSE;

// 在代码体中:
risingEdge := Input AND NOT lastInput;
lastInput := Input;  // ✅ 这行必须写在最后,确保下次调用读取最新值

若写成 lastInput := InputrisingEdge 计算前,则本次永远检测不到边沿。

场景2:断电保持型计数器(配合掉电保持标志)

VAR_STATIC
    count: UINT := 0;
    powerLossDetected: BOOL := FALSE;
END_VAR

// 主逻辑中:
IF PowerOK THEN
    IF CountUp AND NOT powerLossDetected THEN
        count := count + 1;
    END_IF;
    powerLossDetected := FALSE;
ELSE
    powerLossDetected := TRUE;  // 标记掉电,但 count 值仍在 RAM 中保留
END_IF;

注:真正掉电保持需额外配置硬件属性(如设置变量为 retain),VAR_STATIC 本身只保证“不因调用结束而清零”,不等于断电不丢。

场景3:多步顺序流程的状态机

VAR_STATIC
    step: INT := 0;  // 0=待机, 1=加压, 2=保压, 3=泄压
END_VAR

CASE step OF
    0: IF Start THEN step := 1; END_IF;
    1: IF PressureOK THEN step := 2; END_IF;
    2: IF HoldTimeDone THEN step := 3; END_IF;
    3: IF PressureLow THEN step := 0; END_IF;
END_CASE

每次 FB 被扫描调用,step 值自动维持,无需外部变量传入传出。


五、绝对禁止的操作(否则行为不可预测)

错误做法 后果 正确替代
VARVAR_INPUT 中声明同名变量(如 count: INT)再在 VAR_STATIC 中也声明 count: INT 编译报错:标识符重复定义 删除重复声明,仅留 VAR_STATIC 版本
VAR_STATIC 声明在 PROGRAMFUNCTION 编译失败:“VAR_STATIC not allowed in this context” 改用 FB;FC 中可用 VAR_GLOBAL(需谨慎)或传参+返回值模拟
VAR_STATIC 变量执行 REF(取地址)并传给其他函数修改 可能破坏内存模型,引发不可复现故障 避免取地址;如需共享,改用全局变量(VAR_GLOBAL)并加注释说明生命周期
VAR_STATIC 中声明未初始化的数组并期望首次调用后保持内容 数组元素值为随机内存残留(非零) 显式初始化:buffer: ARRAY[0..9] OF INT := [10(0)];

六、与 VAR_GLOBAL、RETAIN 的关系辨析

类型 生存周期 作用域 是否需硬件支持 典型用途
VAR_STATIC FB 实例整个生命周期(从实例创建到销毁) 仅该 FB 实例内可见 状态机、边沿检测、中间计算缓存
VAR_GLOBAL 整个项目运行期间存在 全局可见(所有 POU 可读写) HMI 通信变量、系统参数、调试标记
VAR_GLOBAL RETAIN 掉电后依靠电池/超级电容保持 全局可见 是(需 PLC 支持 retain 区配置) 累计产量、最后报警号、校准参数

✅ 正确组合:VAR_STATIC 处理实时逻辑状态,VAR_GLOBAL RETAIN 存储需断电保持的关键业务数据。


七、调试技巧:如何确认 VAR_STATIC 是否生效?

无需示波器或专用工具,仅靠在线监控即可验证:

  1. 在 PLC 编程软件中打开 FB 实例的变量表(如 TIA Portal 的“监控表”或 Codesys 的“Online > Watch”);
  2. 找到该实例下的 VAR_STATIC 变量(通常带 “S” 图标或标注 “Static”);
  3. 强制 Start := TRUE,观察 internalState01
  4. 清除强制,让 Start := FALSE,再次强制 Start := TRUE
  5. internalState 保持 1(而非跳回 0),证明 VAR_STATIC 工作正常。

提示:某些 IDE 默认不刷新静态变量监控值,需勾选“循环读取”或按 F5 手动刷新。


八、高级注意:FB 实例复制与 VAR_STATIC 的继承性

当你复制一个已配置的 FB 实例(如 M1 复制为 M2):

  • M1M2 各自拥有完全独立VAR_STATIC 区域;
  • 修改 M1.internalState 绝不影响 M2.internalState
  • 两者初始化值相同(都来自源声明中的 := 表达式),但运行后各自演化。

这正是 FB 封装性的体现:高内聚、低耦合。


九、总结:三句话掌握 VAR_STATIC

VAR_STATIC 是 FB 的记忆器官——它让逻辑具备时间维度。
只要 FB 实例不被销毁,VAR_STATIC 中的值就永不归零。
所有需要“记住上次发生了什么”的自动化逻辑,都应优先考虑 VAR_STATIC

评论 (0)

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

扫一扫,手机查看

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