03 — FUNCTION_BLOCK
The machine with a memory — one block type, as many instances as the plant has motors.
Estimated 22 minutes.
Five motors, five copies of the same rung
Every plant has this file. A start/stop latch that worked, then got copied for the next motor, and the next. Five near-identical blocks — until someone edits four of them. The fifth keeps running last year's logic, and nobody finds out until a night shift.
Apply one change order to five machines
Engineering wants every motor start logged. Patch the plant both ways and count the edits.
The latch you already know
Both languages say the same thing. Neither one has a name for "which motor".
A FUNCTION_BLOCK is logic plus a place to remember
A FUNCTION forgets everything the moment it returns. A FUNCTION_BLOCK is handed its own private drawer of memory that survives from scan to scan — and every time you declare a variable of that block's type, the runtime hands out another drawer.
The block type, written once
Prev is the whole trick: a plain VAR that keeps its value between scans, so the block can compare "now" against "last time".
Where does Prev actually live?
FB_Motor declares Prev in a plain VAR block. The program declares two instances, Conveyor and Pump. How many copies of Prev exist at runtime?
- One — it belongs to the block type
- Two — one per instance
- None — VAR is temporary, it is cleared each scan
- Two, but they are kept in sync automatically
Watch two instances diverge, scan by scan
This is the real ST engine, not an animation. Press Run, then scrub back and forth — the counters below the skids are the actual values the engine computed on each scan cycle.
Two instances in both languages
The instance name above the ladder box and the variable name in the ST call are the same thing.
Three sections, three levels of trust
VAR_INPUT is what the caller may write. VAR_OUTPUT is what the caller may read. Plain VAR is private — the block's own scratch space. Getting this split right is the difference between a reusable block and a shared global with extra steps.
Map every pin on the instance
Click each pin and each internal variable to see who is allowed to touch it.
The forgotten input
You call Conveyor(Start := PB_Start); and omit Stop entirely. On the next scan, what value does Stop have inside the instance?
- FALSE — omitted inputs are cleared
- Whatever it was given the last time it was passed
- The call fails to compile
- It reads directly from the physical input card
Four legal ways to call an instance
Named arguments are worth the extra characters: they survive somebody reordering the block's inputs next year.
Call it once per scan, from one place
An instance is a physical drawer of memory, so calling it twice in a scan runs its logic twice — timers advance twice, edges get eaten, counters double-count. One instance, one call site, once per scan. If two parts of the program need it, they need two instances, or one owner they both ask.
The double-call bug
A programmer calls Conveyor(...) inside two different IF branches, "so it works in both modes". The block contains a TON. What breaks?
- Nothing — the second call is ignored
- The timer can advance twice per scan and finish early
- The compiler rejects the second call
- The instance splits into two instances automatically
What you learned
- A FUNCTION_BLOCK is logic plus private memory that survives between scans.
- The block type is the blueprint; every declared instance gets its own copy of every variable.
- VAR_INPUT is written by the caller, VAR_OUTPUT is read by the caller, VAR is private.
- An omitted input keeps its last value — pass what matters on every call.
- One instance, one call per scan: calling twice runs the block twice.
- The point is not fewer keystrokes; it is having exactly one place to change.
FUNCTION_BLOCK — done
- You watched two instances of one block type keep separate memories on the real engine.
- You can read and write the same block in ladder and in Structured Text.
- You know why one change order costs one edit instead of five.