Subrange types

A subrange type is a type whose range of values is only a subset of that of the basic type. The declaration can be carried out in the data types register, but a variable can also be directly declared with a subrange type:

Syntax for the declaration in the ‘Data types’ register:

TYPE <Name> : <Inttype>     (<ug>..<og>) END_TYPE;

<Name> must be a valid IEC identifier,
<Inttype> is one of the data types SINT, USINT, INT, UINT, DINT, UDINT, BYTE, WORD, DWORD (LINT, ULINT, LWORD).
<ug> Is a constant which must be compatible with the basic type and which sets the lower boundary of the range types. The lower boundary itself is included in this range.
<og> Is a constant that must be compatible with the basic type, and sets the upper boundary of the range types. The upper boundary itself is included in this basic type.

Example:

TYPE
 SubInt : INT (-4095..4095);
END_TYPE

Direct declaration of a variable with a subrange \ type:

VAR
 i : INT (-4095..4095);
 ui : UINT (0..10000);
END_VAR

If a constant is assigned to a subrange type (in the declaration or in the implementation) that does not fall into this range (e.g. 1:=5000), an error message is issued.

In order to check for observance of range boundaries at runtime, the functions CheckRangeSigned or CheckRangeUnsigned must be introduced. In these, boundary violations can be captured by the appropriate method and means (e.g. the value can be cut out or an error flag can be set.). They are implicitly called as soon as a variable is written as belonging to a subrange type constructed from either a signed or an unsigned type.

Example:

In the case of a variable belonging to a signed subrange type (like i, above), the function CheckRangeSigned is called; it could be programmed as follows to trim a value to the permissible range:

FUNCTION CheckRangeSigned : DINT
VAR_INPUT
 value, lower, upper: DINT;
END_VAR
IF (value < lower) THEN
 CheckRangeSigned := lower;
ELSIF(value > upper) THEN
 CheckRangeSigned := upper;
ELSE
 CheckRangeSigned := value;
END_IF

In calling up the function automatically, the function name CheckRangeSigned is obligatory, as is the interface specification: return value and three parameters of type DINT

When called, the function is parameterized as follows:

- value:  the value to be assigned to the range type

- lower:  the lower boundary of the range

- upper:  the upper boundary of the range

- Return value: this is the value that is actually assigned to the range type

An assignment i:=10*y implicitly produces the following in this example:

i := CheckRangeSigned(10\*y, -4095, 4095);

Even if y for example has the value 1000, then i still has only the value 4095 after this assignment.

The same applies to the function CheckRangeUnsigned: function name and interface must be correct.

FUNCTION CheckRangeUnsigned : UDINT
VAR_INPUT
 value, lower, upper: UDINT;
END_VAR

Warning

If neither of the functions CheckRangeSigned or CheckRangeUnsigned is present, no type checking of subrange types occurs during runtime! The variable i could then take on any value between 32768 and 32767 at any time!

Attention

If neither of the functions CheckRangeSigned or CheckRangeUnsigned is present like described above, there can result an endless loop if a subrange type is used in a FOR loop. This will happen when the range given for the FOR loop is as big or bigger than the range of the subrange type!

Attention

The CheckRangeSigned-function provided with the Check.Lib library is just a sample solution! Before using the library module check whether the function is working as requested for your project, or implement an appropriate CheckRange-function directly as a POU in the project.

Example:

VAR
 ui : UINT (0..10000);
END_VAR

FOR ui:=0 TO 10000 DO
...
END_FOR

The FOR loop will never be finished, because ui cannot get bigger than 10000.

Also take care of the definition of the CheckRange functions when you define the incremental value of a FOR loop!