Home > IEC 61131-3 (english) > IEC 61131-3: Arrays with variable length

IEC 61131-3: Arrays with variable length

While declaring arrays, one had always to define a constant value up to now. Since the 3rd edition of the IEC 61131-3, arrays can be declared with a variable length. Thus, you can create functions much more generically than previously.

Although, variables can be used for array bounds, they have to be declared as constants. An adaption of the array bounds is thus not possible at runtime.

PROGRAM MAIN
VAR
  arrData             : ARRAY[1..ARRAY_UPPER_BOUND] OF INT;
END_VAR
VAR CONSTANT
  ARRAY_UPPER_BOUND   : INT := 10;  
END_VAR

Fixed array bounds represent an inconvenient limitation especially when arrays are passed to functions or function blocks as parameters. If this limitation is inacceptable, one had to switch to pointer arithmetic with all usual disadvantages. Below is a simple example, which calculates the sum of a one-dimensional array of LREAL variables.

FUNCTION F_CalcSum1DimArrayOldSchool : LREAL
VAR_INPUT
  pData           : POINTER TO LREAL;
  nSize           : UDINT;
END_VAR
VAR
  pDataIndex      : POINTER TO LREAL;
  nUpperIndex     : UDINT;
  nIndex          : UDINT;
END_VAR
 
F_CalcSum1DimArrayOldSchool := 0;
nUpperIndex := nSize / SIZEOF(pDataIndex^);
IF (nUpperIndex > 0) THEN
  FOR nIndex := 0 TO (nUpperIndex - 1) DO
    pDataIndex := pData + (nIndex * SIZEOF(pDataIndex^));
    F_CalcSum1DimArrayOldSchool := F_CalcSum1DimArrayOldSchool + pDataIndex^;   
  END_FOR
END_IF

The function can be used for the addition of arbitrary LREAL arrays. It is independent of the number of elements and of the upper and lower array bounds.

PROGRAM MAIN
VAR
  array01    : ARRAY[2..8] OF LREAL := [16.1, 34.1, 4.1, 43.1, 35.1, 2.1, 65.1];
  lrSum01    : LREAL;
     
  array02    : ARRAY[-1..2] OF LREAL := [16.1, 34.1, 9.1, 13.1];
  lrSum02    : LREAL;
     
  array03    : ARRAY[-3..-1] OF LREAL := [16.1, 34.1, 8.1];
  lrSum03    : LREAL;
END_VAR
lrSum01 := F_CalcSum1DimArrayOldSchool(ADR(array01), SIZEOF(array01));
lrSum02 := F_CalcSum1DimArrayOldSchool(ADR(array02), SIZEOF(array02));
lrSum03 := F_CalcSum1DimArrayOldSchool(ADR(array03), SIZEOF(array03));

Sample 1 (TwinCAT 3.1.4020) on GitHub

However, this solution has several drawbacks. First of all, simply the fact that the pointer arithmetic has to be used. The source code of the function gets rather complex even with simple tasks. Secondly, size or length value has also to be passed to the function. When calling a function, it must be guaranteed that the array pointer and the length reference match.

Since the 3rd Edition of IEC 61131-3, array can be defined with a variable array bound. Instead of the array bound, a “*” is declared:

arrData   : ARRAY[*] OF LREAL;

If the function is called, the passed array should have constant array bounds. By means of the functions LOWER_BOUND and UPPER_BOUND, the corresponding upper and lower array bounds can be queried in the function.

Currently, arrays with a variable length can be passed only to VAR_IN_OUT variables of functions, function blocks and methods. (One would hope that VAR_INPUT and VAR_OUTPUT variables will be supported in the future.)

Here is an adjusted example:

FUNCTION F_CalcSum1DimArray : LREAL
VAR_IN_OUT
  arrData    : ARRAY[*] OF LREAL;
END_VAR
VAR
  nIndex     : DINT;
END_VAR
F_CalcSum1DimArray := 0;
FOR nIndex := LOWER_BOUND(arrData, 1) TO UPPER_BOUND(arrData, 1) DO
  F_CalcSum1DimArray := F_CalcSum1DimArray + arrData[nIndex];
END_FOR

The function expects only one array of LREAL values as an input parameter. The number of array elements is variable. An iteration over the whole array can be performed with LOWER_BOUND and UPPER_BOUND. The source code is much more readable than in the first example.

PROGRAM MAIN
VAR
  array01    : ARRAY[2..8] OF LREAL := [16.1, 34.1, 4.1, 43.1, 35.1, 2.1, 65.1];
  lrSum01    : LREAL;
     
  array02    : ARRAY[-1..2] OF LREAL := [16.1, 34.1, 9.1, 13.1];
  lrSum02    : LREAL;
     
  array03    : ARRAY[-3..-1] OF LREAL := [16.1, 34.1, 8.1];
  lrSum03    : LREAL;
END_VAR
lrSum01 := F_CalcSum1DimArray(array01);
lrSum02 := F_CalcSum1DimArray(array02);
lrSum03 := F_CalcSum1DimArray(array03);

Sample 2 (TwinCAT 3.1.4020) on GitHub

Multidimensional arrays are also supported. All dimensions have to be declared as variable:

arrData    : ARRAY[*, *, *] OF LREAL;

The second parameter of UPPER_BOUND and LOWER_BOUND specifies the dimension whose respective array bounds have to be identified.

FUNCTION F_CalcSum3DimArray : LREAL
VAR_IN_OUT
   arrData      : ARRAY[*, *, *] OF LREAL;
END_VAR
VAR
   nIndex1, nIndex2, nIndex3  : DINT;
END_VAR
F_CalcSum3DimArray := 0;
FOR nIndex1 := LOWER_BOUND(arrData, 1) TO UPPER_BOUND(arrData, 1) DO
  FOR nIndex2 := LOWER_BOUND(arrData, 2) TO UPPER_BOUND(arrData, 2) DO
    FOR nIndex3 := LOWER_BOUND(arrData, 3) TO UPPER_BOUND(arrData, 3) DO
      F_CalcSum3DimArray := F_CalcSum3DimArray + arrData[nIndex1, nIndex2, nIndex3];
    END_FOR
  END_FOR
END_FOR

With the call, any three-dimensional array of LREAL values can be passed to a function.

ROGRAM MAIN
VAR
  array01    : ARRAY[1..2, 3..4, 5..6] OF LREAL := [16.1, 34.1, 4.1, 43.1, 35.1, 2.1, 65.1, 16.1];
  lrSum01    : LREAL;
END_VAR
lrSum01 := F_CalcSum3DimArray(array01);

Sample 3 (TwinCAT 3.1.4020) on GitHub

Thus, more complex tasks can be implemented flexibly without making use of pointer arithmetic.

Finally, it should be demonstrated with a function block which multiplies two matrices. The sizes of the matrices are variable:

METHOD PUBLIC Multiplication : BOOL
VAR_IN_OUT
  arrayA     : ARRAY[*, *] OF DINT;
  arrayB     : ARRAY[*, *] OF DINT;
  arrayX     : ARRAY[*, *] OF DINT; 
END_VAR
VAR
  nIndex1, nIndex2, nIndex3, nIndex4   : DINT;
END_VAR;
FOR nIndex1 := LOWER_BOUND(arrayA, 1) TO UPPER_BOUND(arrayA, 1) DO
  FOR nIndex2 := LOWER_BOUND(arrayB, 2) TO UPPER_BOUND(arrayB, 2) DO
    nIndex4 := 0;
    FOR nIndex3 := LOWER_BOUND(arrayA, 2) TO UPPER_BOUND(arrayA, 2) DO
      nIndex4 := nIndex4 + arrayA[nIndex1, nIndex3] * arrayB[nIndex3, nIndex2];
    END_FOR;
    arrayX[nIndex1, nIndex2] := nIndex4;
  END_FOR;
END_FOR;

The method can be called by different-sized arrays.

PROGRAM MAIN
VAR
  fbMatrix     : FB_Matrix;
  arrayA1      : ARRAY[1..2, 1..2] OF DINT := [1, 2, 3, 4];
  arrayB1      : ARRAY[1..2, 1..2] OF DINT := [5, 6, 7, 8];
  arrayX1      : ARRAY[1..2, 1..2] OF DINT;
     
  arrayA2      : ARRAY[1..3, 1..3] OF DINT := [1, 2, 3, 4, 5, 6, 7, 8, 9];
  arrayB2      : ARRAY[1..3, 1..3] OF DINT := [5, 6, 7, 8, 10, 11, 12, 13, 14];
  arrayX2      : ARRAY[1..3, 1..3] OF DINT;             
END_VAR
fbMatrix.Multiplication(arrayA1, arrayB1, arrayX1);
fbMatrix.Multiplication(arrayA2, arrayB2, arrayX2);

Sample 4 (TwinCAT 3.1.4020) on GitHub

Advertisements
  1. Alex
    September 5, 2017 at 1:17 pm

    The array’s are not really of variable length. VAR_IN_OUT are by defnition just references to variables. So you can pass the array into a FB, but the caller of this FB still needs to define a fixed length array…

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: