A TIA program isn't one long script β it's a stack of blocks that call each other. Four kinds, one question tells them apart.
Memory is the dividing line. An FC (function) is pure maths β give it inputs, get an output, it forgets everything the instant it returns. An FB (function block) gets a private instance DB to remember state across scans. An OB is where the OS calls in. A DB is just data.
sqrt(). No private memory; forgets between calls.Here OB1 calls two blocks every scan, both trying to count a pump's run-hours. FC_Hours is a plain function β its working number is a temp that's wiped after every call, so it can never accumulate. FB_Pump has an instance DB, so its total survives scan after scan. Run the pump and watch only one meter climb.
#runHours." But #runHours is a temp β born at 0 every call.#runHours is a static var in the instance DB, so it persists.That's the whole reason FBs exist. Anything that must remember β a timer, a counter, a step number, a state machine β needs an FB and its instance DB. Pure, stateless calculations can stay in an FC.
The payoff: write FB_Pump once, then call it for every pump on the site. Each call names its own instance DB β same behaviour, separate memory. Fix a bug in the FB and all of them inherit it; yet Pump_1's hours never bleed into Pump_2's.
FUNCTION_BLOCK "FB_Pump" VAR running:Bool; runHours:Real; END_VAR // VAR = static = remembered // call it three times β three private instance DBs: "Pump_1"(running := %I0.0); // DB30 "Pump_2"(running := %I0.1); // DB31 "Pump_3"(running := %I0.2); // DB32
One block of code, three notebooks. This is the same idea you met as instance DBs in the Execution Model lesson β here's why it's the backbone of every real Siemens project.
Blocks don't run in isolation β they call each other, and the OS only ever calls the OB. Press Run one scan and follow the execution: OB1 calls FC_Scale (which forgets), then FB_Conveyor (which remembers, in its instance DB), which reads a recipe from a global DB. The call-stack and the memory ledger update live β so you can see exactly where each value lives and what survives to the next scan.
π΄ TEMP is wiped at every return Β· π’ STATIC lives in the instance DB scan-to-scan Β· π‘ a global DB persists for the whole program.
The shape of every TIA program is here: the OS calls an OB, the OB orchestrates FCs and FBs, and they read/write DBs. Logic flows down the tree; data lives in DBs. Master this and "where does this value live?" stops being a mystery.
"Four block types" is the surface. Here's what actually trips people up in real projects:
π OBs are more than OB1
OB100 runs once at startup (init values). OB30β38 are cyclic interrupts firing on a fixed time base β perfect for PID that must run at an exact interval, independent of OB1's scan. Hardware-interrupt OBs fire on an input edge; error OBs (OB82/121β¦) catch faults so the CPU doesn't STOP.
π§ Multi-instance β an FB inside an FB
A TON is itself an FB. When you declare it as a static var inside your FB, its memory nests inside your instance DB β no separate DB per timer. That's multi-instance: one tidy instance DB holds your FB's state and all its sub-instances.
π¦ Optimized vs standard DB
Optimized DBs (S7-1200/1500 default) have no fixed byte addresses β the CPU packs them for speed and you access by name only. Standard DBs keep classic %DBx.DBWy offsets for legacy access. Inserting a tag in a standard DB shifts every address below it β a classic footgun.
β FC β useless β it's your library
Because an FC has no instance DB, it's the lightest, cleanest home for stateless work: a scaling routine, a CRC, a unit conversion. No DB clutter, callable anywhere. Reach for an FC when the answer depends only on the inputs β and an FB the moment it must remember.
You need a reusable conveyor controller that holds its own state machine and a TON timer, callable for each of 12 conveyors. What do you build?