ST怎么写数组遍历清零:FOR i:=0 TO 9 DO Array[i] := 0; END_FOR;

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

在电气自动化编程中,使用结构化文本(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等),不能是REALSTRING
初始值 0 必须是常量或编译期可确定的表达式(如 MIN_INDEX
终值 9 必须 ≥ 初始值,否则循环体一次也不执行(这是标准行为,非Bug)
BY步长 省略 → 默认 1 若写 BY -1,则需用 DOWNTO 关键字

⚠️ 重要陷阱:TODOWNTO 决定循环方向

  • 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;

📌 执行效果说明

  • bExecuteFALSETRUE(即出现上升沿),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 初始化为 -1TO 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个,用状态机分多周期完成);
  • ❌ 不要尝试用 FILL 指令替代——ST中无标准FILL,各厂商实现不一,降低可移植性。

七、跨平台实操验证要点(必查清单)

平台 验证项 操作方式
西门子 TIA Portal ① 数组声明是否带[0..9]<br>② FOR 循环是否在OB1/FB中<br>③ 编译后查看“交叉引用”确认i类型为INT 在“块接口”中声明数组;右键FB→“编译”→检查诊断窗口
倍福 TwinCAT 3 ① 项目设置→“IEC 61131-3”→启用FOR循环<br>② 变量名区分大小写(iI “项目”→“属性”→勾选“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;

使用方法

  1. 将上述代码粘贴至PLC项目新建的FB中;
  2. 在主程序(如OB1)中实例化该FB:ClearInst(IN := StartButton);
  3. 连接输出 ClearInst.bCleared 到后续逻辑;
  4. 下载并运行——按下启动按钮,aData 瞬间归零。

FOR i:=0 TO 9 DO Array[i] := 0; END_FOR; 不是一行代码,而是一套经过工业现场千锤百炼的确定性、可验证、可移植的自动化控制契约。它不依赖硬件加速,不隐藏副作用,不制造耦合,只做一件事:把指定范围的内存单元,用确定的值,按确定的顺序,确定地写入。这正是电气自动化最核心的哲学——确定性压倒一切

评论 (0)

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

扫一扫,手机查看

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