TwinCAT Structured Text does not “call” a function-block output. Declare an instance, execute that instance, then consume its VAR_OUTPUT values either through Instance.Output or an output mapping in the call. The call must execute before downstream logic reads the new result.
Function-block instance declaration
Before anything else, confirm that the called function block has an instance in the caller’s VAR section. A function-block declaration defines a type; the instance stores that block’s inputs, outputs, and internal state.
PROGRAM MAIN
VAR
Numbers : One;
NumberTotal : INT;
END_VAR
Here, One is the function-block type and Numbers is its instance. Calls and output references use Numbers, not the type name.
| Declaration element | Purpose | Example |
|---|---|---|
| Function-block type | Defines the executable logic and interface | One |
| Instance | Holds one copy of the block’s data and state | Numbers |
| Destination variable | Receives or uses an output value | NumberTotal |
Do not move on until the declaration compiles and the instance appears as a structured variable with its declared members.
Input assignment and block execution
Call the instance cyclically and assign its inputs with :=. Named input assignment makes the connection independent of argument order and keeps larger calls readable.
Numbers(DigIn_1 := DigIn_1);
The call executes the body of One. Reading Numbers.Number1 without first executing Numbers(...) only reads the value currently stored in the instance. On the first scan that may be an initialization value; on later scans it may be a result retained from an earlier execution.
- Write the external signal or calculated value to the input argument.
- Execute the function-block instance.
- Inspect the block outputs online before adding downstream calculations.
The original block condition also needs correction. DigOut_1 is declared as an output but is tested before the shown logic assigns it. Test the input when the calculation is meant to follow the input state:
FUNCTION_BLOCK One
VAR_INPUT
DigIn_1 : BOOL;
END_VAR
VAR_OUTPUT
DigOut_1 : BOOL;
Number1 : INT;
Number2 : INT;
END_VAR
DigOut_1 := DigIn_1;
IF DigIn_1 THEN
Number1 := 3;
Number2 := 5;
END_IF
Confirm that forcing or changing DigIn_1 executes the intended branch and updates the watched outputs.
Output access through the instance
After the call, access a function-block output with dot notation: Instance.Output. The proposed expression is valid only when it follows the instance call in execution order.
Numbers(DigIn_1 := DigIn_1);
NumberTotal := Numbers.Number1 + Numbers.Number2;
With the shown block logic and a true input, Number1 becomes 3, Number2 becomes 5, and NumberTotal becomes 8. This access method is useful when several expressions consume the same output or when the caller needs the value later in its logic.
Execution order remains significant. This sequence reads the previous stored values before calculating the current ones:
NumberTotal := Numbers.Number1 + Numbers.Number2;
Numbers(DigIn_1 := DigIn_1);
Do not move on until the watch window shows the outputs changing at the call and NumberTotal changing immediately afterward in the same scan path.
Output mapping in the call
An output can also be connected to a caller variable in the instance call. Output association uses =>, while input assignment uses :=.
VAR
Block1 : firstblock;
In1 : INT;
Out1 : INT;
END_VAR
Block1(
firstin := In1,
firstout1 => Out1
);
| Connection | Syntax | Data direction |
|---|---|---|
| Input assignment | formal_input := expression |
Caller to block |
| Output mapping | formal_output => variable |
Block to caller |
| Direct output read | Instance.Output |
Stored instance value to expression |
Choose one clear consumption pattern for each result. Mapping an output is convenient when a caller variable is the established interface. Dot notation avoids a duplicate variable when downstream logic can use the instance member directly.
An omitted output connection does not prevent the block from executing. Its output remains available as an instance member:
Block1(firstin := In1);
Out1 := Block1.firstout1;
Confirm that the mapped variable and Block1.firstout1 show the same value after the call.
Caller and block I/O separation
Keep physical I/O addresses in the program or I/O-mapping layer and pass their values into reusable function blocks. The sample declares DigIn_1 AT %IX0.0 and DigOut_1 AT %QX0.0 in MAIN; those are concrete bit addresses. A reusable block can expose ordinary Boolean inputs and outputs without binding itself to physical process-image locations.
PROGRAM MAIN
VAR
DigIn_1 AT %IX0.0 : BOOL;
DigOut_1 AT %QX0.0 : BOOL;
Numbers : One;
NumberTotal : INT;
END_VAR
Numbers(DigIn_1 := DigIn_1);
DigOut_1 := Numbers.DigOut_1;
NumberTotal := Numbers.Number1 + Numbers.Number2;
This structure separates three operations: read the mapped input, execute the block, and write the mapped output. It also prevents an instance from hiding process-image ownership inside its implementation.
- Verify that
%IX0.0changes with the intended input. - Verify that
Numbers.DigIn_1receives that state at the call. - Verify that
Numbers.DigOut_1changes after execution. - Verify that assigning it to
%QX0.0changes the intended output image bit.
Do not move on while more than one logic path writes the same physical output.
Function and function-block distinction
A function block requires a declared instance because its outputs and internal variables belong to that instance. A function does not require an instance and returns one value through the function name.
result := functionname(inp1, inp2, inp3);
For the shown function declaration, functionname : REAL defines the return type, and the body assigns the return value to functionname. By contrast, a function block may expose multiple VAR_OUTPUT members and retain instance data between calls.
| POU type | Caller declaration | Result access |
|---|---|---|
| Function block | Declare an instance such as Numbers : One
|
Numbers.Number1 or Number1 => destination
|
| Function | No instance declaration | destination := functionname(...) |
Confirm the POU type before choosing syntax; changing argument names between a function declaration and its call does not create a connection. Named arguments must use the formal names declared by that POU, while positional arguments depend on declaration order.
FAQ
What happens if I read a TwinCAT function-block output before calling the block?
You read the value already stored in that instance, which may be an initialization value or the previous execution’s result. Place the instance call before the output expression.
What happens if I leave a function-block output unconnected in the call?
The block still executes, and the output remains accessible through Instance.Output. Connect it later with dot notation or map it using =>.
What happens if I use := for a function-block output?
:= assigns an input from the caller to the block. Use => to map a declared output to a caller variable, or read the output through the instance.
What happens if I call the function-block type name instead of its instance?
A function block needs an instance such as Numbers : One. Execute Numbers(...); reserve direct calls without an instance for functions.
How do I verify a TwinCAT function-block output end to end?
Watch the physical input, the instance input, the instance output, the destination variable, and the physical output in that order. Change %IX0.0 and confirm the shown true-state calculation produces Number1 = 3, Number2 = 5, and NumberTotal = 8 after the call.