PLC Book Part III — Advanced Instructions & Data Handling Chapter 9
Chapter 9 Part III · Advanced Instructions & Data Handling Advanced ⏱ 50 min read ✦ 5 PLC Programs

09

Program Control Instructions

Up to now, the PLC has scanned your ladder logic exactly the way you wrote it — top to bottom, left to right, every rung every scan. Welcome to Part III, where that changes. Program control instructions let you bypass rungs, jump backwards, call reusable subroutines, freeze entire sections of code, force I/O immediately without waiting for the scan, and run urgent code on a strict time schedule. Used well, they make complex programs cleaner and faster. Used carelessly, they break the predictable scan model that makes PLCs reliable. This chapter shows you both — when to reach for these tools and when to leave them alone.

What you’ll be able to do after this chapter

Your goals for this chapter:

  • Explain how each program-control instruction modifies the normal top-to-bottom scan order.
  • Build a Master Control Reset (MCR) zone that drops every output in a section of program when a safety condition is broken.
  • Use JMP and LBL to skip rungs entirely — and explain how that’s different from MCR.
  • Write a subroutine (JSR / SBR / RET) for reusable code such as alarm logging or fault analysis.
  • Use Immediate Input (IIN) and Immediate Output (IOT) for fast safety responses that can’t wait for the next scan.
  • Recognise when forcing I/O from the programming software is appropriate — and when it’s a serious safety hazard.
  • Distinguish software interlocks from hardwired safety circuits, and explain why both are required for high-risk machinery.
  • Configure a Selectable Timed Interrupt (STI) to sample data or run control loops at a fixed time interval.
  • Use fault routines to handle runtime errors gracefully without losing the machine.
  • Apply Temporary End (TND) and Suspend (SUS) for testing, debugging, and trapping intermittent faults.

Key Concepts & Terms

Program controlScan flow MCR · Master Control ResetMCR zone JMP · JumpLBL · Label JSR · Jump to SubroutineSBR · SubroutineRET · Return Parameter passing IIN · Immediate InputIOT · Immediate Output I/O forcingForce value Hardwired safetySafety relaySIL / PL ratings STI · Selectable Timed InterruptPeriodic task Fault routineWatchdog timeout TND · Temporary EndSUS · Suspend
Section 9.1

Program Control — An Overview

Every PLC scan you’ve seen so far follows the same rigid pattern: read all inputs, execute every rung in order, write all outputs, repeat. That predictability is the PLC’s superpower — every machine engineer can look at a ladder program and know exactly when each rung will run.

Program control instructions bend that rule on purpose. They give you tools to:

  • Skip a section of rungs entirely (JMP / LBL).
  • Force every output in a section to OFF while still letting the rungs scan (MCR).
  • Call a reusable block of code from anywhere in the program (JSR / SBR / RET).
  • Read or write an I/O point right now, mid-scan, without waiting for the I/O update phase (IIN / IOT).
  • Schedule code to run at strict time intervals, independent of scan time (STI).
  • Catch runtime errors with dedicated fault-handler routines.
  • End the scan early for testing or freeze the CPU for diagnostics (TND / SUS).

Each instruction is a tool, and like any tool it has a right and a wrong way to be used. The chapter’s goal is to make you fluent with all of them, so you can pick the right one for any control problem — and just as importantly, recognise when you should leave them alone.

HOW PROGRAM CONTROL INSTRUCTIONS ALTER THE SCAN Normal scan flows top-to-bottom; control instructions divert that flow. NORMAL Read inputs Rung 0 Rung 1 Rung 2 Rung N Write outputs Repeat JMP / LBL Read inputs Rung 0 JMP → Rung 2 Rung 3 LBL · Rung N Write outputs Repeat MCR ZONE Read inputs MCR start Rung 1 (forced OFF) Rung 2 (forced OFF) MCR end Rung N Write outputs Repeat JSR / SBR / RET Read inputs Rung 0 JSR ↘ subroutine …sub runs… RET ↗ next rung Rung 2 Write outputs Repeat

Figure 9.1 — Scan flow with program control instructionsNormal scan goes straight down. JMP skips rungs. MCR scans them but forces outputs OFF. JSR detours into a subroutine and returns to the next rung.

Section 9.2

Master Control Reset (MCR)

An MCR zone is a section of program bracketed by two MCR instructions. The first MCR (at the top of the zone) sees the rung condition that controls the zone — typically your safety chain. The second MCR (at the bottom) closes the zone with an unconditional rung. Everything between them is “inside the zone”.

Two states matter:

  • Zone enabled — the first MCR’s rung is true. Rungs inside the zone execute exactly as if the MCR pair didn’t exist. Your normal logic runs and drives outputs as designed.
  • Zone disabled — the first MCR’s rung is false. All non-retentive outputs (OTE) inside the zone are forced to 0. The rungs are still scanned, but their OTE results are overridden. Latched outputs (OTL/OTU), timer accumulators (RTO), and counter accumulators retain their values — which is sometimes what you want, and sometimes a trap.

When to use MCR

Whenever a single condition — typically a safety interlock or a master “system enable” — should drive a whole section of outputs to a known-safe state. Light curtain breaks → MCR zone disables every motor in the cell. Safety relay drops → MCR zone disables every solenoid on a press. The advantage over wiring the safety bit into every rung is dramatic: you write it once, in the MCR rung, and it covers everything inside the zone.

Critical Limits

MCR is not a safety device.

  • An MCR zone disables outputs through software. If the PLC processor itself fails, the MCR cannot help.
  • For SIL- or PL-rated safety circuits, you must use hardwired safety relays in addition to (or instead of) MCR. See Section 9.7.
  • MCR does not zero retentive accumulators (RTO timers, counters). If your safety logic must reset those too, add explicit RES rungs alongside the MCR.
  • Never nest MCR zones inside each other — most platforms forbid it, and even when allowed it makes troubleshooting nearly impossible.
Section 9.3

Jump (JMP) and Label (LBL)

JMP tells the scan to skip ahead to a matching LBL instruction. When the JMP rung is true, the rungs between JMP and LBL are not scanned at all. They don’t run; they’re invisible to the processor for that scan. When the JMP rung is false, scan continues normally and reaches the rungs between JMP and LBL one at a time.

Each JMP/LBL pair shares an integer label number (e.g., LBL 03). You can have many JMP/LBL pairs in a program, each with its own number. You can also have multiple JMP instructions targeting the same LBL — different conditions all funnel to the same skip target.

The critical difference between JMP and MCR:

BehaviourJMP / LBLMCR zone
Are rungs scanned?No (skipped entirely)Yes, but outputs forced off
What happens to OTE outputs?Hold their last stateForced to 0
What happens to OTL/OTU latches?Hold their last stateHold their last state
What happens to TON/TOF timers?Frozen (their input rung never updates)Reset (their input rung sees forced-off condition)
Best forOptional code, speed optimisation, mode-specific logicMaster safety zone, “system enable” disabling many outputs at once

Why “hold their last state” matters

Imagine a motor controlled by an OTE inside a JMP-skipped section. If the JMP becomes true while the motor is running, the motor keeps running — its OTE rung doesn’t get scanned, so the OTE keeps writing whatever value it last wrote. This is rarely what you want for safety. For safety zones use MCR; for “this code only runs sometimes” use JMP.

JMP/LBL is also used for backward jumps (jumping to an earlier LBL) on some platforms — but most engineers avoid this because it’s the ladder equivalent of a goto loop and can blow past the watchdog timer if the loop never exits.

Section 9.4

Subroutines (JSR / SBR / RET)

A subroutine is a separate program file containing reusable code. It’s called from the main program with a JSR (Jump to Subroutine) instruction, executes from start to finish, then returns to the rung after the JSR. The same subroutine can be called from many places in the main program, or even from inside other subroutines.

Three instructions work together:

  • JSR — placed in the main program. Names the subroutine file to call (e.g., JSR file:U3). Optionally lists input parameters and output destinations.
  • SBR — placed at the very first rung of the subroutine file. Receives the input parameters listed in the JSR.
  • RET — placed at the end of the subroutine. Returns control to the rung after the JSR. Optionally returns output values to the caller.

When to write a subroutine

Whenever you find yourself copying the same logic into multiple places. The classic uses:

  • Diagnostic and alarm logic that runs the same way for every motor, valve, or station.
  • Scaling routines that convert raw analog values to engineering units — written once, called from every analog rung.
  • Recipe selection where one subroutine handles “load recipe parameters” and is called whenever a new recipe is selected.
  • Conditional logic that’s only relevant when a particular feature is enabled — wrap it in a subroutine and only call it then.

The big advantage

Test the subroutine once. Bug-fix it once. Improve it once. Every place that calls it gets the benefit. Compare with the alternative — copying the same 20 rungs into 15 different places — and a year later, finding a bug you have to track down 15 separate copies of. Subroutines are how you scale a program from “one machine” to “twenty machines on the same line”.

Subroutine pitfalls

Things to watch out for:

  • Overuse: a subroutine called from one place adds complexity without saving anything. Inline that code instead.
  • Side effects on shared bits: if two subroutines write to the same internal bit, the order of JSR calls determines the result — fragile and confusing.
  • Parameter mistakes: passing the wrong word to a parameterised JSR causes silent corruption that’s hard to trace.
  • Recursion: most platforms forbid a subroutine calling itself. Even when allowed, recursion can overflow the stack.
  • Conditional subroutine calls inside a fault routine: if the subroutine itself can fault, you’re inside a fault from another fault — usually unrecoverable.
Section 9.5

Immediate Input/Output Instructions (IIN / IOT)

Recall from Chapter 5 the three-phase scan: read all inputs into the input image, run all rungs, write the output image to all outputs. This means a typical input is read once per scan at the very beginning, and a typical output is written once per scan at the very end. For most applications, that’s plenty.

For some applications, that’s far too slow. An emergency-stop signal needs to disable outputs within milliseconds. A safety light curtain on a high-speed press cannot wait for the next scan to fire its stop relay. For these cases, two instructions break the normal scan rule:

  • IIN — Immediate Input. When this instruction executes, the PLC stops, reads the named input directly from the I/O card, updates the input image table for that bit, and continues. Subsequent rungs see the freshly-read value, not the value from the start-of-scan image.
  • IOT — Immediate Output. When this instruction executes, the PLC stops, writes the named output directly to the I/O card without waiting for the end-of-scan output update, and continues. The actual physical output changes state immediately.

Real-world impact

On a 20 ms scan, normal I/O updates can mean an input change isn’t reflected on an output until 40 ms later (read it next scan, output it the scan after). With IIN at the top of the program and IOT at the bottom of a critical rung, that delay drops to a fraction of a millisecond — fast enough for safety-critical bypasses.

Use IIN/IOT sparingly. Each instruction adds CPU overhead because it forces a hardware access mid-scan. Reserve them for genuinely time-critical signals — emergency stops, light curtains, safety mats, protective interlocks. Everything else is fine on the normal scan.

Section 9.6

Forcing External I/O Addresses

“Forcing” is a debugging feature of the programming software, not a ladder instruction. It lets the engineer override the actual hardware state of any input or output bit. A forced bit reads or writes whatever value the engineer set, regardless of what the wire or the program says.

Two flavours:

  • Force input ON / OFF — pretend the input is at the forced state. The CPU and your ladder logic see it as forced. Useful for testing rungs without having to physically actuate a switch or sensor.
  • Force output ON / OFF — drive the physical output to the forced state regardless of the rung result. Useful for proving wiring, testing actuators, and simulating fault conditions.

Every modern PLC programming environment shows forced bits with a clear visual indicator — usually a yellow halo or a “F” badge in the online monitor. There’s also typically a “forces enabled” indicator on the CPU itself (a flashing yellow LED on most Allen-Bradley CPUs) so anyone walking past the panel can see that the program is running with forces active.

Why forces are dangerous

Forces stay in effect until explicitly cleared.

  • If you force an output ON during commissioning and then leave the site without clearing the force, the output stays ON forever. You will have created a hidden, persistent override that no one looking at the ladder logic can see.
  • Forces survive program downloads on most platforms.
  • Forces can be lost on power cycle on some platforms — but not all. Always assume they persist.
  • Always clear all forces before leaving site, before final hand-over, before publishing a program update. Make it a checklist item.

The legitimate uses of forcing are testing, diagnostic work, and short-term troubleshooting. The illegitimate use is “patching” a wiring or programming problem with a permanent force instead of fixing the root cause. Don’t do that.

Section 9.7

Safety Circuitry — Why Software Alone Is Not Enough

Every PLC program you’ll ever write contains some logic that’s “safety-related” — a stop button, an interlock, a guard switch. The temptation is to handle all of it in ladder logic. After all, the PLC is reliable and the rungs are right there. But there’s a critical principle that every controls engineer must internalise:

A PLC is a single point of failure. If the CPU crashes, the watchdog locks up, or the program has a bug, every “safety” interlock in software is lost.

Real industrial safety circuits use hardwired protection that does not depend on the PLC at all. An emergency-stop button connects through a safety relay directly to the contactor coils that drive the motors. Press the E-stop and the motor contactors drop out — no PLC involvement, no software, no firmware. Even if the PLC is on fire, the motors stop.

Two-channel monitoring

Modern safety circuits use two independent channels through the safety relay. Both channels must agree, both must transition together when the E-stop is pressed, and both must reset together when the system is rearmed. If either channel fails — open wire, stuck contact, welded relay — the safety relay refuses to allow restart. This is the core of how SIL (Safety Integrity Level) and PL (Performance Level) ratings are achieved.

Where the PLC fits in the safety system

The PLC’s role in a properly engineered safety system is monitoring and indication, not protection. Bring the safety relay’s status contacts back to PLC inputs so the program knows whether the safety chain is enabled. Use those bits to drive HMI indicators, alarm logs, and software interlocks for non-safety functions. The actual disconnection of motor power happens in the hardwired safety circuit, not in the PLC program.

When MCR, JMP, and software interlocks are appropriate

For non-safety control logic — process interlocks, recipe-based bypasses, mode selection, optional features — software is fine. MCR zones are excellent for “system enable” type signals. JMP is excellent for mode-specific code. Just don’t use any of them as your only protection against a hazardous machine state. The hardwired safety relay is mandatory; the software is supplementary.

Section 9.8

Selectable Timed Interrupts (STI)

Most code in a PLC runs once per scan, and the scan time varies based on how much logic is true and how complex the math is. For most control jobs that’s fine. For some jobs — closed-loop control, motion profiles, fast data sampling — it’s not.

A Selectable Timed Interrupt is a special subroutine that runs at a strict time interval set by the engineer, regardless of where the main scan happens to be. Configure an STI with a 50 ms period and the PLC will execute that subroutine every 50 ms — interrupting the main scan if necessary, completing the STI’s logic, then resuming the main scan exactly where it left off.

What STIs are good for

  • PID control loops that need a fixed sample rate to stay tuned correctly.
  • Motion control calculations that depend on consistent timing.
  • Data logging at precise intervals for trending and analysis.
  • Filtering noisy analog signals using algorithms that assume a known sample period.
  • Communication polling where another device expects data at a fixed cadence.

STI cautions

STI rules to follow:

  • Keep the STI subroutine short. Whatever runs in the STI runs at the STI rate, not the scan rate. Slow STI logic eats CPU time fast.
  • Don’t share bits between main and STI without thinking. The STI can fire mid-rung in the main program. If both touch the same word, you can get torn reads.
  • Set the STI period larger than the worst-case STI execution time. Otherwise the STI starts before the previous one finishes — instant overload.
  • Never put a long communication or BSL/BSR shift in an STI. Those instructions can take many milliseconds; if they run inside a 10 ms STI, the CPU faults.
Section 9.9

Fault Routines

Sometimes a PLC encounters something it can’t handle in normal logic — a math overflow, a divide-by-zero, an array index out of range, a watchdog timeout, a comms-card fault. Without intervention, the CPU faults and the entire program halts. Every output drops, every motor stops, the operator sees a flashing red LED. Recovery requires a manual fault-clear and often a restart.

A fault routine is a special subroutine that the PLC executes automatically the moment a runtime fault occurs. Inside the fault routine you can:

  1. Log the fault — record the fault code, the rung that caused it, the timestamp.
  2. Drive critical outputs to a safe state — close valves, turn off heaters, etc.
  3. Try to clear the fault programmatically (some can be cleared, some cannot).
  4. Decide whether to continue running or halt for operator attention.

Fault routines are typically configured in the controller properties (not as a normal subroutine call). The CPU knows to invoke them automatically when a fault is detected.

A practical example

A line uses an analog flow meter. One day, a wire breaks and the flow meter reports an out-of-range value. Without a fault routine, the next time the program tries a math operation on that value, it overflows — CPU faults, every motor stops, operator confusion. With a fault routine: detect the overflow, log “flow meter fault on AI 1.3”, set a substitute flow value, raise an alarm, continue running on the substitute. The operator gets a clear alarm, the production line keeps moving, and the maintenance team can repair the wire without an emergency.

Section 9.10

Temporary End (TND)

The Temporary End instruction does exactly what its name says — it temporarily ends the program scan. When TND is true, the rungs after TND are not executed for that scan. The PLC jumps to the end-of-scan output update, sends outputs, and starts the next scan from rung 0 as normal.

TND is a development and testing tool, not a feature you’d typically leave in production code. Two common uses:

  • Section-by-section testing. When developing a long program, place a TND temporarily after the section you’re working on. The rungs below don’t run, so you can test just the section above without the rest of the program interfering.
  • Conditional bypass. Place a TND with an XIC of a “test mode” bit. When in test mode, only the rungs above the TND run; production rungs below are skipped. Toggle the bit off and the full program runs.

TND vs. JMP/LBL

TND ends the scan. JMP/LBL just skips a section. The difference matters: with TND, every rung after it is bypassed for the entire scan. With a JMP/LBL pair, you only skip the section between them, and rungs after the LBL still run normally. Use JMP/LBL for “skip this part”; use TND for “stop here entirely”.

Section 9.11

Suspend (SUS)

The Suspend instruction is the diagnostic tool of last resort. When SUS is true, the PLC immediately halts execution, captures the entire CPU state to non-volatile memory, and stops. All outputs go to their failure-safe state. The CPU’s status LEDs change to indicate “suspended”. An engineer connecting with the programming software sees the captured state and can analyse exactly what was happening at the moment SUS fired.

Each SUS instruction has a suspend ID — an integer the engineer chose when writing the rung. When SUS halts the CPU, the suspend ID is recorded so you know which SUS instruction fired (you can have many in the program, each with a different ID).

When SUS is appropriate

SUS exists for catching intermittent faults that are too rare to reproduce on demand. Imagine a machine that mysteriously stops every two or three days for no apparent reason. You suspect a particular condition is causing it but can’t catch the moment in real time. Add a SUS rung that fires when that condition is detected. Next time it happens, the PLC freezes itself with the entire program state captured — every register, every bit, every counter and timer ACC. You connect the laptop, look at the snapshot, and find the cause.

SUS rules

Things to remember about SUS:

  • SUS halts production. Never leave a SUS rung in production code unless you genuinely want the line to stop and freeze when the condition fires.
  • Recovery requires a manual restart from the programming software or operator panel. SUS isn’t self-clearing.
  • Document each SUS with a comment explaining what condition triggered it and what the engineer should look at when it fires. A future engineer who finds a halted PLC with no documentation is in for a long debugging session.

Worked PLC Programs

Five Programs — From an MCR Safety Zone to a Timed Sampling Subroutine

Each program below shows one of the program-control instructions used the right way: a real industrial requirement, a clean rung structure, and an explanation of the trade-offs. Pay attention to the safety notes — for these instructions, knowing when not to use them is as important as knowing how.

01

PLC Program · MCR safety zone

Robot Cell Master Control — Drop All Outputs on Light-Curtain Break

MCR

The problem: a robot cell contains four motorised axes, two solenoid grippers, and a conveyor. A light curtain across the cell entrance must, when broken, drop every one of those outputs to OFF. Rather than wiring an XIO of the light-curtain bit into seven separate rungs, we wrap the cell’s output rungs in an MCR zone driven by the light-curtain status. One rung at the top of the zone protects everything inside.

Inputs & Outputs

INPUTS

I:1/0 — Light_Curtain_OK (1 = beam intact)

I:1/1 — Cell_Enable selector

I:1/2 to I:1/5 — Axis position bits etc.

OUTPUTS

O:2/0O:2/3 — Axis 1–4 motors

O:2/4, O:2/5 — Solenoid grippers

O:2/6 — Conveyor

All forced OFF when MCR zone is disabled.

Ladder Diagram (Five Rungs Shown)

L1 L2 000 Light curtain I:1/0 Cell enable I:1/1 MCR START ZONE 001 — inside MCR zone — Axis 1 cmd I:1/2 O:2/0 Axis 1 motor 002–005 …axis 2, axis 3, axis 4, gripper, conveyor rungs (similar shape)… 006 MCR END ZONE 007 XIO LC OK I:1/0 O:3/7 Alarm horn

The MCR pair (rungs 0 and 6) brackets all output rungs in between. The alarm horn (rung 7) sits outside the zone so it can fire when the zone is disabled.

How it works

  1. Light curtain intact, cell enabled. Rung 0’s MCR sees its rung true. Rungs inside the zone (1–5) execute as normal logic. Whatever drives Axis 1’s command bit (I:1/2) drives the Axis 1 motor (O:2/0); same for the other axes, grippers, and conveyor.
  2. Light curtain breaks (someone walks into the cell). I:1/0 goes to 0. Rung 0’s MCR rung is now false, the zone is disabled. Every OTE inside the zone is overridden to 0 — every motor, every solenoid, the conveyor. All physical outputs drop simultaneously.
  3. Rung 7 (outside the zone) keeps running normally. Its XIO of the light-curtain bit fires, energising the alarm horn to alert the operator. This rung must be outside the MCR zone — if it were inside, it would also be forced off, and we’d lose the alarm exactly when we needed it.
  4. Operator clears the obstruction. Light curtain restores, MCR rung true again, zone re-enables. Outputs are not automatically re-energised — each rung’s logic must be true on its own merits before its output comes back. Latched outputs (OTL) hold their state through the disable; that’s a deliberate trade-off — see safety note below.
What we learned: wrap a section of related output rungs in an MCR pair driven by your safety bit. One rung up top covers every output inside. Place alarm/diagnostic rungs OUTSIDE the zone — that’s the only way they keep working when the zone is disabled. And remember: the MCR is supplementary; the actual safety disconnect of motor power must be hardwired through a safety relay, per Section 9.7.
02

PLC Program · JMP / LBL

Mode Selector — Skip Auto-Mode Logic in Manual Mode

JMP / LBL

The problem: a packaging machine has two operating modes selected by a key switch — AUTO (full sequence: count, fill, cap, label) and MANUAL (operator drives each station individually with pushbuttons for setup and maintenance). In manual mode, the entire AUTO-sequence section of the program should be skipped — both to prevent it from interfering with manual commands and to save scan time.

Inputs & Outputs

INPUTS

I:1/0 — Mode switch (1 = AUTO, 0 = MANUAL)

I:1/1I:1/4 — Manual jog buttons

I:1/5I:1/8 — AUTO-sequence sensors

OUTPUTS

O:2/0O:2/3 — Station solenoids

B3:0/x — Internal sequence bits (used by AUTO logic)

Ladder Diagram (Outline)

L1 L2 000 — Manual jog rungs (always run) — I:1/1 Manual jog I:1/0 XIO Auto O:2/0 Sol 1 001 XIO · Auto mode I:1/0 JMP → LBL 03 002–010 — AUTO sequence rungs (skipped when JMP fires) — count fill cap label, internal sequence bits, recipe lookup, etc. 011 LBL 03 scan resumes here · alarm/HMI rungs continue normally below

In MANUAL mode, the JMP at rung 1 fires (XIO of AUTO bit passes) and scan jumps directly to LBL 03 — bypassing the entire AUTO-sequence section.

How it works

  1. AUTO mode (key switch in AUTO position). I:1/0 = 1. Rung 0’s manual-jog logic still runs but is gated by an XIO of the AUTO bit, so manual buttons do nothing. Rung 1’s JMP rung is false (XIO of AUTO blocks). Scan continues into the AUTO sequence. AUTO sequence rungs run as designed and drive the outputs.
  2. MANUAL mode (key switch in MAN position). I:1/0 = 0. Rung 0’s manual-jog logic now allows the operator to drive the outputs directly. Rung 1’s JMP rung is true (XIO passes). The PLC jumps directly to LBL 03 at rung 11, skipping every AUTO-sequence rung in between. Those rungs don’t run at all this scan, so AUTO-mode internal bits and stage flags don’t fight the operator’s manual commands.
  3. Alarm and HMI rungs after LBL 03 run regardless of mode — they need to in both. Things like “report mode to HMI”, “log button presses”, “watchdog timer” must always run.

Why JMP and not MCR?

If we used MCR around the AUTO section, the rungs would still scan and any internal bits inside would be forced off. That would break the AUTO sequence the next time the operator switched back to AUTO — sequence stages would all start from scratch. With JMP, the AUTO sequence rungs simply don’t run while in MANUAL mode, but their internal bits keep their last values. When the operator switches back to AUTO, the sequence resumes from where it left off (or you can use a separate “restart” button to clear the stage bits — your choice).

What we learned: JMP/LBL is the right tool for “skip a section” when you want the rungs simply not to execute. Use MCR when you want them to scan but with their outputs forced off. Use neither for safety — the JMP can still fail in software, and a stale value on a held output is no protection against a hazard.
03

PLC Program · JSR / SBR / RET

Reusable Motor-Diagnostic Subroutine

SUBROUTINE

The problem: a machine has six motors, and for each one we want the same diagnostic logic — check the auxiliary contactor feedback, compare it to the command bit, raise an alarm if they disagree for more than 2 seconds (start fault), log the fault number to the alarm queue. Rather than copying 8 rungs of diagnostic logic six times (48 rungs total, all near-identical), we write the diagnostic once as a subroutine and call it six times — passing the motor’s command bit, feedback bit, and a unique fault ID each time.

Inputs & Outputs (per motor)

JSR INPUTS (passed)

Command bit — the motor’s commanded state (output bit)

Feedback bit — auxiliary contact input

Fault ID — integer 1–6

JSR OUTPUTS (returned)

Fault flag — 1 if disagreement > 2 s

Internal: writes fault ID to alarm queue N7:50

Main Program Calls

L1 L2 000 JSR — Motor Diagnostic Subroutine file:U3 · cmd O:2/0 · fb I:1/0 · ID 1 · fault B3:5/0 001 JSR — Motor Diagnostic Subroutine file:U3 · cmd O:2/1 · fb I:1/1 · ID 2 · fault B3:5/1 …rungs 002–005: same JSR called for motors 3, 4, 5, 6 with different params… 006 EQU — Equal B3:5 ≠ 0 (any fault) O:3/0 Master alarm

Six near-identical JSR rungs call the same diagnostic subroutine with different parameters. The fault flags B3:5/0 to B3:5/5 reside in one word; if any are non-zero, the master alarm fires.

Inside the Subroutine (file U3)

L1 L2 000 SBR — Subroutine Receive params: cmd → bit, fb → bit, ID → N7:0, fault → bit 001 XOR of cmd and fb feeds a 2-second TON — if they disagree for that long it’s a fault. XOR — cmd ⊕ fb → disagree bit TON · 2.0 s → fault 002 RET return to caller

SBR receives the parameters at the top; the diagnostic logic uses them; RET sends the result back to the caller.

How it works

  1. Main scan reaches rung 0. JSR fires. The PLC pushes its current rung index, jumps to the start of file U3, and binds the parameters in the JSR rung to the parameter slots in the SBR rung.
  2. The subroutine runs. Inside U3, the cmd bit and fb bit refer to motor 1’s actual addresses. The XOR detects disagreement; the TON times it. If the disagreement persists for 2 seconds, the fault flag is set and the fault ID (1) is logged.
  3. RET fires. The PLC pops the saved rung index and resumes the main scan at rung 1.
  4. Rung 1 fires the same JSR with motor 2’s parameters. Same code runs, but now cmd, fb, ID, and fault bits all point to motor 2. Same diagnostic logic, separate state.
  5. This repeats for motors 3–6. Six calls to the same 5-rung subroutine give us 30 rungs of effective diagnostic coverage with one piece of code to maintain.
What we learned: a JSR with parameter passing turns “the same logic with different addresses” from a copy-paste maintenance nightmare into a reusable, testable, single-source-of-truth function. Every motor diagnostic is identical because they all run the same code; if you improve the diagnostic, every motor benefits at once. This is the closest a PLC gets to writing a function in a programming language — and it scales from 6 motors to 60 with no extra code.
04

PLC Program · IIN / IOT

High-Speed Press — Sub-Millisecond Software E-Stop Bypass

IMMEDIATE I/O

The problem: a high-speed stamping press has a primary safety circuit (hardwired safety relay → contactor coil) that handles the actual motor cut-off. But the PLC also drives an auxiliary “press-armed” solenoid and a number of HMI indicators that should drop within 1 ms of an emergency-stop signal — far faster than the typical 15 ms scan would allow. We use IIN at the start of each scan to read the E-stop bit immediately, and IOT at the bottom of the safety-response rung to write the auxiliary outputs immediately, bypassing the normal scan latency.

Important context

IIN/IOT supplements hardware; it does NOT replace it.

  • The actual motor power cutoff comes from the hardwired safety relay, not from this rung.
  • If the PLC CPU itself fails, IIN/IOT fails too. Hardware safety must work even when the PLC is dead.
  • This program improves response time of secondary actions — auxiliary solenoids, status indicators, soft holds — that benefit from being faster than scan rate but aren’t safety-rated by themselves.

Inputs & Outputs

INPUTS

I:1/0 — E-Stop circuit (NC, 1 = circuit healthy)

I:1/1 — Press position sensor

OUTPUTS

O:2/0 — Press-armed solenoid (auxiliary)

O:2/1 — HMI “fault” indicator

B3:0/0 — E-stop fault latch

Ladder Diagram (Four Rungs)

L1 L2 000 IIN — Immediate Input refresh I:1/0 (E-Stop) right now, mid-scan 001 XIO E-stop OK I:1/0 L B3:0/0 OTL · Fault 002 XIO Fault B3:0/0 O:2/0 Press armed 003 IOT — Immediate Output push O:2/0 to physical card right now, mid-scan

IIN at rung 0 reads the E-stop bit immediately. The fault latch and output rung run with the fresh value. IOT at rung 3 forces the output card to update before the scan completes.

How it works (timing breakdown)

  1. Without IIN/IOT: If the E-stop opens at the worst moment (just after the start-of-scan input update), the PLC won’t read the change until the next scan starts. With a 15 ms scan, that’s up to 15 ms of latency on the read, plus another ~15 ms of latency before the output card writes the result. Total: up to 30 ms before the press-armed solenoid drops.
  2. With IIN at rung 0: The very first thing the PLC does is re-read the E-stop input directly from the I/O card. The latency on the read drops to a fraction of a millisecond regardless of where the rest of the scan was.
  3. With IOT at rung 3: The instant the rung result is determined, the IOT writes the output card directly. The latency on the write drops to a fraction of a millisecond.
  4. Result: total worst-case latency from E-stop open to press-armed drop is now well under 1 ms — fast enough that the auxiliary solenoid effectively drops at the same instant as the hardwired safety relay.
What we learned: use IIN/IOT to slash latency on signals that must respond faster than the scan rate. Place IIN at the very top of the program for inputs that drive critical decisions; place IOT immediately after the rung that determines a critical output value. Reserve these instructions for genuinely time-critical signals — overuse adds CPU overhead. And remember: the actual safety-rated power disconnect comes from the hardwired safety relay, not from any IIN/IOT pair.
05

PLC Program · STI · Selectable Timed Interrupt

100 ms Analog Sampling Subroutine for Trend Logging

STI

The problem: a chemical reactor’s temperature must be sampled and logged exactly every 100 ms for later trend analysis and PID-loop tuning. The main program’s scan time fluctuates between 8 ms and 22 ms depending on alarm activity, so we cannot use the main scan to drive a fixed sample rate. Instead, we configure a Selectable Timed Interrupt with a 100 ms period, and write a small subroutine that reads the analog input, scales it to engineering units, and stores the value in a circular buffer.

Inputs & Outputs

INPUT (read in STI)

I:3.0 — Reactor RTD raw value (4–20 mA card)

STI WRITES

F8:0 — Reactor temperature in °C (display)

F8:10F8:209 — 200-sample circular buffer (20 s of history at 100 ms)

N7:0 — Buffer write index (0–199)

STI Configuration

Where to set the period

STI configuration lives in the controller properties (or status file S:30 on Allen-Bradley SLC), not in the ladder logic itself. You set STI period = 100 ms and STI subroutine file = U4. After download, the CPU executes file U4 every 100 ms regardless of where the main scan happens to be.

STI Subroutine (file U4)

L1 L2 000 SCP — Scale Raw to °C I:3.0 [4000–20000] → F8:0 [0.0–250.0 °C] 001 COP — Copy Sample to Buffer F8:0 → F8:[10 + N7:0] 002 ADD — Increment Buffer Index N7:0 + 1 → N7:0 003 GEQ — Greater Equal N7:0 ≥ 200 CLR · N7:0 → wrap to 0 (Implicit RET at end of STI subroutine — control returns to interrupted main scan.)

Four short rungs: scale, copy to circular buffer, increment index, wrap at 200. Total execution time well under the 100 ms STI period.

How it works

  1. STI period reaches 100 ms. CPU pauses the main scan wherever it is, jumps into file U4.
  2. Rung 0 — Scale. SCP reads I:3.0 raw count and converts to a calibrated temperature in F8:0 (0.0 to 250.0 °C). The HMI continuously displays this value.
  3. Rung 1 — Buffer write. COP copies the current F8:0 into the array slot at index N7:0. After 100 ms the next sample lands in the next slot.
  4. Rung 2 — Increment. ADD bumps the index by 1.
  5. Rung 3 — Wrap. If the index reaches 200, CLR resets it to 0. The buffer holds the last 200 samples — exactly 20 seconds of history at 100 ms sampling.
  6. Implicit RET at the end of file U4 returns control to the interrupted main scan — which resumes exactly where it was paused.

Why this needs an STI

If we put the same logic in the main program, samples would be taken at scan rate — 8 ms one cycle, 22 ms the next, 13 ms the one after. Trend analysis would be useless because the timestamps would be irregular. The STI guarantees a sample exactly every 100 ms regardless of scan time. PID loops, motion control, and any signal-processing math depend on this consistency.

What we learned: use an STI for any code that must run on a strict time schedule. Configure the period in the controller properties (not in ladder logic), keep the STI subroutine short, and never share writeable bits between the STI and the main scan without careful synchronisation. The STI is the PLC equivalent of a hardware timer interrupt — extremely useful when used for the right job, dangerous when overused.

Common Student Mistakes

Things to watch out for in this chapter:

  • Treating MCR as a safety device. MCR is software, and any software interlock can fail when the PLC fails. Real safety requires hardwired safety relays. Use MCR for “drop these outputs together when the master condition is broken” — not as your only line of defence.
  • Forgetting that JMP-skipped rungs hold their last state. If a motor was running when the JMP fired, it keeps running. Use MCR (which forces outputs off) when “skip and disable” is what you want, not “skip and continue”.
  • Nesting MCR zones. Most platforms forbid this and the ones that allow it produce code that’s almost impossible to debug. Keep zones flat and non-overlapping.
  • Calling a subroutine with the wrong parameter mapping. A misaligned parameter list silently corrupts data. Always test each JSR with its parameters before assuming the subroutine is reusable.
  • Using IIN/IOT everywhere “to be fast”. Each immediate I/O instruction stops the CPU mid-scan to access the I/O card. Overuse adds latency to the rest of the program. Reserve them for genuinely time-critical signals.
  • Leaving forces in production code. Forces persist until cleared and they don’t appear in the ladder. A forced output is invisible to anyone reading the program. Always clear forces before leaving site.
  • Putting slow logic in an STI. The STI subroutine runs every period — if its execution time exceeds the period, the CPU faults. Keep STIs short and predictable; never put communication or BSL/BSR shifts in them.
  • Sharing bits between STI and main scan without protection. The STI can fire mid-rung in the main program. Use atomic-update patterns (set a single bit at a known point in the STI, read it once at a known point in the main scan) to avoid torn reads.
  • Forgetting the SUS instruction is still in production code. A leftover SUS rung from debugging will halt the line if its condition fires months later. Always remove SUS rungs before going to production.

Quick Recap

The ten things to take away from Chapter 9:

  • Program control instructions bend the normal scan order. They give you tools to skip rungs, force outputs off, call subroutines, do urgent I/O, run timed code, handle faults, and pause execution.
  • MCR zones force every OTE output inside the zone to OFF when the start-MCR rung is false. Excellent for system-enable safety logic, but not a substitute for hardwired safety relays.
  • JMP/LBL skips a section of rungs entirely — they don’t run at all. Outputs in the skipped section hold their last state, so don’t use it for safety.
  • JSR/SBR/RET subroutines turn repetitive logic into reusable functions. Pass parameters in, get results out, write the logic once, call it many times.
  • IIN/IOT read or write a specific I/O point immediately, mid-scan. Drops latency from full-scan times to under 1 ms. Reserve for time-critical signals only.
  • Forcing I/O overrides hardware state from the programming software. Useful for testing; dangerous if left in place. Always clear forces before leaving site.
  • Hardwired safety using safety relays must protect any hazardous output. Software interlocks (MCR, JMP, ladder logic) are supplementary, not primary.
  • Selectable Timed Interrupts run a subroutine on a strict time schedule regardless of main scan. Use for PID loops, fixed-rate sampling, motion math, and communication polling.
  • Fault routines catch runtime errors automatically — math overflows, watchdog timeouts, hardware faults — and let you log, alarm, and recover gracefully.
  • TND ends the scan early for testing. SUS halts the CPU and captures a snapshot for diagnosing intermittent faults. Both are development tools — remove from production code.

Review & Self-Assessment

Chapter 9 Review Questions

Try answering each question on your own first. Tap to reveal the answer when you’re done.

Q1What is the key behavioural difference between MCR and JMP/LBL?+
MCR scans the rungs but forces every OTE output inside the zone to OFF when the zone is disabled. JMP/LBL does not scan the rungs at all when the JMP is true — those rungs are skipped, and any outputs they drive hold their last state. MCR is for “make these outputs go off together”; JMP/LBL is for “don’t run this section right now”.
Q2Why is MCR not a substitute for hardwired safety circuits?+
MCR is implemented in software — it depends on the PLC CPU running correctly. If the CPU faults, the firmware has a bug, the watchdog locks up, or the program is corrupted, the MCR cannot drop the outputs. Real industrial safety requires hardwired safety relays connected directly to motor contactor coils so that motor power drops even when the PLC is dead. The PLC’s role in safety is monitoring and indication; the actual disconnect must be hardware.
Q3What three instructions together implement a subroutine, and what does each one do?+
JSR (Jump to Subroutine) sits in the calling program; it specifies which subroutine file to execute and lists any parameters to pass. SBR (Subroutine) sits as the first rung of the subroutine file; it receives the parameters from the JSR. RET (Return) sits at the end of the subroutine; it returns control to the rung after the JSR and optionally returns output values.
Q4An OTE inside an MCR zone is currently energised. What happens to it the moment the zone is disabled?+
It’s forced to 0 immediately. The rung is still scanned, but the OTE result is overridden. As long as the zone remains disabled, every OTE inside is held off regardless of what the rung’s input logic says. When the zone re-enables, OTE rungs return to following their input logic — they don’t automatically come back on; the inputs must allow them.
Q5A 20 ms scan PLC has an emergency-stop signal. What is the worst-case latency without immediate I/O? With IIN at the top of the program and IOT after the safety rung?+
Without IIN/IOT: the input is read at the start of scan, processed mid-scan, output written at end of scan. Worst case the E-stop opens just after the start-of-scan read, so the change isn’t seen until the next read — up to ~20 ms — plus another ~20 ms before the output is written: up to ~40 ms. With IIN at top + IOT at the safety rung: the input is re-read directly within microseconds of program start, and the output is written within microseconds of the rung’s completion: total well under 1 ms.
Q6Why is forcing an output during commissioning considered dangerous if you forget to clear it?+
A forced output stays at the forced state regardless of what the ladder logic says. Anyone reading the ladder program later sees the rung normally — they have no way to know that the output is being held in a fixed state by an invisible force. The result is a permanent override that doesn’t appear in the program, and the next engineer to troubleshoot the system can spend hours wondering why the output isn’t responding to the logic. Always clear all forces before leaving site, and check the “forces enabled” LED on the CPU before final hand-over.
Q7What kind of code belongs inside a Selectable Timed Interrupt subroutine, and what kind doesn’t?+
Belongs in an STI: code that needs a strict, predictable execution interval — PID loop calculations, fixed-rate sampling, motion math, communication polling at a fixed cadence. Does NOT belong in an STI: any code that takes a substantial fraction of the STI period (slow communication, large COP/FAL/BSL operations, complex math), anything that touches shared bits with the main program without explicit synchronisation, and anything that’s not strictly time-critical (move it to the main scan instead).
Q8What is a fault routine, and how does it differ from a regular subroutine?+
A fault routine is a special subroutine that the PLC executes automatically when a runtime error is detected — math overflow, divide-by-zero, watchdog timeout, hardware fault. It’s configured in the controller properties rather than called by a JSR. Inside the fault routine you can log the fault, drive outputs to safe state, attempt to clear the fault, and decide whether to continue running or halt for operator attention. A regular subroutine is called explicitly by JSR; a fault routine is called by the PLC firmware in response to a fault.
Q9When would you use TND (Temporary End) instead of JMP/LBL?+
Use TND when you want to end the scan entirely from this point on. Every rung after a true TND is bypassed for that scan, and the CPU jumps directly to the output update. Useful for: section-by-section debugging (place a TND just below the section under test), conditional bypass during commissioning (“test mode” bit causes TND to fire), or experimenting with a partial program without disturbing the rest. JMP/LBL only skips between two specific points; TND skips everything from itself to the end of the program.
Q10What happens to internal bits and timer accumulators inside a JMP-skipped section while the JMP is true?+
They hold their last values. The skipped rungs don’t run, so nothing updates them. A timer that was actively counting before the JMP fired will freeze — its accumulator stays where it was (it’s not even decremented or reset). This is unlike MCR, where TON/TOF timers see their input rung as forced-off and therefore reset their accumulators. If you want timers and counters to continue normally while a section is skipped, structure them outside the JMP.
Q11A subroutine writes to internal bit B3:0/0. The subroutine is called from three different places in the main program. What problem could this cause?+
Each call writes B3:0/0, so by the time the third call finishes, the bit only reflects the result of the third call’s logic — the first two calls’ results are lost. This is the classic “shared write” subroutine bug. The fix is to either pass an output parameter so each call writes to a different destination bit (as in Worked Example 3, where each motor’s fault flag is a different B3:5/n bit), or to redesign the subroutine to never write shared bits at all. Inputs can be safely shared between calls; output bits should always be unique per call.
Q12Describe an appropriate use case for the SUS (Suspend) instruction, and what makes it different from TND.+
SUS is used to catch rare, intermittent faults that are too infrequent to debug live. You write a SUS rung whose condition fires only when the suspected fault occurs (e.g. “axis position error > 5 mm AND motor command was zero”). When that fault next happens, the PLC immediately halts and captures the entire CPU state — every register, every bit, every accumulator. An engineer with the programming software can then analyse exactly what was happening at the moment of the fault. Difference from TND: TND only ends the current scan and the PLC continues running normally on the next scan. SUS halts the entire CPU until manually restarted, making it suitable for offline diagnostic analysis but unsuitable for any production code where you don’t want the line to stop.

Need help architecting a safety-rated control system or writing reusable subroutines?

Get one-on-one tutoring or design-review help from Dr Ahsan Rahman — Head of Electrical Engineering, with two decades of teaching advanced PLC architecture, functional safety, and program-control practices.

Request Consultation →