Using functions and function-block instances in Structured Text
The Core Idea
A function is a reusable calculation, while a function-block instance can keep its own state between calls. Separate instances let the same behavior serve different devices without sharing their working memory.
What you will learn
Call a function with named inputs and use the value it returns.
Declare a function-block instance, call it with its inputs, and read its outputs.
Give every independent job its own instance, because an instance is memory.
Why it matters
Two lanes can use the same counting logic and still need independent counts. Choosing and calling the right reusable unit keeps those responsibilities clear.
The problem
Two packing lanes each count parts into batches of 6. Each lane has a photo-eye, and one Reset button starts both batches again.
| Signal | Type | Meaning |
LANE_A_EYE, LANE_B_EYE | BOOL | TRUE while a part blocks that lane's photo-eye. |
RESET_PB | BOOL | TRUE while Reset is pressed. |
A_COUNT, B_COUNT | INT | Parts counted on each lane. |
A_FULL, B_FULL | BOOL | TRUE when that lane has a full batch. |
A_PCT, B_PCT | REAL | Each lane's batch progress, in %. |
The program uses two kinds of reusable code:
A function,
PercentOf, supplied at the top of the program. You call it with named inputs,PercentOf(Part := A_COUNT, Whole := 6), and it returns aREAL. It keeps nothing between calls.A function block, the standard up-counter
CTU. You declare an instance of it inVAR ... END_VAR, call the instance with its inputs, and read its outputs with a dot:CVis the count, andQturns on whenCVreaches the presetPV.
The program declares one counter instance, LaneCounter, and calls it for each lane. Declared variables appear at the bottom of the Tags list, with every member of the instance. The simulator scans every 100 ms.
After you lock in your prediction, press LANE A EYE once, then hold it down for a few seconds.
See it go wrong
Hold LANE A EYE and watch LaneCounter.CU_M in Tags: it is the counter's memory of CU from the previous call.
Fix it
Give each lane its own counter instance, and read each lane's outputs from its own instance. The checks count parts on both lanes, hold an eye, fill a batch, and press Reset. Editing pauses the program, so press Run to try your version.
Passing the checks records Structured Text progress on the shared counters concept.
What happened and why
An instance is a named block of memory that its type's code runs on. LaneCounter held one count and one edge memory, CU_M. Each call ran the counter on that memory:
| Call | CU | CU_M before | Counts? | CU_M after |
| Lane A, scan 1 | TRUE | FALSE | Yes | TRUE |
| Lane B, scan 1 | FALSE | TRUE | No | FALSE |
| Lane A, scan 2 | TRUE | FALSE | Yes, again | TRUE |
With LaneA and LaneB, each instance sees only its own eye, once per scan, so each keeps its own count and its own edge memory.
A function is different. PercentOf has inputs and a result, but no memory, so one definition can serve every lane: the same inputs always give the same answer. Use a function for a calculation, and a function block when the result depends on what happened in earlier scans, such as a count, an edge, or a timer.
Read an instance's outputs after its call in the same scan. Before the call, they still hold the previous scan's results.
Common mistakes
Sharing one instance between two jobs: the jobs share one count and one edge memory. Declare an instance per job.
Reading an output before the call: the lamp and the count show the previous scan's values, one scan late.
Calling a counter only while its input is TRUE: the instance never sees
CUgo FALSE, so its edge memory stays TRUE and the next part never counts. Call every instance on every scan.Swapping a function's inputs:
PercentOf(Part := 6, Whole := A_COUNT)returns 300 % after two parts. Named inputs make the meaning visible; read them as you write them.
Platform notes
IEC 61131-3 (the international standard for programmable controller languages) defines the
CTU,CTD, andCTUDcounters used here, with the sameCU,R,PV,Q, andCVnames. Some platforms add other members, keep counters in a separate counter data type, or name the reset input differently.This simulator lets you leave out an input of a function block in a call; the input keeps its last value. A function call in this simulator, by contrast, must supply all of the function's inputs.
Enable JavaScript to run the embedded simulator and complete the interactive exercises.