PLC Dialect Cheat Sheet: TON, R_TRIG, TOF Across 8 Dialects
PLC Dialect Cheat Sheet: TON, R_TRIG, TOF Across 8 Dialects

Three of the most-searched PLC programming questions come down to the same thing: "how do I write a TON in Mitsubishi?", "what is the Allen-Bradley R_TRIG syntax?", "how does a TOF work in Siemens?" The logic is identical across all vendors. The notation is not. This reference gives you the canonical idiom for each, dialect by dialect, with the actual code.
The snippets below are sourced from the simulator's curriculum starters and briefings for lessons 7 (R_TRIG), 8 (TON), and 9 (TOF). You can work through each lesson interactively at /learn/dialects/<dialect>/<lesson> — the links are included in each section.
TON — On-Delay Timer
A TON (timer on-delay) starts counting when its input goes TRUE. Its output (the done bit, .Q) goes TRUE only if the input has been TRUE continuously for at least the preset duration. If the input drops before the preset elapses, the accumulator resets to zero. This is the standard "delay-before-action" building block — warn before starting a conveyor, debounce a noisy sensor, hold an alarm state for a minimum time.
Every dialect in the curriculum has a native on-delay timer. The differences are in the call shape and, importantly, in the preset units.
IEC 61131-3
VAR
ENABLE AT %I0.0 : BOOL;
WARN_LAMP AT %Q0.0 : BOOL;
T_WARN : TON;
END_VAR
T_WARN(IN := ENABLE, PT := T#3s);
WARN_LAMP := T_WARN.Q;
T#3s is a typed TIME literal — the unit is on the page, which makes misreading the preset substantially harder. T_WARN is a named function-block instance; you can have many timers without any risk of address collision.
Allen-Bradley (Studio 5000 / RSLogix)
TAG ENABLE I:0/0 BOOL
TAG WARN_LAMP O:0/0 BOOL
TAG T_WARN TON
TON T_WARN IN:ENABLE PT:3000
XIC T_WARN.Q OTE WARN_LAMP
PT is in milliseconds — 3000 means 3 seconds. The instance is declared as a TON tag. The done bit is read as T_WARN.Q, which you examine with a standard XIC contact.
Siemens (TIA Portal SCL)
VAR
ENABLE AT %I0.0 : BOOL;
WARN_LAMP AT %Q0.0 : BOOL;
T_WARN : TON;
END_VAR
T_WARN(IN := ENABLE, PT := T#3s);
WARN_LAMP := T_WARN.Q;
Siemens SCL in TIA Portal (S7-1200/1500) uses the same IEC-compatible function-block call. The named-argument form IN := and PT := with a T# literal is standard across IEC-compliant environments.
Mitsubishi (GX Works — IL mnemonics)
VAR
ENABLE AT %I0.0 : BOOL;
WARN_LAMP AT %Q0.0 : BOOL;
T_WARN : TON;
END_VAR
LD X0
OUT T1 K30
LD T1
OUT Y0
Mitsubishi has no TON function block. Instead, T1 is a numbered timer device. OUT T1 K30 drives the timer's IN from whatever is on the rung — here LD X0 (ENABLE) — and sets a preset of K30. On the default 100 ms timer base, K30 = 30 × 100 ms = 3 seconds. The timer's done bit is read as a plain contact: LD T1. The classic gotcha: K30 is 3 seconds, not 30 seconds and not 30 milliseconds. The unit is a memorised convention, not something the syntax tells you.
Schneider (Unity Pro / EcoStruxure — SCL)
VAR
ENABLE AT %I0.0 : BOOL;
WARN_LAMP AT %Q0.0 : BOOL;
T_WARN : TON;
END_VAR
T_WARN(IN := ENABLE, PT := T#3s);
WARN_LAMP := T_WARN.Q;
Unity Pro and EcoStruxure both support IEC 61131-3 Structured Text. The TON call is identical to the IEC form.
Delta (WPLSoft — IL mnemonics)
LD X0
TMR T1 K30
LD T1
OUT Y0
Delta's TMR instruction is the timer call — syntactically equivalent to Mitsubishi's OUT T<n> for this purpose. K30 is again 30 × 100 ms = 3 seconds on the default 100 ms base. The done bit is read as LD T1.
Omron (CX-Programmer — IL mnemonics)
VAR
ENABLE AT %I0.0 : BOOL;
WARN_LAMP AT %Q0.0 : BOOL;
T_WARN : TON;
END_VAR
LD ENABLE
TIM 1 #30
LD T1
OUT WARN_LAMP
Omron uses TIM as the timer instruction. The preset uses a # prefix (#30) rather than a K prefix, but the base is still 100 ms — so #30 = 3 seconds. The done-bit contact is T1.
Instruction List (IEC IL)
VAR
ENABLE AT %I0.0 : BOOL;
WARN_LAMP AT %Q0.0 : BOOL;
T_WARN : TON;
END_VAR
CAL T_WARN(IN := ENABLE, PT := T#3s)
LD T_WARN.Q
ST WARN_LAMP
IEC IL uses CAL to call a function block with named arguments, then LD/ST to move the output to a coil.
TON Quick Reference
| Dialect | Timer call | Preset format | Done-bit read |
|---|---|---|---|
| IEC 61131-3 | T_WARN(IN := ENABLE, PT := T#3s) | Typed TIME literal | T_WARN.Q |
| Allen-Bradley | TON T_WARN IN:ENABLE PT:3000 | Integer milliseconds | T_WARN.Q (via XIC) |
| Siemens SCL | T_WARN(IN := ENABLE, PT := T#3s) | Typed TIME literal | T_WARN.Q |
| Mitsubishi | OUT T1 K30 | K × 100 ms | LD T1 |
| Schneider | T_WARN(IN := ENABLE, PT := T#3s) | Typed TIME literal | T_WARN.Q |
| Delta | TMR T1 K30 | K × 100 ms | LD T1 |
| Omron | TIM 1 #30 | # × 100 ms | LD T1 |
| Instruction List | CAL T_WARN(IN := ENABLE, PT := T#3s) | Typed TIME literal | T_WARN.Q (via LD/ST) |
At a glance, the same on-delay timer in every dialect:
Practice TON across all dialects: IEC lesson 8, Allen-Bradley lesson 8, Siemens lesson 8, Mitsubishi lesson 8, Delta lesson 8, Omron lesson 8.
R_TRIG — Rising-Edge Detector
An R_TRIG (rising-edge trigger) produces a one-scan TRUE pulse the moment its input transitions from FALSE to TRUE. Even if the input stays TRUE for multiple scans, the output is only TRUE on the first scan of that transition. This is the building block for counting events, incrementing registers on a single button-press, and preventing double-triggers on held inputs.
IEC, Allen-Bradley, Siemens, Schneider, and Instruction List all expose R_TRIG as a named function-block instance with a .Q output. Mitsubishi, Delta, and Omron have no atomic rising-edge instruction. In those dialects, the one-shot is synthesised from two rungs and a history bit.
Edge detection is where vendor naming diverges most — R_TRIG/F_TRIG in IEC, ONS/OSR in Allen-Bradley, and P/N edge contacts in Siemens:
IEC 61131-3
VAR
BUTTON AT %I0.0 : BOOL;
COUNT_PULSE AT %Q0.0 : BOOL;
EDGE_DET : R_TRIG;
END_VAR
EDGE_DET(CLK := BUTTON);
| EDGE_DET.Q | := COUNT_PULSE ;
Declare EDGE_DET as an R_TRIG instance, call it with CLK := BUTTON, read the pulse via EDGE_DET.Q. The function block manages the history bit internally.
Allen-Bradley (Studio 5000 / RSLogix)
TAG BUTTON I:0/0 BOOL
TAG COUNT_PULSE O:0/0 BOOL
TAG EDGE_DET R_TRIG
R_TRIG EDGE_DET CLK:BUTTON
XIC EDGE_DET.Q OTE COUNT_PULSE
TAG EDGE_DET R_TRIG declares the instance. The instruction R_TRIG EDGE_DET CLK:BUTTON calls it. XIC EDGE_DET.Q OTE COUNT_PULSE reads the done bit on a standard rung.
Siemens (TIA Portal SCL)
VAR
BUTTON AT %I0.0 : BOOL;
COUNT_PULSE AT %Q0.0 : BOOL;
EDGE_DET : R_TRIG;
END_VAR
EDGE_DET(CLK := BUTTON);
COUNT_PULSE := EDGE_DET.Q;
Same IEC-compatible form as the IEC starter. Siemens TIA Portal supports R_TRIG natively.
Mitsubishi (GX Works — synthesised from primitives)
Mitsubishi GX-IL has no R_TRIG instruction. The synthesis: a rising edge of BUTTON is the scan where BUTTON is TRUE and its previous-scan value (PREV_BTN) is FALSE.
VAR
BUTTON AT %I0.0 : BOOL;
COUNT_PULSE AT %Q0.0 : BOOL;
PREV_BTN AT %M0.0 : BOOL;
END_VAR
; Rung 1: pulse = BUTTON AND NOT (previous BUTTON)
LD X0
ANI PREV_BTN
OUT Y0
; Rung 2: capture this scan's BUTTON into PREV_BTN for the next scan
LD X0
OUT PREV_BTN
Rung order is load-bearing. The pulse rung must execute while PREV_BTN still holds the previous scan's value. If you write rung 2 first, PREV_BTN already matches X0 by the time the pulse comparison runs, and COUNT_PULSE never fires. ANI is the AND-inverse (AND NOT) instruction.
Schneider (Unity Pro / EcoStruxure — SCL)
VAR
BUTTON AT %I0.0 : BOOL;
COUNT_PULSE AT %Q0.0 : BOOL;
EDGE_DET : R_TRIG;
END_VAR
EDGE_DET(CLK := BUTTON);
COUNT_PULSE := EDGE_DET.Q;
Unity Pro SCL supports R_TRIG natively with the standard IEC call form.
Delta (WPLSoft — synthesised from primitives)
Delta WPLSoft has no R_TRIG instruction. The synthesis pattern is mnemonic-for-mnemonic identical to Mitsubishi:
VAR
BUTTON AT %I0.0 : BOOL;
COUNT_PULSE AT %Q0.0 : BOOL;
PREV_BTN AT %M0.0 : BOOL;
END_VAR
; Rung 1: pulse = BUTTON AND NOT (previous BUTTON)
LD X0
ANI PREV_BTN
OUT Y0
; Rung 2: latch this scan's BUTTON into PREV_BTN (must come AFTER rung 1)
LD X0
OUT PREV_BTN
Same two-rung structure, same ANI for AND-NOT, same scan-order constraint.
Omron (CX-Programmer — synthesised from primitives)
Omron CX-Programmer has no R_TRIG instruction. Same two-rung approach, but Omron uses symbolic variable names and AND NOT (two words) rather than ANI:
VAR
BUTTON AT %I0.0 : BOOL;
COUNT_PULSE AT %Q0.0 : BOOL;
PREV_BTN AT %M0.0 : BOOL;
END_VAR
; Rung 1: pulse = BUTTON AND NOT (previous BUTTON)
LD BUTTON
AND NOT PREV_BTN
OUT COUNT_PULSE
; Rung 2: refresh the history bit AFTER the pulse rung has fired
LD BUTTON
OUT PREV_BTN
The logic is identical to Mitsubishi and Delta. The surface forms differ: AND NOT instead of ANI, symbolic names instead of device addresses.
Instruction List (IEC IL)
VAR
BUTTON AT %I0.0 : BOOL;
COUNT_PULSE AT %Q0.0 : BOOL;
EDGE_DET : R_TRIG;
END_VAR
CAL EDGE_DET(BUTTON)
LD EDGE_DET.Q
ST COUNT_PULSE
R_TRIG Quick Reference
| Dialect | Mechanism | Key instruction | History bit managed by |
|---|---|---|---|
| IEC 61131-3 | Native FB | EDGE_DET(CLK := BUTTON) | FB internals |
| Allen-Bradley | Native FB | R_TRIG EDGE_DET CLK:BUTTON | FB internals |
| Siemens SCL | Native FB | EDGE_DET(CLK := BUTTON) | FB internals |
| Mitsubishi | Synthesised | LD X0 / ANI PREV_BTN / OUT Y0 | M-device relay (M0) |
| Schneider | Native FB | EDGE_DET(CLK := BUTTON) | FB internals |
| Delta | Synthesised | LD X0 / ANI PREV_BTN / OUT Y0 | M-device relay (M0) |
| Omron | Synthesised | LD BUTTON / AND NOT PREV_BTN / OUT COUNT_PULSE | Work-bit relay |
| Instruction List | Native FB | CAL EDGE_DET(BUTTON) | FB internals |
The defining behaviour of any rising-edge detector — a single one-scan pulse per FALSE→TRUE transition, no matter how long the input is held:
In ladder, the synthesised one-shot is a normally-open input contact in series with a normally-closed history bit:
Practice R_TRIG across all dialects: IEC lesson 7, Allen-Bradley lesson 7, Siemens lesson 7, Mitsubishi lesson 7, Delta lesson 7, Omron lesson 7.
TOF — Off-Delay Timer
A TOF (timer off-delay) works the inverse of a TON. When its input goes TRUE, its output goes TRUE immediately. When the input drops to FALSE, the output stays TRUE for the full preset duration before going FALSE. If the input returns to TRUE during the hold window, the timer resets and the output remains TRUE — no gap in output. This pattern is used wherever you need a hold-open window: an automatic door that stays open for 5 seconds after the last person passes, a lubrication pump that runs for 10 seconds after the machine stops, a fan that keeps running after the motor cuts out.
IEC, Allen-Bradley, Siemens, Schneider, and Instruction List all expose TOF as a named function-block instance. Mitsubishi, Delta, and Omron synthesise TOF from three rungs: a self-sealing latch, an output mirror, and a conditionally-gated on-delay timer.
IEC 61131-3
VAR
OCCUPANCY AT %I0.0 : BOOL;
DOOR_OPEN AT %Q0.0 : BOOL;
T_DOOR : TOF;
END_VAR
T_DOOR(IN := OCCUPANCY, PT := T#5s);
| T_DOOR.Q | := DOOR_OPEN ;
Declare T_DOOR as TOF, call with IN := and PT := T#5s. The .Q output is TRUE as long as OCCUPANCY has been TRUE recently — it drops 5 seconds after the last FALSE-to-TRUE-back-to-FALSE transition.
Allen-Bradley (Studio 5000 / RSLogix)
TAG OCCUPANCY I:0/0 BOOL
TAG DOOR_OPEN O:0/0 BOOL
TAG T_DOOR TOF
TOF T_DOOR IN:OCCUPANCY PT:5000
XIC T_DOOR.Q OTE DOOR_OPEN
PT:5000 is 5 000 milliseconds = 5 seconds.
Siemens (TIA Portal SCL)
VAR
OCCUPANCY AT %I0.0 : BOOL;
DOOR_OPEN AT %Q0.0 : BOOL;
T_DOOR : TOF;
END_VAR
T_DOOR(IN := OCCUPANCY, PT := T#5s);
DOOR_OPEN := T_DOOR.Q;
Mitsubishi (GX Works — synthesised from three rungs)
GX-IL has no TOF instruction. The synthesis uses a self-sealing M-device latch, a native on-delay timer T0 (100 ms base, K50 = 5 seconds), and three rungs.
VAR
OCCUPANCY AT %I0.0 : BOOL;
DOOR_OPEN AT %Q0.0 : BOOL;
LATCH AT %M0.0 : BOOL;
END_VAR
; Rung 1: latch = (LATCH AND NOT T0.Q) OR OCCUPANCY
LD M0
ANI T0
OR X0
OUT M0
; Rung 2: DOOR_OPEN mirrors the latch
LD M0
OUT Y0
; Rung 3: T0 runs only when LATCH is on AND OCCUPANCY has gone away
LD M0
ANI X0
OUT T0 K50
How the three rungs interact: while OCCUPANCY (X0) is TRUE, the OR X0 in rung 1 forces LATCH on regardless of T0. When OCCUPANCY drops, the latch self-seals via LD M0 until T0's done bit rises and the ANI T0 breaks it. Rung 3 starts T0 counting only when LATCH is on AND OCCUPANCY is off — the ANI X0 gate is critical. Without it, T0 would start counting the moment OCCUPANCY went TRUE, T0's done bit would rise mid-presence, and ANI T0 in rung 1 would break the latch seal while the person is still in the doorway.
Schneider (Unity Pro / EcoStruxure — SCL)
VAR
OCCUPANCY AT %I0.0 : BOOL;
DOOR_OPEN AT %Q0.0 : BOOL;
T_DOOR : TOF;
END_VAR
T_DOOR(IN := OCCUPANCY, PT := T#5s);
DOOR_OPEN := T_DOOR.Q;
Delta (WPLSoft — synthesised from three rungs)
Delta WPLSoft has no TOF instruction. Structure identical to Mitsubishi; the only difference is TMR T0 K50 instead of OUT T0 K50 on rung 3.
VAR
OCCUPANCY AT %I0.0 : BOOL;
DOOR_OPEN AT %Q0.0 : BOOL;
LATCH AT %M0.0 : BOOL;
END_VAR
; Rung 1: latch = (LATCH AND NOT T0.Q) OR OCCUPANCY
LD M0
ANI T0
OR X0
OUT M0
; Rung 2: DOOR_OPEN mirrors the latch
LD M0
OUT Y0
; Rung 3: TMR runs only during the off-delay window
LD M0
ANI X0
TMR T0 K50
TMR is Delta's timer instruction; K50 is 50 × 100 ms = 5 seconds. Otherwise the pattern is mnemonic-for-mnemonic the same as Mitsubishi.
Omron (CX-Programmer — synthesised from three rungs)
Omron CX-Programmer has no TOF instruction. Same three-rung structure; differences are symbolic variable names, AND NOT (two words), and TIM 0 #50 for the timer call (# prefix, still 100 ms base).
VAR
OCCUPANCY AT %I0.0 : BOOL;
DOOR_OPEN AT %Q0.0 : BOOL;
LATCH AT %M0.0 : BOOL;
END_VAR
; Rung 1: latch = (LATCH AND NOT TIM0.Q) OR OCCUPANCY
LD LATCH
AND NOT TIM0
OR OCCUPANCY
OUT LATCH
; Rung 2: DOOR_OPEN mirrors the latch
LD LATCH
OUT DOOR_OPEN
; Rung 3: TIM 0 runs only when LATCH is on AND OCCUPANCY has gone away
LD LATCH
AND NOT OCCUPANCY
TIM 0 #50
Note: TIM0 (no space) on rung 1 is the contact reference for the done bit; the timer call on rung 3 is the two-token form TIM 0 #50.
Instruction List (IEC IL)
VAR
OCCUPANCY AT %I0.0 : BOOL;
DOOR_OPEN AT %Q0.0 : BOOL;
T_DOOR : TOF;
END_VAR
CAL T_DOOR(IN := OCCUPANCY, PT := T#5s)
LD T_DOOR.Q
ST DOOR_OPEN
TOF Quick Reference
| Dialect | Mechanism | Preset | Output |
|---|---|---|---|
| IEC 61131-3 | Native FB | PT := T#5s | T_DOOR.Q |
| Allen-Bradley | Native FB | PT:5000 (ms) | T_DOOR.Q (via XIC) |
| Siemens SCL | Native FB | PT := T#5s | T_DOOR.Q |
| Mitsubishi | 3-rung synthesis (latch + mirror + gated TON) | OUT T0 K50 (K × 100 ms) | M0 latch → Y0 |
| Schneider | Native FB | PT := T#5s | T_DOOR.Q |
| Delta | 3-rung synthesis (latch + mirror + gated TMR) | TMR T0 K50 (K × 100 ms) | M0 latch → Y0 |
| Omron | 3-rung synthesis (latch + mirror + gated TIM) | TIM 0 #50 (# × 100 ms) | LATCH → DOOR_OPEN |
| Instruction List | Native FB | PT := T#5s | T_DOOR.Q (via LD/ST) |
Native function block in four dialects, three-rung latch synthesis in the other three:
Practice TOF across all dialects: IEC lesson 9, Allen-Bradley lesson 9, Siemens lesson 9, Mitsubishi lesson 9, Delta lesson 9, Omron lesson 9.
The Synthesis Pattern — Why It Matters
The three dialects that synthesise R_TRIG and TOF — Mitsubishi, Delta, and Omron — collectively cover a large share of the Asia-Pacific market. If you work in Japanese, South-East Asian, or Australian manufacturing, you will encounter these platforms. Understanding the synthesis patterns does two things.
First, it gives you a transferable mental model. An R_TRIG function block is just a pair of rungs with a history bit. A TOF function block is a self-sealing latch, an output mirror, and a conditionally-gated on-delay timer. When you understand the decomposition, you can reconstruct it in any dialect — or debug it when someone else's implementation has a subtle scan-order error.
Second, it gives you insight into why scan order is not a stylistic preference. In the Mitsubishi and Delta R_TRIG synthesis, swapping the two rungs means the history bit is updated before the comparison, and the pulse never fires. In the TOF synthesis, removing the ANI X0 gate from rung 3 means the timer starts counting during occupancy, not after it — and the output drops mid-presence. These are not edge cases. They are the core of what makes PLC programming different from writing a function in Python.
Beyond Timers — Counters and Latches
The same naming divergence applies to the other two instruction families you reach for constantly. Counters (CTU up, CTD down) are largely consistent across IEC-style dialects but become numbered devices in Mitsubishi:
Set, reset and latch operations are the other family worth keeping on hand — note that Allen-Bradley uses OTL/OTU rather than the IEC S/R coils:
Using This Cheat Sheet
A few habits keep you from the classic cross-dialect mistakes when reading these tables:
Where to Learn
The Coding Tutor covers all three primitives — TON in lesson 8, R_TRIG in lesson 7, TOF in lesson 9 — across all eight dialects. Each lesson gives you a scenario, a starter, and a live I/O model that grades your solution.
The dialect comparison matrix shows how all eight dialects render the same program side by side — a useful companion to the per-lesson practice.