05 — STANDARD BLOCKS
TON, CTU and R_TRIG are function blocks — read them as proof of the instance model.
Estimated 24 minutes.
You have been declaring instances since your first timer
Open any project you have shipped. Somewhere in it is a line like T_Fill : TON; — a type, a name, and a block that remembers how long the valve has been open. TON is not an instruction and not a keyword: it is a FUNCTION_BLOCK from the IEC 61131-3 standard library, and T_Fill is an instance of it. Everything the last lesson claimed about instances, you have already been trusting at three in the morning.
One input, three timers, three different answers
Delay, Hold and Shot are three instances — a TON, a TOF and a TP — wired to the same gate switch with the same 600 ms preset. Run it, then scrub scan by scan and find the exact cycle where they stop agreeing.
A timer on a rung, and the same timer in text
Delay above the ladder box and Delay in the ST call are not two timers. They are one instance, drawn twice.
The gate that chatters
Delay is a TON with PT := T#600ms. It is 300 ms into its delay when a knock drops ZS_Gate FALSE for a single 100 ms scan, then it goes TRUE again. What does Delay.ET read on the next scan?
- 300 ms — it pauses and picks up where it left off
- 0 ms — the on-delay starts again from zero
- 600 ms — the interruption completes the timer
- It holds at 300 ms until PT is changed
The standard set, and the pins you have to know cold
The library is small and it is fixed: three timers, three counters, two edge detectors, two bistables. Every one of them is a FUNCTION_BLOCK, so every one needs a declared instance name and keeps its state between scans. Learn the pins once — IN and PT into the timers, CU/R/PV into a CTU, CLK into an edge block — and you can read any vendor project without opening the help file.
Ten blocks on the rail
CTD loads with LD instead of resetting with R. CTUD has both, plus two outputs — QU when CV reaches PV, QD when it hits zero — and R beats LD when you assert both.
Hold both buttons and watch the latches disagree
One SET button and one RESET button, wired to an RS and an SR at the same time. Hold each on its own first, then hold both together.
The long carton on the belt
A 900 mm carton sits in front of PE_Part for 30 scans, holding CU TRUE the whole time. Tally is a CTU with PV := 24. What is Tally.CV after those 30 scans?
- 30 — one per scan while CU is TRUE
- 1 — it counts the edge, not the level
- 0 — CU must pulse faster than the scan
- 24 — it stops at PV
A counter on a rung, and the same counter in text
Tally.CV is the running count and Tally.Q is the "reached PV" flag — two VAR_OUTPUTs of one instance.
One job, one instance name
An instance is a physical drawer of memory, not a label on a rung. Two pieces of logic calling the same timer are not sharing it — they are taking turns overwriting each other's IN and ET inside the same scan, and the last call before the read is the one that wins. Name one instance per job: Dly_DoorA, Dly_DoorB. When you copy a rung, the instance name is the thing you must remember to change.
Two guard doors, one timer instance
Both wirings run in the same program on the real engine. Switch between them, play the run, and watch what door A gets for its 400 ms horn delay.
.Q, .ET and .CV are just members
Delay.Q is not special timer syntax. It is the same member access you used for Conveyor.Running last lesson: an instance, a dot, one of the block's VAR_OUTPUTs. So they work anywhere an expression works — inside an IF, in arithmetic, as the pin of the next block. Read them freely; writing into them is writing into another block's memory.
Build a debounce block that owns a timer
FB_Debounce takes a bouncing contact and gives back one clean signal. Drop each part into the section it belongs in — a wrong drop tells you why and hands the part straight back. When the block is complete it runs on a real bottle line.
Your block, in both languages
A block that contains a block. That is composition — and it is how every serious library is built.
Call it every scan, and call it once
An instance only advances when its call executes, so a standard block belongs on the unconditional part of your code — never inside an IF that sometimes runs. Call it twice in one scan and it runs twice: the timer takes two ticks, the counter double-counts, the edge detector eats its own edge. Treat power-up with suspicion too: the IEC reference bodies start their internal memory at FALSE, so a signal that is already TRUE on the first scan reads as a fresh rising edge on CODESYS and TwinCAT.
The counter that lost a shift
Tally(CU := Pulse, R := PB_Reset, PV := 500); sits inside IF Mode = MODE_AUTO THEN … END_IF. An operator jogs the line in manual for twenty seconds while cartons keep passing the sensor, then switches back to auto. What does Tally.CV show?
- It catches up — the block buffers the edges it missed
- Every carton that passed in manual is missing; at best one edge is seen when the call resumes
- The compiler rejects a counter inside an IF branch
- CV resets to zero while the branch is not running
What your controller adds on top
On a real CODESYS or TwinCAT target you can hang a METHOD on this same block — Reset(), say, to clear the inner timer without waiting for the beam. TryPLC's browser engine runs the IEC 61131-3 second-edition core, so METHOD, EXTENDS and INTERFACE are rejected here: the block you ran live is the composition form, and the source below is what you would download to the controller. Lesson 07 takes the method apart.
What you learned
- TON, TOF, TP, CTU, CTD, CTUD, R_TRIG, F_TRIG, RS and SR are FUNCTION_BLOCKs — every one needs its own declared instance.
- Pins: IN/PT into a timer and Q/ET out; CU/R/PV into a CTU and Q/CV out; CLK into an edge block; Q1 out of both bistables.
- RESET1 dominates on RS and SET1 dominates on SR — the pin carrying the 1 is the pin that wins.
- .Q, .ET and .CV are instance members: read them in any expression, and never write into them.
- Two rungs on one timer instance is not sharing, it is overwriting — the last call before the read wins.
- A block can own a block: FB_Debounce holds a TON, and Main can still watch PE.T.ET.
- Call every instance once per scan and unconditionally; gate the result, never the call.
STANDARD BLOCKS — done
- You can name every pin on the standard set and say which input dominates on each bistable.
- You watched TON, TOF and TP answer one input three different ways on the real engine, and saw what a shared instance does to a guard door.
- You built a reusable block that owns a timer inside it — your first composed FUNCTION_BLOCK.