返回
SCL PLC 编程教程第五篇 循环语句:FOR、WHILE、REPEAT 在 PLC 里到底怎么用,哪些场景最有价值


前面几篇,我们已经把 SCL 的基础地基搭起来了:

第一篇讲了 SCL 是什么,它适合做什么。
第二篇认识了编程界面和最基础的程序结构。
第三篇把变量、数据类型、地址概念理清了。
第四篇又把 IF、ELSIF、ELSE、CASE 这些条件判断讲透了。

到了这里,很多人会开始有一个新需求:

有些逻辑,不是只对一个变量做一次判断,而是要对一整组数据反复处理。
比如:

一次性清除 50 个报警位
批量初始化一组参数
遍历 20 个传感器状态
检查数组里有没有异常值
统计一批信号中有几个成立
求一组采样值的平均值
在一串数据里查找某个目标

这时候,如果你还完全用一条一条赋值、一条一条判断的方式来写,当然也能写,但会越来越笨重。

于是,循环就派上用场了。

所以你可以先把循环理解成一句非常实在的话:

循环,就是让程序按你设定的规则,把同一类处理重复执行多次。

听起来很简单,但它的价值非常大。
尤其在 SCL 里,循环是让程序从“单点判断”走向“批量处理”的一个关键台阶。

一、先把最重要的一句话记住:PLC 里的循环,不是拿来“等时间过去”的

这一点必须先讲,而且要讲得非常明确。

很多刚学循环的人,会不自觉地把编程里的循环理解成:

“让程序一直反复执行,直到过一会儿再继续。”

这种理解在一些普通软件编程场景里,可能还勉强能联想到某些等待机制。
但在 PLC 里,如果你用这种思路理解循环,很容易出事。

在 PLC 程序中,尤其是在主扫描逻辑里,循环最常见的本质是:

在一次扫描周期内,快速地把一批重复处理做完。

也就是说,FOR、WHILE、REPEAT 通常都不是“拖时间”的工具。
它们更像是“批量处理工具”。

比如你写:

FOR i := 1 TO 10 DO
    Alarm[i] := FALSE;
END_FOR;

这不是说 PLC 会一个周期清一个报警,清十个周期。
而是说:在这一次程序执行中,它很快就把 1 到 10 号报警位都处理完了。

所以你一定要把这个观念刻进脑子里:

PLC 里的循环,主要是为了批量处理数据,不是为了做延时。

延时要用定时器、时间逻辑、状态机,不要拿循环硬等。

这点后面我还会反复提醒,因为这是循环部分最容易踩的大坑之一。

二、为什么 PLC 工程师需要学循环

有些人会问:

“我平时写梯形图也没怎么用循环,项目不也做了吗?”

这话没错。
不是所有 PLC 项目都必须大量使用循环。
但一旦你遇到下面这些场景,循环会明显提高程序的条理性和效率。

1. 处理成组数据时更自然

例如:

32 路报警位
16 路输入状态
20 个温度点
一组配方参数
一段历史缓存区

如果你逐条写:

Alarm[1] := FALSE;
Alarm[2] := FALSE;
Alarm[3] := FALSE;
...
Alarm[32] := FALSE;

当然可以,但很笨重,也不利于修改。

如果用循环,逻辑马上就收紧了。

2. 更适合写“规则一致”的逻辑

例如你要检查 20 个模拟量中有没有超限值。
每一路判断规则都一样:只要大于上限就报警。

这时候,循环就特别合适。

3. 更容易和数组、结构化数据配合

SCL 一旦进入数组、数据表、批量参数处理的阶段,循环几乎是天然搭档。
不学循环,你后面很多内容会写得很别扭。

4. 代码更紧凑,维护成本更低

不是说代码越少越高级,而是同一类重复处理,应该尽量写得更有结构。
循环在这方面非常有价值。

所以从工程成长角度看,循环不是“可有可无的高级技巧”,而是你进入批量数据处理、模块化处理之后很重要的能力。

三、SCL 里常见的三类循环

SCL 中最常见的循环结构主要有三类:

FOR
WHILE
REPEAT

它们都能实现“重复执行”,但适合的场景不完全一样。

你可以先粗略这样理解:

FOR

适合已知重复次数的场景。

例如:

从 1 到 10 依次处理
遍历数组 1 到 20
清零某一段固定长度数据
统计一组固定数量的信号
WHILE

适合在某个条件成立时一直继续执行的场景。

例如:

当索引还没到边界时继续找
当还没找到目标且还在有效范围内时继续扫描

但要特别小心,WHILE 如果条件控制不好,很容易出现死循环风险。

REPEAT

适合先执行一次,再判断是否继续的场景。

它和 WHILE 的差别在于:
WHILE 是先判断,再执行;
REPEAT 是先执行,再判断。

在 PLC 项目里,REPEAT 没有 FOR 那么常用,但在某些“至少执行一次”的逻辑里也有价值。

四、FOR 循环是什么,为什么它最适合 PLC 初学者先掌握

如果说三类循环里,哪一种最值得 PLC 工程师优先吃透,那通常就是 FOR。

因为它最直观、最安全、工程上也最常用。

FOR 的核心思路非常简单:

让某个计数变量按设定范围一步一步变化,并在每一步执行相同逻辑。

最基础的写法是:

FOR i := 起始值 TO 结束值 DO
    语句;
END_FOR;

例如:

FOR i := 1 TO 10 DO
    Alarm[i] := FALSE;
END_FOR;

这段代码的意思是:

让 i 从 1 变化到 10;
每变化一次,就把对应的 Alarm[i] 清零。

你可以把 FOR 理解成一个“自动编号器”。
程序不用你一个一个写 1、2、3、4,而是自动按范围帮你跑一遍。

这就是 FOR 的本质。

五、FOR 循环里最重要的角色:索引变量

FOR 循环里一定会有一个很重要的变量,通常叫:

i
j
k
Index
AlarmNo
ChannelNo

它的作用就是在每一轮循环里,表示“当前轮到第几个”。

例如:

FOR i := 1 TO 8 DO
    InputOK[i] := TRUE;
END_FOR;

在这段程序里:

第一轮时 i = 1
第二轮时 i = 2
第三轮时 i = 3
一直到 i = 8

所以 InputOK[i] 实际上就会依次变成:

InputOK[1]
InputOK[2]
InputOK[3]
...
InputOK[8]

这就是为什么 FOR 特别适合数组处理。

初学者要注意一点:
这个索引变量不是随便写着玩的,它必须和你的数组范围、数据结构范围一致。

否则很容易出现越界、错位或者逻辑不对的情况。

六、FOR 在 PLC 里最典型的几个场景
1. 批量清零或初始化

这是 FOR 最基础、也最常见的用途之一。

例如系统复位时,要把 20 个报警标志全部清掉:

IF ResetPB THEN
    FOR i := 1 TO 20 DO
        Alarm[i] := FALSE;
    END_FOR;
END_IF;

这种写法比一条一条清楚很多。

2. 批量判断

例如检查 10 路温度是否有超上限:

FOR i := 1 TO 10 DO
    IF TempPV[i] > TempHighLimit[i] THEN
        TempHighAlarm[i] := TRUE;
    ELSE
        TempHighAlarm[i] := FALSE;
    END_IF;
END_FOR;

这就很像实际工程逻辑了。

你会发现,一旦规则一致,FOR 就能自然地把同类处理批量展开。

3. 统计数量

例如统计 16 路输入中,有多少路为 TRUE:

ActiveCount := 0;

FOR i := 1 TO 16 DO
    IF SensorOK[i] THEN
        ActiveCount := ActiveCount + 1;
    END_IF;
END_FOR;

这个场景非常实用。
很多计数、统计、汇总逻辑,都可以这么做。

4. 查找第一个满足条件的位置

例如在一组产品槽位中,找第一个空位:

FirstEmptyPos := 0;

FOR i := 1 TO 12 DO
    IF NOT SlotUsed[i] THEN
        FirstEmptyPos := i;
    END_IF;
END_FOR;

不过这里要注意,这段代码会一直扫到最后。
如果你只是想找第一个空位,后面我们还会讲更细的优化思路。

5. 求和、求平均值

例如对 8 个采样值求平均:

SumValue := 0.0;

FOR i := 1 TO 8 DO
    SumValue := SumValue + SampleValue[i];
END_FOR;

AverageValue := SumValue / 8.0;

这种数据处理在模拟量、采样平滑、统计分析中都很常见。

七、FOR 虽然好用,但有两个工程问题必须警惕
第一个问题:循环次数不能无脑放大

很多人刚学会循环,会有一种冲动:

既然能循环,那我直接从 1 到 10000,甚至更多,不就行了?

理论上某些场景可能可以,
但 PLC 不是普通办公电脑程序,它的扫描周期、实时性都要考虑。

如果你在主循环里做一个非常大的 FOR,每一轮里面又有大量计算、比较、字符串处理、通信解析,那就可能带来这些后果:

扫描周期明显变长
响应变慢
程序抖动
某些动作延后
极端情况下影响设备实时性

所以 FOR 不是不能大,而是要有概念:

循环次数乘以循环体复杂度,才是真正决定负担的关键。

也就是说,不只是看你循环几次,还要看每次里面做了多重的工作。

第二个问题:不要把 FOR 当延时器

例如有人会想:

“我让 FOR 从 1 跑到 50000,不就能等一会儿了吗?”

这非常不建议。

因为:

这样做的时间并不稳定
和 PLC 性能、程序负载有关
不同设备、不同程序状态下效果都可能不一样
会浪费扫描资源
本质上是错误使用循环

真正需要延时,请用:

TON
TOF
TP
时间比较
状态机加时间条件

不要拿 FOR 做假延时。
这在工程里是一个很不好的习惯。

八、WHILE 是什么,它和 FOR 最大的区别在哪儿

WHILE 的基本思路是:

只要条件成立,就继续执行。

基础写法如下:

WHILE 条件 DO
    语句;
END_WHILE;

例如思路上:

当索引还没超过上限时,就继续检查数组内容。

WHILE 和 FOR 最大的区别在于:

FOR 是“次数基本已知”

比如从 1 到 20,很明确。

WHILE 是“是否继续,由条件决定”

比如只要没找到目标且索引还在范围内,就继续。

所以 WHILE 更灵活,但也更危险。

为什么危险?
因为 FOR 至少天然有一个明确边界,而 WHILE 如果条件一直成立,就可能一直循环下去。

这就是 PLC 工程里使用 WHILE 必须格外谨慎的原因。

九、WHILE 在 PLC 里适合什么场景

虽然 WHILE 需要小心,但不是不能用。
它在某些场景下还是有价值的。

1. 条件驱动的查找

例如你想从数组头开始找某个目标,只要没找到并且索引还没越界,就继续:

i := 1;
Found := FALSE;

WHILE (i <= 20) AND (NOT Found) DO
    IF ProductCode[i] = TargetCode THEN
        Found := TRUE;
        FoundPos := i;
    ELSE
        i := i + 1;
    END_IF;
END_WHILE;

这个例子就很典型。
它不是固定跑满 20 次,而是满足条件就继续,不满足就停。

也就是说,如果第 3 个位置就找到了,那就可以提前结束。
这比某些“无脑从头扫到尾”的逻辑更有效率。

2. 根据变化条件逐步推进

有些逻辑不是严格已知次数,而是“只要条件成立就继续推进”,这种时候 WHILE 会比较自然。

但这种场景在 PLC 主控逻辑里没有 FOR 那么常见。
多数标准批量处理,FOR 往往已经够用了。

所以从工程习惯来说:

如果 FOR 能解决,很多时候优先考虑 FOR;
只有当“继续条件”比“固定次数”更自然时,再考虑 WHILE。

十、WHILE 最危险的地方:死循环

这必须单独讲,而且要讲得透。

所谓死循环,就是循环条件始终满足,程序一直出不来。
这在 PLC 里是非常危险的。

例如下面这种思路就有风险:

i := 1;

WHILE i <= 10 DO
    Alarm[i] := FALSE;
END_WHILE;

这里的问题是:
你没有让 i 发生变化。

那程序会一直看到 i <= 10 成立,于是永远不停。

这就是典型死循环。

所以你以后写 WHILE 时,必须养成一个近乎机械的检查习惯:

第一,循环条件是什么

比如 i <= 20

第二,循环体里谁在推动条件变化

比如 i := i + 1

第三,是否一定存在退出路径

比如找到目标会退出,或者 i 增加到边界后退出

如果这三件事不清楚,WHILE 就不要轻易写。

一句很实在的话:

WHILE 最怕的,不是写复杂,而是忘了让条件走向结束。

十一、REPEAT 是什么,它和 WHILE 有什么差别

REPEAT 的核心特点是:

先执行一次,再判断要不要继续。

基础写法通常是:

REPEAT
    语句;
UNTIL 条件
END_REPEAT;

这和 WHILE 的区别是:

WHILE:先判断条件,再决定要不要进循环
REPEAT:先执行循环体,再判断是否满足结束条件

所以 REPEAT 适合这种逻辑:

不管怎样,先做一轮,然后根据结果决定是否继续。

这类场景在 PLC 中不是最主流,但在某些需要“至少执行一次”的处理中会用到。

十二、REPEAT 适合哪些场景

举一个容易理解的思路:

假设你有一组数据,想从头开始检查,直到找到第一个不合格项。
而检查动作本身至少要先做一次。

这类逻辑理论上可以用 REPEAT。

不过在 PLC 实际工程中,REPEAT 的使用频率通常低于 FOR 和 WHILE。
原因很简单:

大多数批量处理,FOR 更直观
大多数条件驱动处理,WHILE 更常见
REPEAT 虽然有它的价值,但工程师用得相对少一些

所以从学习顺序来说:

先把 FOR 吃透,再理解 WHILE,最后把 REPEAT 当作补充。

这是更符合 PLC 工程实践的路径。

十三、循环和数组为什么几乎是天然搭档

这一点后面你学数组时会更强烈感受到,但现在先建立直觉很重要。

数组的本质就是:

把一组同类型数据按编号放在一起。

例如:

Alarm[1..20]
TempPV[1..8]
SensorOK[1..16]
RecipeValue[1..50]

而循环的本质就是:

按编号一轮一轮处理。

所以数组和循环一结合,就非常自然。

例如:

FOR i := 1 TO 20 DO
    IF AlarmTrig[i] THEN
        AlarmAct[i] := TRUE;
    END_IF;
END_FOR;

这种写法的感觉,和你逐条写 20 段判断完全不同。
它更像是在表达一个统一规则:

“对这 20 个对象,都执行同样的判断。”

这就是结构化编程的味道。

十四、循环里能不能套 IF,IF 里能不能再套循环

当然可以。
而且在工程里很常见。

例如:

FOR i := 1 TO 10 DO
    IF TempPV[i] > TempHighLimit[i] THEN
        TempHighAlarm[i] := TRUE;
    ELSE
        TempHighAlarm[i] := FALSE;
    END_IF;
END_FOR;

这里就是循环里套判断。

反过来,也可能是先满足某个条件,再进入循环:

IF ResetPB THEN
    FOR i := 1 TO 20 DO
        AlarmAct[i] := FALSE;
    END_FOR;
END_IF;

这也很常见。

甚至某些复杂场景里,还会有双层循环。
比如二维数据处理、行列扫描等。

不过这里要注意一个工程原则:

结构可以嵌套,但嵌套越深,越要警惕可读性和扫描负担。

不是不能写,而是要心里有数。

十五、一个非常实用的工程示例:报警批量复位

我们来看一个典型 PLC 场景。
系统里有 32 路报警位,按下复位按钮后,需要把这些报警活动位全部清掉。

用 SCL 可以写成:

IF ResetPB THEN
    FOR i := 1 TO 32 DO
        AlarmAct[i] := FALSE;
    END_FOR;
END_IF;

这段程序的好处很明显:

第一,逻辑一眼就懂。
第二,以后如果报警数量从 32 改成 40,修改很方便。
第三,不容易写漏。
第四,比一条一条赋值更整洁。

这就是循环的工程价值。
不是炫技,而是让重复逻辑更有结构。

十六、一个更接近现场的数据处理示例:查找第一个故障通道

假设有 16 路设备状态信号,你想找到第一个故障通道编号,便于 HMI 显示。

可以这样写:

FirstFaultNo := 0;

FOR i := 1 TO 16 DO
    IF NOT DeviceOK[i] THEN
        FirstFaultNo := i;
    END_IF;
END_FOR;

不过这里有一个细节。
这段程序最后得到的,不一定是“第一个故障”,而可能是“最后一个故障位置”,因为它会一直扫到末尾,后面又把前面的结果覆盖了。

如果你真想找第一个故障位置,写法就要更有意识。
比如可以加一个条件限制:

FirstFaultNo := 0;

FOR i := 1 TO 16 DO
    IF (FirstFaultNo = 0) AND (NOT DeviceOK[i]) THEN
        FirstFaultNo := i;
    END_IF;
END_FOR;

这时候一旦第一个故障位置被记录,后面就不会再覆盖。

这个例子特别值得你体会。
因为它说明了:

循环只是工具,结果对不对,关键还是逻辑细节。

十七、循环最适合做“规则统一”的事情,不适合做“每一步都完全不同”的事情

比如你要处理 20 路温度,每一路判断规则一样:
大于上限报警,小于下限报警。
这就很适合循环。

但如果你有 10 个步骤,而每一步的动作、时序、条件、跳转规则都完全不同,那就不一定适合用循环一把梭。
这种时候,往往 CASE、状态机更合适。

所以你要建立一个判断标准:

当对象很多,但处理规则一致时

优先考虑循环。

当步骤很多,但每一步逻辑差异很大时

更可能适合状态分支,而不是纯循环。

这能帮助你避免“为了用循环而用循环”。

十八、PLC 里使用循环时,为什么要特别有“边界意识”

边界意识是写循环时非常核心的工程习惯。

比如你的数组是 Alarm[1..20],那循环就应该非常明确地控制在 1 到 20 之间。
如果你不小心写成:

FOR i := 1 TO 25 DO

那后面 21 到 25 的访问就可能有风险。

同样,如果你的数据是从 0 开始编号,而你却按 1 开始扫,也会错位。

所以每次写循环,你都应该先想清楚:

起始下标是多少
结束下标是多少
数组定义范围是什么
有没有可能越界
有没有可能漏掉最后一个元素

这个习惯特别重要。
因为循环一旦写错,不是只错一个点,而是可能一口气把一批处理都带偏。

十九、循环会不会影响 PLC 扫描周期

会。
但不是说一写循环就一定有问题,而是要看:

循环次数
循环体复杂度
执行频率
CPU 性能
是否在主循环中高频执行

例如:

一个每扫都执行的 5000 次 FOR,里面还做字符串处理、复杂运算、通信解析,那当然要警惕。

但一个每次复位时才执行一次、只清 20 个 BOOL 位的 FOR,通常就很轻。

所以正确态度不是“害怕循环”,而是:

对循环的开销有概念。

工程上要学会区分:

高频执行逻辑
条件触发逻辑
大批量处理逻辑
可分批执行逻辑

这样你才会越来越成熟,而不是一听循环就怕,或者一会循环就乱用。

二十、初学者写循环时最常见的几个错误
第一,拿循环做延时

这是前面已经强调过的大坑。
一定不要这么用。

第二,循环条件没有退出机制

尤其是 WHILE,最容易死循环。

第三,下标范围写错

数组定义是 1 到 10,你却扫到 11 或者从 0 开始。

第四,循环里修改了不该修改的东西

比如本来只是想找目标,结果把控制状态在每轮都反复覆盖。

第五,结果变量没有先初始化

例如求和前不先清零,统计前不先归零,查找位置前不先设默认值。
这会导致逻辑看起来能跑,但结果越来越怪。

第六,没想清楚是要“找到第一个”还是“统计全部”还是“处理全部”

这三种需求虽然都可能用循环,但写法思路不一样。
这个区分非常重要。

二十一、一个很实用的学习建议:学循环时,不要一开始就追求复杂嵌套

很多人一学会 FOR,就想立刻上:

双层循环
条件嵌套
字符串数组处理
动态查找
大型数据表遍历

这当然不是以后不能学,而是现在没必要太快。

你现在最重要的是先练熟这几类最基础的循环任务:

清零一组数据
检查一组状态
统计成立数量
求和求平均
查找第一个满足条件的位置

只要这几类思路顺了,你对循环就真正有感觉了。
后面再往复杂方向走,会稳很多。

二十二、一个你以后会越来越常用的思路:循环不是“为了省代码”,而是“为了表达一类处理规则”

这句话特别值得记住。

很多人一开始学循环,会觉得它的好处只是“代码变短了”。
但那只是表面。

循环真正的价值,是它在表达一种规则:

这一组对象,都要接受同样的处理。

比如:

这 16 路输入都要做状态检查
这 20 路报警都要判断触发
这 8 个采样点都要参与平均值计算
这 32 个标志位都要在复位时清零

当你用循环写出来时,程序本身就在说:

“这不是零散的 32 条语句,而是一条统一规则作用于 32 个对象。”

这就是结构化思维。
这也正是 SCL 相比单纯图形堆砌更有优势的地方之一。

二十三、这一篇最后,给你一个很实在的工程判断标准

以后只要你在项目里遇到某段逻辑,脑子里可以先问自己这几个问题:

第一,这是不是对一组对象做同样的处理

如果是,循环往往值得考虑。

第二,这组对象数量是否明确

如果数量明确,优先考虑 FOR。

第三,是否存在“只要条件成立就继续”的需求

如果有,再考虑 WHILE。

第四,这段循环是否可能带来明显的扫描负担

如果有,先评估,再决定是否拆分、优化或改写。

第五,我是不是误把循环当成了延时工具

如果是,立即停下来,换成定时器或时间逻辑。

只要你养成这几个判断习惯,循环就会从“有点怕的东西”,变成“很顺手的工具”。

小结

这一篇你最应该记住这些核心点:

循环的本质,是按规则重复执行同类处理。
在 PLC 里,循环主要是批量处理工具,不是延时工具。
FOR 适合已知次数的重复处理,是 PLC 工程里最常用、也最适合先掌握的循环。
WHILE 适合条件驱动的继续执行,但必须特别注意退出条件,否则容易死循环。
REPEAT 是先执行后判断,适合至少要执行一次的场景,但使用频率通常低于 FOR。
循环和数组几乎是天然搭档,特别适合批量判断、初始化、统计、查找、求和等任务。
使用循环时,必须有边界意识、扫描周期意识和退出机制意识。
真正好的循环,不只是代码短,而是能清楚表达“同一类规则作用于一组对象”。
下载资料前请先绑定手机号码