Siemens S7 TON Multi-Instance FB Resolving PT Parameter

David Krause11 min read
S7-300SiemensTroubleshooting
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Problem Overview

When programming a multi-instance Function Block (FB) in STEP 7 Classic (S7-300/S7-400), engineers frequently encounter a fault where the preset time PT of a TON (on-delay timer) declared as a static variable of type TON, TP, or TOF cannot be assigned in the LAD/FBD editor. Typical symptoms include:

  • The MOVE box on the timer's PT input is rejected by the compiler with "Type check error" or "Invalid data type".
  • The PT input on the CALL coil shows as a missing connection and the block will not compile to the PLC.
  • Modifying the static instance #STAT0.PT directly succeeds in STL but is flagged as bad practice and may behave inconsistently in LAD/FBD.
  • The PT value appears correctly in the instance DB after compilation, but the timer does not produce the expected Q pulse because the time was never latched into the timer's working area before the first CALL.

This is one of the most common compile-time errors encountered when converting a single-instance TON program to a reusable multi-instance FB architecture.

Root Cause Analysis

The root cause is a violation of the FB encapsulation rule established by STEP 7 since V5.x. The rules are:

  1. Static data belonging to an instance (declared in the STAT section of an FB) is private to that FB. It may be read and written only by the code body of the FB that owns the instance.
  2. All input, output, and in-out data of an FB must be exchanged through its formal parameter interface (IN, OUT, IN_OUT, STAT, TEMP) using the parameter syntax of the CALL instruction.
  3. Direct symbolic access to a static component from outside the FB (e.g., from OB1 or from another FB's body) is treated as a compile-time error when type checking is enabled.

When you declare a multi-instance timer in the STAT section:

VAR
   STAT0 : TON;      // multi-instance timer
   STAT1 : TP;       // pulse timer
   STAT2 : TOF;      // off-delay timer
END_VAR

…you must drive STAT0.IN, STAT0.PT, and read STAT0.Q/STAT0.ET through the FB's CALL interface, not by addressing the components directly. The LAD/FBD editor enforces this rule and refuses to wire a MOVE box to #STAT0.PT in the parent FB's network unless type checking is suppressed.

Why the error is intentional

Allowing direct writes to STAT0.PT from outside the FB breaks several design guarantees:

  • Reentrancy: Multi-instance FBs share code but each call uses its own instance data. External code that writes STAT0.PT may collide with the FB's internal scheduling.
  • Maintainability: An outside caller can corrupt timer state without the FB knowing, producing intermittent faults that are extremely difficult to trace.
  • Reusability: Encapsulation lets the same FB be copied between projects without dragging hidden dependencies on the parent's instance DB layout.

Affected Platforms and Firmware

Controller family STEP 7 version Editor Behavior
S7-300 (CPU 312C–319F) V5.4 / V5.5 / V5.6 LAD/FBD/STL Type check active by default; compile error in LAD/FBD
S7-400 (CPU 412–417) V5.4 / V5.5 / V5.6 LAD/FBD/STL Same as S7-300
WinAC RTX V5.4 / V5.5 LAD/FBD/STL Same as S7-300
S7-1200 (TIA Portal V13–V20) TIA Portal LAD/FBD/SCL Multi-instance IEC timers; same encapsulation rules, but SCL is the preferred text language
S7-1500 (TIA Portal V13–V20) TIA Portal LAD/FBD/SCL Multi-instance IEC timers; optimized DB access for IEC timers in firmware ≥ V2.0
Note: On S7-1200 and S7-1500 the TON instruction is documented in the TIA Portal help under "Timer operations S7-1200/S7-1500 / TON: Generate on-delay". The conceptual rule — drive IN and PT through the formal call interface, not by writing to the instance components directly — is unchanged. See the official Siemens reference at TON: Generate on-delay (S7-1200, S7-1500) - TIA Portal V20.

Solution 1 — Pass PT Through the FB Interface (Recommended)

Declare PT as a formal IN parameter on the multi-instance FB and assign the time constant at the call site. This is the engineering best practice and the only method that compiles cleanly in LAD, FBD, and STL with type checking enabled.

FB interface (declaration view)

VAR_INPUT
   Start      : BOOL;          // trigger input
   Duration   : TIME;          // preset time
END_VAR

VAR_OUTPUT
   Running    : BOOL;          // Q of TON
   Elapsed    : TIME;          // ET of TON
END_VAR

VAR
   STAT0      : TON;           // multi-instance timer
END_VAR

BEGIN
   STAT0(IN := Start, PT := Duration, Q => Running, ET => Elapsed);
END_FUNCTION_BLOCK

Calling OB1 / parent FB

      CALL  FB_TimerControl
        Start    := I0.0
        Duration := T#500ms
        Running  := Q0.0
        Elapsed  := MW10

Or, using the IEC-style parameter call supported in SCL and modern LAD/FBD editors:

      %FB1_DB(
        Start    := I0.0,
        Duration := T#500ms,
        Running  => Q0.0,
        Elapsed  => MW10
      );

Why this works

The CALL instruction copies the value of Duration into the PT field of the instance owned by FB_TimerControl before the timer logic executes. The timer body itself reads the new PT on the next scan, so the Q output is produced after exactly the requested interval. Because the FB declares Duration as an IN, external code cannot accidentally overwrite STAT0.PT.

Solution 2 — STL Direct Assignment (Legacy Workaround)

STEP 7 STL does not enforce the LAD/FBD type-check restriction, so you can write directly to the static instance:

      A   I 0.0
      =   #STAT0.IN
      L   T#500ms
      T   #STAT0.PT
      CALL  #STAT0
        IN:=
        PT:=
        Q :=
        ET:=

This compiles, downloads, and runs. However, it is considered legacy code and is explicitly discouraged in modern Siemens programming style guides. Use it only when:

  • You are maintaining an existing project and cannot change the FB interface.
  • You are running an older CPU with firmware that does not fully support formal parameter CALL in the desired editor.
  • You need to test timer behavior in isolation before adding the FB interface.

Solution 3 — Intermediate TIME Variable in TEMP

To stay in LAD/FBD without disabling the type check, declare a TEMP variable of type TIME and use it as a buffer:

VAR_TEMP
   tmp_pt : TIME;
END_VAR

BEGIN
   tmp_pt := T#500ms;          // MOVE box accepts TIME constant in LAD/FBD
   CALL #STAT0
      IN   := I0.0
      PT   := tmp_pt
      Q    => M0.0
      ET   => MW2
END_FUNCTION_BLOCK

The compiler accepts tmp_pt as a TIME, so the formal parameter wire is type-correct. This is the cleanest "stay in graphical editor" workaround.

Solution 4 — Initialize PT via the Instance DB (VAT)

If the PT value is constant and known at commissioning time, write it once into the instance DB using the Variable Table (VAT) tool:

  1. Open the multi-instance FB's instance DB in STEP 7.
  2. Open Monitor / Modify > Variable Table (VAT).
  3. Enter the path: DB100.DBX[STAT0.PT offset] or, symbolically, "DB_Timer".STAT0.PT.
  4. Type the value: T#500ms (or T#1m30s, etc.).
  5. Click Modify to write the constant into the loaded DB.

The timer then runs with that preset on every subsequent call. This is the only solution that does not require modifying the FB source — useful when the FB is in a protected library block (know-how protected, for example) and you cannot change its code.

Solution 5 — Disable Type Check (Last Resort)

Warning: Disabling address type checking is a project-wide setting. It will mask many legitimate type errors in the entire STEP 7 program. Use this only as a diagnostic step, not as a permanent workaround.
  1. Open the LAD/FBD editor.
  2. Menu: Options > Customize > LAD/FBD.
  3. Uncheck "Type check of addresses".
  4. Click OK and recompile.

After this, you can wire a MOVE box directly to #STAT0.PT in the parent FB. Re-enable the type check once the prototype is complete, then refactor the code into one of the solutions above.

Multi-Instance FB Concept (Background)

A multi-instance FB is an FB that contains another FB (or IEC timer) as a static component. STEP 7 supports this to allow code reuse without creating a separate instance DB for every reusable block. The key rules:

Property Single instance Multi-instance
Instance DB Own DB, one per call site Shares the parent FB's instance DB
Data layout Root of the instance Nested in the parent's static area
Variable name in caller DB number (e.g., DB100) Symbolic static name (e.g., STAT0)
Call syntax CALL FB 100 , DB100 CALL #STAT0
Encapsulation Same — components are private to FB Same — components are private to FB

Declaration example for a multi-instance IEC timer

FUNCTION_BLOCK FB_TimerControl
VAR
   STAT0 : TON;          // on-delay,  multi-instance
   STAT1 : TP;           // pulse,     multi-instance
   STAT2 : TOF;          // off-delay, multi-instance
   STAT3 : TONR;         // retentive on-delay (S7-1500 only)
END_VAR

Instance DB Layout

When a multi-instance FB is called, its instance DB is laid out with a fixed offset for each static component. For an S7-300 with the declaration above, the offsets (in bytes) of the timer fields are:

Offset (byte) Symbol Type Width (bytes)
0.0 STAT0.IN BOOL 1
2.0 STAT0.PT TIME 4
6.0 STAT0.Q BOOL 1
8.0 STAT0.ET TIME 4
12.0 STAT1.IN BOOL 1
14.0 STAT1.PT TIME 4
18.0 STAT1.Q BOOL 1
20.0 STAT1.ET TIME 4
24.0 STAT2.IN / PT / Q / ET ... 12

Offsets are calculated by STEP 7 at compile time. Do not hard-code them; always use symbolic addresses (#STAT0.PT) so that re-declaration does not break the offsets.

Verification and Diagnostics

After applying one of the solutions above, validate the timer behavior in the following order:

  1. Compile check. In LAD/FBD, ensure no red boxes remain. In STL, ensure the compiler reports no warnings for instance access.
  2. Download to PLC. Use PLC > Download with the instance DB included. A partial download that omits the DB will not pick up new PT defaults.
  3. Online monitor. Open the instance DB in Monitor/Modify and watch STAT0.IN, STAT0.PT, STAT0.Q, and STAT0.ET live.
  4. Functional test. Set I0.0 = TRUE and verify that Q0.0 transitions to TRUE after exactly the requested PT. Use a stopwatch or the PLC's scan-time stamping to confirm.
  5. Edge cases:
    • Set PT := T#0ms — the timer should pass Q immediately on the next scan.
    • Toggle IN rapidly — ET should reset to T#0ms on each falling edge.
    • Power-cycle the CPU — the timer state in the instance DB is volatile unless you have declared it retentive (S7-1500 only); expect ET to start at zero.

Troubleshooting Matrix

Symptom Likely cause Fix
Compile error "Data type not allowed" on PT LAD/FBD type check is on and PT is being wired directly Use Solution 1, 3, or 5
Timer never reaches Q PT written after CALL, or PT=0 Move PT assignment above CALL; verify T# value
Q flickers after exactly one scan PT is set inside the timer's own body each call, so the time never accumulates Keep PT as a constant or FB input; do not reassign every cycle
ET does not count up Wrong instance is being monitored (e.g., DB101 vs DB102) Verify the instance DB number matches the call site
Compile warning "Instance component accessed externally" STL code reads/writes STAT outside the FB body Refactor into Solution 1 (parameter interface)
T# constant not accepted in MOVE Source operand is a different data type (INT, WORD) Cast via TIME intermediate or use L T#500ms / T MWxx in STL

Related Timer Operations

IEC type Behavior Fields Notes
TON (IEC) Delays rising edge of Q by PT IN, PT, Q, ET ET reset on falling IN
TOF (IEC) Delays falling edge of Q by PT IN, PT, Q, ET ET reset on rising IN
TP (IEC) Pulse of width PT on rising IN IN, PT, Q, ET Ignores IN changes during pulse
TONR (IEC, S7-1500) Accumulates ET, reset by RES input IN, PT, RES, Q, ET Retentive across power-cycle if DB is retentive
S_PULSE / S_PEXT / S_ODT / S_ODTS / S_OFFDT (legacy) Classic SIMATIC timers S, TV, R, BCD/DW, Q, BI/DE Stored in system memory areas; not multi-instanceable

All five IEC timer types follow the same encapsulation rule described in this article. The same parameter-passing pattern applies to TP, TOF, and TONR identically.

Migration Notes: S7-300/400 to S7-1200/1500 (TIA Portal)

If you are porting the multi-instance FB to a TIA Portal project, the rules remain the same. Two practical differences:

  1. SCL is the preferred text language for multi-instance work in TIA Portal. The IEC call syntax is more compact and avoids the early L/T dance required in STEP 7 STL.
  2. Optimized block access (default for new S7-1500 DBs) means you cannot rely on fixed byte offsets for PT initialization in a VAT. Use symbolic names only.

Reference: TON: Generate on-delay (S7-1200, S7-1500) — TIA Portal V20 documentation.

Frequently Asked Questions

Why does the LAD editor reject a MOVE box wired to the PT of a multi-instance TON?

Because the LAD/FBD type checker treats STAT components of an FB as private. You must pass PT through the FB's formal input interface (Solution 1) or use a TEMP TIME buffer (Solution 3). Direct symbolic writes to #STAT0.PT are reserved for STL.

Can I assign T#500ms directly to PT in a multi-instance FB on S7-300/400?

Not from a graphical editor with type checking enabled. Declare Duration : TIME as a VAR_INPUT, wire the constant to it at the call site, and pass Duration into the timer's PT via the formal CALL interface.

Is direct STL access to #STAT0.PT safe in production code?

It works, but Siemens explicitly discourages it because it breaks encapsulation, hurts reusability, and produces faults that are hard to trace. Use it only for legacy maintenance or quick prototypes, and refactor to the parameter interface before commissioning.

How do I initialize PT to a fixed value without recompiling the FB?

Open the instance DB in the Variable Table (VAT) and write the desired TIME constant to the symbolic address "DB_name".STAT0.PT. The value is latched into the DB and used on the next call. This is the only way to change PT for a know-how-protected FB that you cannot edit.

Does the same rule apply to TP, TOF, and TONR multi-instance timers?

Yes. All IEC timer types (TON, TOF, TP, and TONR on S7-1500) declared in the STAT section of an FB are private. The CALL interface and the same five solutions in this article apply to every one of them.

Back to blog