在电气自动化编程中,使用结构化文本(Structured Text,简称 ST)语言编写数组操作是常见需求。其中,“遍历清零”是最基础、最频繁的数组初始化动作之一。看似简单的一行代码 FOR i:=0 TO 9 DO Array[i] := 0; END_FOR;,背后涉及变量声明规范、索引边界安全、循环执行机制、编译器行为差异、运行时异常防护等多个关键实践要点。本文将从零开始,手把手带你写出真正可靠、可复用、符合IEC 61131-3标准且适配主流PLC平台(如西门子S7-1200/1500、倍福TwinCAT、罗克韦尔Logix、施耐德Unity Pro) 的数组清零逻辑。
一、先搞懂:ST里的“数组”到底是什么?
ST语言中的数组不是内存地址连续的“C风格指针块”,而是带类型、带维度、带边界的命名数据集合。声明时必须显式指定:
- 元素数据类型(如
INT,REAL,BOOL,STRING[16]) - 维度数量(一维、二维、三维…)
- 每一维的下标范围(起始索引和终止索引)
✅ 正确声明(推荐写法,显式定义下界):
VAR
MyArray : ARRAY[0..9] OF INT; // 10个元素,索引从0到9(含)
END_VAR
❌ 危险写法(隐式下界,不同PLC解释不一致):
VAR
BadArray : ARRAY[10] OF INT; // ❌ 西门子视作[0..9],倍福可能视作[1..10],罗克韦尔默认[0..9]但需确认项目设置
END_VAR
关键结论:永远使用
[下界..上界]显式写法。ARRAY[0..9]= 10个元素;ARRAY[1..10]= 也是10个元素,但索引从1开始。二者逻辑等价,但必须与后续FOR循环索引完全匹配。
二、FOR循环语法精解:为什么 FOR i:=0 TO 9 DO ... END_FOR; 是黄金模板?
ST中FOR循环的标准语法为:
FOR <控制变量> := <初始值> TO <终值> [BY <步长>] DO
<循环体语句>
END_FOR;
我们逐字段验证 FOR i:=0 TO 9 DO Array[i] := 0; END_FOR;:
| 字段 | 值 | 是否合规 | 说明 |
|---|---|---|---|
控制变量 i |
i |
✅ | 必须是整型变量(INT, DINT, USINT等),不能是REAL或STRING |
| 初始值 | 0 |
✅ | 必须是常量或编译期可确定的表达式(如 MIN_INDEX) |
| 终值 | 9 |
✅ | 必须 ≥ 初始值,否则循环体一次也不执行(这是标准行为,非Bug) |
| BY步长 | 省略 → 默认 1 |
✅ | 若写 BY -1,则需用 DOWNTO 关键字 |
⚠️ 重要陷阱:TO 和 DOWNTO 决定循环方向
FOR i:=0 TO 9→ i 依次取0,1,2,...,9(升序)FOR i:=9 DOWNTO 0→ i 依次取9,8,7,...,0(降序)FOR i:=0 TO 9 BY 2→ i 取0,2,4,6,8(跳步,共5次)
✅ 安全清零的本质:确保每个有效索引都被访问且仅访问一次。因此,TO 方向 + 精确匹配数组上下界,是最直观、最不易出错的选择。
三、完整可运行示例:从声明到清零,一步不落
以下是一个可在任何IEC 61131-3兼容PLC中直接编译的完整功能块(FB)代码,实现安全清零并验证结果:
FUNCTION_BLOCK FB_ClearIntArray
VAR_INPUT
bExecute : BOOL; // 触发信号:上升沿触发清零
END_VAR
VAR_OUTPUT
bDone : BOOL; // 清零完成标志(单周期脉冲)
nCount : INT; // 实际清零元素个数(用于调试)
END_VAR
VAR
MyArray : ARRAY[0..9] OF INT; // 待清零的10元整型数组
i : INT; // 循环计数器(必须声明为INT或更大整型)
rTrig : R_TRIG; // 上升沿检测(内置功能块)
END_VAR
// 1. 检测执行信号上升沿
rTrig(CLK := bExecute);
IF rTrig.Q THEN
// 2. 执行清零循环
nCount := 0;
FOR i := 0 TO 9 DO
MyArray[i] := 0;
nCount := nCount + 1;
END_FOR;
bDone := TRUE; // 置位完成标志
ELSE
bDone := FALSE;
END_IF;
📌 执行效果说明:
- 当
bExecute从FALSE→TRUE(即出现上升沿),rTrig.Q置位一个扫描周期; - 此周期内,
FOR循环执行10次,MyArray[0]至MyArray[9]全部赋值为0; nCount计数器同步累加至10,可用于HMI显示或故障诊断;bDone输出一个扫描周期的TRUE脉冲,供下游逻辑使用(如启动下一工序)。
四、进阶技巧:让清零更灵活、更健壮
▶ 技巧1:用常量替代硬编码数字,提升可维护性
VAR_GLOBAL CONSTANT
ARRAY_SIZE : INT := 10;
MIN_INDEX : INT := 0;
MAX_INDEX : INT := ARRAY_SIZE - 1;
END_VAR
// 在FB中:
FOR i := MIN_INDEX TO MAX_INDEX DO
MyArray[i] := 0;
END_FOR;
✅ 优势:修改数组大小时,只需改 ARRAY_SIZE,循环范围自动适配,杜绝 TO 9 写成 TO 10 导致越界错误。
▶ 技巧2:支持任意整型数组的通用清零函数(FC)
FUNCTION FC_ClearArray : BOOL
VAR_INPUT
pArray : POINTER TO INT; // 指向数组首地址的指针(需谨慎使用)
nLength : INT; // 数组长度(元素个数)
END_VAR
VAR
i : INT;
END_VAR
IF (pArray <> 0) AND (nLength > 0) THEN
FOR i := 0 TO nLength - 1 DO
pArray^[i] := 0; // 通过指针解引用赋值
END_FOR;
FC_ClearArray := TRUE;
ELSE
FC_ClearArray := FALSE;
END_IF;
⚠️ 注意:指针操作需PLC支持并启用“指针使能”选项(如TIA Portal中需勾选“Enable pointer operations”),且存在类型安全风险,新手慎用。推荐优先使用技巧1的常量方案。
▶ 技巧3:添加越界保护(防御式编程)
即使声明了 ARRAY[0..9],若误将 i 初始化为 -1 或 TO 10,部分PLC会报运行时错误(如西门子“Array index out of bounds”)。加入保护更稳妥:
FOR i := 0 TO 9 DO
IF (i >= 0) AND (i <= 9) THEN // 显式检查,编译器通常会优化掉
MyArray[i] := 0;
END_IF;
END_FOR;
✅ 实际价值:在复杂嵌套循环或动态索引计算场景中,此检查能防止静默错误(某些PLC越界时读取随机内存值,不报错但逻辑失控)。
五、常见错误及修正对照表
| 错误代码 | 错误类型 | 后果 | 正确写法 |
|---|---|---|---|
FOR i := 1 TO 10 DO Array[i] := 0; END_FOR; |
索引越界(若Array声明为[0..9]) |
西门子:运行时报错;倍福:可能静默失败 | FOR i := 0 TO 9 DO Array[i] := 0; END_FOR; |
FOR i := 0 TO 9 DO Array[i] := 0; |
缺少 END_FOR; |
编译失败:语法错误 | 补全 END_FOR; |
FOR i := 0.0 TO 9.0 DO ... |
控制变量非整型 | 编译失败:i 必须为整数类型 |
改为 i : INT; |
FOR i := 0 TO SIZEOF(Array) DO ... |
SIZEOF 返回字节数,非元素数 |
循环次数错误(如INT数组10个元素→SIZEOF=20) |
用 UPPER_BOUND(Array, 1) 或常量 |
Array[0..9] := 0; |
尝试对整个数组赋值 | 编译失败:ST不支持数组整体赋值(除初始化外) | 必须用循环逐个赋值 |
💡 提示:多数现代PLC支持
UPPER_BOUND(Array, Dimension)函数获取上界。例如:
FOR i := LOWER_BOUND(MyArray, 1) TO UPPER_BOUND(MyArray, 1) DO
可实现完全动态适配数组声明,无需硬编码数字,是高级应用首选。
六、性能真相:FOR循环清零慢吗?要不要优化?
在典型PLC(1ms~10ms扫描周期)中,清零100个INT耗时约 0.5~2 μs(微秒),远低于1ms扫描时间的0.1%。结论明确:
- ✅ 对于 ≤ 1000 元素的常规数组,
FOR循环清零无需优化; - ⚠️ 若需清零10万+元素(如大数据缓存),才考虑:
- 使用PLC厂商专用指令(如西门子
MOVE_BLK块移动,底层调用DMA); - 或分批处理(每周期清零100个,用状态机分多周期完成);
- 使用PLC厂商专用指令(如西门子
- ❌ 不要尝试用
FILL指令替代——ST中无标准FILL,各厂商实现不一,降低可移植性。
七、跨平台实操验证要点(必查清单)
| 平台 | 验证项 | 操作方式 |
|---|---|---|
| 西门子 TIA Portal | ① 数组声明是否带[0..9]<br>② FOR 循环是否在OB1/FB中<br>③ 编译后查看“交叉引用”确认i类型为INT |
在“块接口”中声明数组;右键FB→“编译”→检查诊断窗口 |
| 倍福 TwinCAT 3 | ① 项目设置→“IEC 61131-3”→启用FOR循环<br>② 变量名区分大小写(i ≠ I) |
“项目”→“属性”→勾选“Enable FOR loop”;代码中统一用小写i |
| 罗克韦尔 Studio 5000 | ① 数据类型必须为DINT(避免INT溢出)<br>② 使用FOR指令块(非文本ST)或确保ST编辑器启用 |
新建标签时选择DINT;ST编辑器右键→“Properties”→勾选“Allow FOR loops” |
| 施耐德 EcoStruxure | ① ARRAY声明后必须点击“Apply”生效<br>② END_FOR 后不能跟分号; |
声明后按回车;删除多余分号 |
✅ 统一建议:所有平台均优先采用 VAR 区声明 + FOR i:=0 TO N-1 模式,兼容性最高。
八、终极模板:复制即用的安全清零代码块
// === 全局常量(放在Global Variables或Library中)===
VAR_GLOBAL CONSTANT
MY_ARRAY_SIZE : INT := 10;
MY_ARRAY_MIN : INT := 0;
MY_ARRAY_MAX : INT := MY_ARRAY_SIZE - 1;
END_VAR
// === 功能块(FB_ClearMyArray)===
FUNCTION_BLOCK FB_ClearMyArray
VAR_INPUT
bTrigger : BOOL; // 输入:清零触发信号(BOOL)
END_VAR
VAR_OUTPUT
bCleared : BOOL; // 输出:清零完成(单周期脉冲)
END_VAR
VAR
aData : ARRAY[MY_ARRAY_MIN..MY_ARRAY_MAX] OF INT; // 数据数组
i : INT; // 循环变量
rTrig : R_TRIG; // 上升沿检测
END_VAR
// 主逻辑
rTrig(CLK := bTrigger);
IF rTrig.Q THEN
FOR i := MY_ARRAY_MIN TO MY_ARRAY_MAX DO
aData[i] := 0;
END_FOR;
bCleared := TRUE;
ELSE
bCleared := FALSE;
END_IF;
使用方法:
- 将上述代码粘贴至PLC项目新建的FB中;
- 在主程序(如OB1)中实例化该FB:
ClearInst(IN := StartButton);; - 连接输出
ClearInst.bCleared到后续逻辑; - 下载并运行——按下启动按钮,
aData瞬间归零。
FOR i:=0 TO 9 DO Array[i] := 0; END_FOR; 不是一行代码,而是一套经过工业现场千锤百炼的确定性、可验证、可移植的自动化控制契约。它不依赖硬件加速,不隐藏副作用,不制造耦合,只做一件事:把指定范围的内存单元,用确定的值,按确定的顺序,确定地写入。这正是电气自动化最核心的哲学——确定性压倒一切。

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