Iteration is a core concept in programming, and Structured Text (ST) provides a variety of loop types to implement it. However, when working with TwinCAT, understanding the behavior of loops—especially the FOR
-loop—can be trickier than it seems. This article offers a concise introduction to TwinCAT loops and their peculiarities.
FOR-Loop
The FOR
-loop is an entry-controlled loop that executes statements repeatedly until the <counter> meets the specified end condition:
- Without a <step size expression>: Loop stops when the <counter> is greater than the <end condition expression>.
- With a positive <step size expression>: Loop stops when the <counter> exceeds the <end condition expression>.
- With a negative <step size expression>: Loop stops when the <counter> is below the <end condition expression>.
The following components must be or return an integer:
- <counter>, <initial expression>, <end condition expression>, and <step size expression>:
ANY_BIT
:BYTE
,WORD
,DWORD
,LWORD
,__XWORD
ANY_INT
:SINT
,USINT
,INT
,UINT
,DINT
,UDINT
,LINT
,ULINT
,__XINT
, or__UXINT
After each execution of the loop’s <instructions>, the <counter> is automatically incremented by the value of the <step size expression>. If no step size is specified, the default step size is 1
.
EXIT
-statement is used to jump out of a loop.CONTINUE
-statement interrupts the current iteration and proceeds with the next iteration of the loop.Syntax
FOR
<counter> :=
<initialization expression> TO
<end condition expression> [BY
<step size expression>] DO
<instruction>
[CONTINUE;
]
[EXIT;
]
[<instructions>]
END_FOR
Flowchart
OR
. The <end condition expression> is evaluated once per cycle without <step size expression> and twice per cycle with <step size expression>.Examples
Simple Examples
VAR
i :LINT;
squares :ARRAY[0..20] OF LINT;
END_VAR
VAR
FIRST_SQUARE :LINT := LOWER_BOUND(squares,1);
LAST_SQUARE :LINT := UPPER_BOUND(squares,1);
END_VAR
FOR i := FIRST_SQUARE TO LAST_SQUARE DO
squares[i] := i*i;
END_FOR
Example with Pointer Operations
VAR
foo : T_MaxString := 'Hello foo';
bar : POINTER TO BYTE;
baz : PVOID;
inverseFoo : T_MaxString;
END_VAR
(* clean the memory before we start *)
Tc2_System.MEMSET(ADR(inverseFoo),0,TO_UDINT(Tc2_Standard.LEN(foo)+1));
(* store the inverse content of foo in inverseFoo *)
FOR baz := bar := ADR(foo) + TO___UXINT(MAX(Tc2_Standard.LEN(foo)-1,0)) TO ADR(foo) BY -1 DO
bar := baz;
inverseFoo := Tc2_Standard.CONCAT(inverseFoo, CHR(bar^));
END_FOR
Example with a Reference
VAR
foo :REFERENCE TO UDINT;
baz :ARRAY[0..25] OF UDINT;
bar :UDINT := TO_UDINT(LOWER_BOUND(baz,1));
barz :UDINT := 12;
END_VAR
(* The array baz will only once initialize until the value of bar is set to 0 again *)
FOR foo REF= bar TO TO_UDINT(UPPER_BOUND(baz,1)) DO
baz[foo] := barz * foo;
END_FOR
Example with EXIT
and CONTINUE
VAR
actBit :DINT;
bar :WORD;
highestSetBit :DINT(-1..15);
END_VAR
(* For loop finds the number of the highest set bit *)
FOR actBit := 15 TO 0 BY -1 DO
IF (bar = 0) THEN
highestSetBit := -1;
EXIT;
END_IF
IF ((bar AND SHL(1,actBit)) = 0) THEN
CONTINUE;
END_IF
highestSetBit := actBit;
EXIT;
END_FOR
Example with Complex Expressions
VAR
values :ARRAY[0..20] OF WORD;
a,b,c,d :WORD;
END_VAR
(*
for loop that calculates the fibonacci sequence up to 100.
- the init expression is called only once
- the expression (a := c := d := 0) returns 0 so b is initailized with 1 and b is the counter varibale
- the condition expression is called twice per cycle
- the expression (0 AND (c := (d := d + 1) / 2)) returns 0 because of the bitwise AND 0 but it increments d by two and c by one every loop cycle because it is called twice per loop cycle
- the step size expression will be called three times per loop cycle a is the (c-1)th fibonacci number and b is the (c+1)th fibonacci number
*)
FOR b := 1 + (a := c := d := 0) TO 100 * 3 + (0 AND (c := (d := d + 1) / 2)) BY (a := b - a) DO
values[c - 1] := a;
END_FOR
WHILE-Loop
The WHILE
-loop is an entry-controlled loop that executes statements repeatedly until the <condition expression> returns FALSE
.
EXIT
-statement is used to jump out of a loop.CONTINUE
-statement interrupts the current iteration and proceeds with the next iteration of the loop.Syntax
WHILE
<condition expression> D
<instruction>
[CONTINUE;
]
[EXIT;
]
[<instructions>]
END_WHILE
Flowchart
Examples
Simple Example
VAR
i :LINT;
squares :ARRAY[0..20] OF LINT;
END_VAR
VAR
FIRST_SQUARE :LINT := LOWER_BOUND(squares,1);
LAST_SQUARE :LINT := UPPER_BOUND(squares,1);
END_VAR
i := FIRST_SQUARE;
WHILE (i <= LAST_SQUARE) DO
squares[i] := i*i;
i := i+1;
END_WHILE
Example with CONTINUE
and EXIT
VAR
actBit :DINT;
bar :WORD;
highestSetBit :DINT(-1..15);
END_VAR
ctBit := 15;
(* While loop finds the number of the highest set bit *)
WHILE (actBit >= 0) DO
IF (bar = 0) THEN
highestSetBit := -1;
EXIT;
END_IF
IF ((bar AND SHL(1,actBit)) = 0) THEN
actBit := actBit -1;
CONTINUE;
END_IF
highestSetBit := actBit;
EXIT;
END_WHILE
REPEAT-Loop
The REPEAT
-loop is a variant of the WHILE
-loop. This loop will execute the code block once, before checking if the <condition expression> is TRUE
. Then it will repeat the loop as long as the <condition expression> returns FALSE
.
EXIT
-statement is used to jump out of a loop.CONTINUE
-statement interrupts the current iteration and proceeds with the next iteration of the loop.Syntax
REPEAT
<instruction>
[CONTINUE;
]
[EXIT;
]
[<instructions>]
UNTIL
<condition expression>
END_REPEAT
Flowchart
Examples
Simple Example
VAR
i :LINT;
squares :ARRAY[0..20] OF LINT;
END_VAR
VAR
FIRST_SQUARE :LINT := LOWER_BOUND(squares,1);
LAST_SQUARE :LINT := UPPER_BOUND(squares,1);
END_VAR
i := FIRST_SQUARE;
REPEAT
squares[i] := i*i;
i := i+1;
UNTIL
(i > LAST_SQUARE)
END_REPEAT
Example with CONTINUE
and EXIT
VAR
actBit :DINT;
bar :WORD;
highestSetBit :DINT(-1..15);
END_VAR
actBit := 15;
(* While loop finds the number of the highest set bit *)
REPEAT
IF (bar = 0) THEN
highestSetBit := -1;
EXIT;
END_IF
IF ((bar AND SHL(1,actBit)) = 0) THEN
actBit := actBit -1;
CONTINUE;
END_IF
highestSetBit := actBit;
EXIT;
UNTIL
(actBit < 0)
END_REPEAT