en
cz

Program in FUPLA, Functional Blocks in ST

Program in FUPLA, Functional Blocks in ST

The most common case. We start the setup in Simplified mode and try to continue as long as possible—in at least until all communication channels, I/O modules, and variables are defined, as well as potentially terminals and other devices. Simplified mode automatically configures things like Autogen, which is the automatic generation of global variables based on defined inputs and outputs, but it does not allow work in ST language. Once we decide to switch to Full mode, there is no going back. We must then be careful to manually enable Autogen for newly created I/O variables and select the target project in which the global variables, tied to inputs and outputs, will be generated.

A block in ST language allows for constructions that are complicated or even impossible in FUPLA, such as loops. However, we will start with a very simple block to show how to work with inputs and outputs, so that the resulting block most closely resembles FUPLA blocks from standard libraries. Let's switch the project to Full mode and let's get started!

Simple "ax + b" Block

This calculation would be easy to implement using two functions in FUPLA: add and mul. However, we will proceed with the ST approach:

  • In the project context menu, select Add – Add Functional Block
  • Fill in the name of the new block, select the type and language.
  • The editor for the functional block body will open.

Let’s recall the difference between a function and a function block: A function returns a single return value and any number of output values, while a function block has one or more outputs. (A function can also affect the values of multiple variables, see the DECOMPOSEDATE example below.) A function is stateless: it processes input values, provides them in the form of a result (i.e., writes to a variable), and “disappears.” In contrast, a function block, when inserted into the program, creates its internal variables, and each instance of the block remembers them separately throughout the entire program execution. Function blocks can therefore remember values from previous calls. A typical example of a function block is a counter or a PI controller. A function takes up less memory compared to solving the same task with a function block. Notice that in the IDE, functions in the FUPLA language are bounded by dashed lines, while function blocks are bounded by solid lines. Programming in ST looks similar for both functions and function blocks, but we will demonstrate programming in ST using the example of a function block.

 

The keywords are highlighted in blue, and comments are highlighted in green. A single-line comment or a comment at the end of a line is introduced by two slashes:

month := m - 1; // 0=January, 1=February, etc.

// this entire line is a comment

while a single- or multi-line comment is introduced and ended with parentheses and an asterisk:

(* this
entire text
is a comment *)

(* and this is also a comment *)

The code contains predefined keywords that delimit the declarations of internal variables, inputs, and outputs of a function block. For our example, we will need two inputs and one output, and no internal variables will be necessary. Therefore, we will adjust the code as follows:

FUNCTION_BLOCK linear

    VAR_INPUT
        x, a, b: real;
    END_VAR
    VAR_OUTPUT
        y : real;
    END_VAR

    y := a * x + b;
END_FUNCTION_BLOCK

The function block is saved and the project is compiled. In the properties of the function block, we can then see the declared inputs and outputs, just as with function blocks from the libraries:

The table of inputs and outputs contains automatically generated variables en and eno. These are used to block the execution of the block and provide information about whether the block is enabled. Typically, they are not used, so we can set their visibility to False. On the other hand, the "Show in Explorer" attribute is useful to set to True, because after compiling, the block will be visible in the FUPLA block tree and can be inserted into the main program by dragging it. Otherwise, we would need to use the context menu "Add Library Block" in the program or the shortcut Ctrl-B to filter and find the block by its name.

After the block is inserted, we then connect it to the variables and, if needed, bring any constant inputs inside the block:

Constants (or more precisely, parameters) a and b can be changed during the program's operation—this is possible because each function block represents a separate instance with its own variables. In the case of a function, this wouldn't be possible; constants could only be changed in the source code and would be the same for all calls of that function. Therefore, we couldn't have different "instances" of the function with different parameters.

Then, all that's left is to upload the program to the PLC and verify that the function block is working correctly.

Notice that at the bottom of the block, there is a label "Development." This state of the block can be changed at any time, and the change will take effect after compilation. We use it for internal marking of the development progress or current status.

Development: the block is under development, it may undergo further modifications.
Test: we are convinced that development is completed, the block is in the testing stage.
Release: the block has been tested and is intended for regular use.
Obsolete: a newer version of the block exists, and it would be good not to use the obsolete version in new projects.
Broken: the version of the block has diverged from the main development branch, no further fixes will be made on it.
Not supported: there is a bug in the block, a newer version exists, and this obsolete version is in the library only for compatibility during compilation and should not be used.

Normally, we move between the Development, Test, and Release states.

For comparison, let's take a look at how the same functionality would be written using standard functions in FUPLA:

The calculation using elementary functions is, at first glance, more readable and understandable, but it takes up more space and, in the case of repeated calculations, is a less clear solution, making it harder to edit. Additionally, we wouldn’t necessarily need to route the output of the function block to a global variable (like Analog03); we could use it directly in visualization or a BACnet server, for example. In that case, it would be advisable to name each instance of the function block (here "linear") according to the processed variable.

In debug mode, we can monitor the internal values of the variables within the block after opening it. The source code opens, and the current values for the respective instance are displayed. If we want to view the values of a different instance of the block, we can select it in the drop-down box in the ribbon with the buttons.

As mentioned above, each instance of a function block can have different parameter values – and these can be changed during program execution. This is not possible with functions.

If we want the parameters to be immediately saved to NVRAM after modification, we declare them as RETAIN. The entire block code would then look like this:

FUNCTION_BLOCK linear

    VAR_INPUT
        x: real;
    END_VAR
    VAR_INPUT RETAIN
        a, b: real;
    END_VAR
    VAR_OUTPUT
        y : real;
    END_VAR

    y := a * x + b;
END_FUNCTION_BLOCK

That’s all for the basic properties of a function block in ST.

In the next section, we will look at an example of detecting a change in the input value.

 All parts of article