在结构化文本(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 实例(例如 MotorCtrl1 和 MotorCtrl2)拥有独立的 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(停止累加) |
关键事实:
internalState和accumulatedTime未被重置,其值从第1次延续到第3次;timer(普通VAR)在第2次调用时是全新实例,其ET从T#0s重新开始计时(除非你手动保存ET到VAR_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 := Input在risingEdge计算前,则本次永远检测不到边沿。
场景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值自动维持,无需外部变量传入传出。
五、绝对禁止的操作(否则行为不可预测)
| 错误做法 | 后果 | 正确替代 |
|---|---|---|
在 VAR 或 VAR_INPUT 中声明同名变量(如 count: INT)再在 VAR_STATIC 中也声明 count: INT |
编译报错:标识符重复定义 | 删除重复声明,仅留 VAR_STATIC 版本 |
将 VAR_STATIC 声明在 PROGRAM 或 FUNCTION 中 |
编译失败:“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 是否生效?
无需示波器或专用工具,仅靠在线监控即可验证:
- 在 PLC 编程软件中打开 FB 实例的变量表(如 TIA Portal 的“监控表”或 Codesys 的“Online > Watch”);
- 找到该实例下的
VAR_STATIC变量(通常带 “S” 图标或标注 “Static”); - 强制
Start := TRUE,观察internalState从0变1; - 清除强制,让
Start := FALSE,再次强制Start := TRUE; - 若
internalState保持1(而非跳回0),证明VAR_STATIC工作正常。
提示:某些 IDE 默认不刷新静态变量监控值,需勾选“循环读取”或按 F5 手动刷新。
八、高级注意:FB 实例复制与 VAR_STATIC 的继承性
当你复制一个已配置的 FB 实例(如 M1 复制为 M2):
M1和M2各自拥有完全独立的VAR_STATIC区域;- 修改
M1.internalState绝不影响M2.internalState; - 两者初始化值相同(都来自源声明中的
:=表达式),但运行后各自演化。
这正是 FB 封装性的体现:高内聚、低耦合。
九、总结:三句话掌握 VAR_STATIC
VAR_STATIC 是 FB 的记忆器官——它让逻辑具备时间维度。
只要 FB 实例不被销毁,VAR_STATIC 中的值就永不归零。
所有需要“记住上次发生了什么”的自动化逻辑,都应优先考虑 VAR_STATIC。

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