Clamping S7 SFB4 TON Preset Time (PT) to Minimum 50 ms in STL

David Krause14 min read
PLC ProgrammingS7-300Siemens
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

Overview

Siemens SFB4 (the IEC Timer On-Delay, "TON") exposes a PT (Preset Time) input in TIME format. A frequent field requirement is to clamp the operator-entered preset to a safe minimum so that a stray T#0ms, T#1ms or T#10ms cannot create an effectively instant on-delay or starve a downstream safety interlock. A typical floor used on machine-tool and packaging machinery is 50 ms — long enough to debounce mechanical contacts and short enough to remain "responsive" in human terms.

This reference shows three working implementations of a 50 ms lower-limit clamp on SFB4 in STEP 7 STL:

  1. A reusable FC that takes two TIME parameters and returns the larger of the two.
  2. A drop-in wrapper FB that encapsulates SFB4 and pre-clamps the caller's PT.
  3. An inline STL sequence placed at the start of the timer's parent block — the technique the original author settled on because ladder's LT_D instruction requires INT/DINT, not TIME.

The underlying SFB4 specification is documented in the SIMATIC S7-300/400 System Software – System and Standard Functions reference manual; SFB4 ships in the Standard Library under System Function Blocks. STL comparison semantics follow the STEP 7 STL Programming Manual.

Technical Background: SFB4, the TON Instance, and TIME Encoding

SFB4 versus FB4

SFB4 is a system function block that lives in the operating system of the S7-300/S7-400 CPU. The block instance DB that holds its parameters (in/out, static) is generated by the user with CALL SFB4, DBn or implicitly when an instance DB is assigned to a function block that contains the SFB4 call. SFB4 has no instance in the user program code as a standalone FB; its interface is:

Interface Name Type Description
Input IN BOOL Start input
Input PT TIME Preset time – the value ET is compared against
Output Q BOOL TRUE once ET ≥ PT
Output ET TIME Elapsed time, counted from the last rising edge of IN

On the S7-1200/S7-1500 platforms the same logic ships as FB IEC_Timer_0_DB (or the IEC TON multi-instance). The block interface is identical, but the encapsulation is an FB with instance DB, not an SFB. All three techniques below transfer verbatim with the rename SFB4 → FB and adjusting the multi-instance declaration.

TIME as a 32-bit signed DINT in Milliseconds

The TIME data type is a 32-bit signed integer with units of milliseconds. Range, resolution and bit layout match DINT exactly:

Property Value
Storage width 32 bits (DINT)
Unit milliseconds (ms)
Range –2 147 483 648 ms … +2 147 483 647 ms
Zero T#0ms / L#0
Hex of T#50ms 16#0000_0032 (50 decimal)
Hex of T#1s 16#0000_03E8 (1000 decimal)

Because TIME is physically a DINT, any DINT comparison instruction — including <D — can be applied to a TIME operand without conversion. This is the foundation of all three techniques below.

Why a 50 ms Floor Matters

SFB4's elapsed-time accumulator is updated by the priority class 1 OB (OB1). The smallest observable jump in ET is one OB1 cycle. If PT is below the cycle time, the timer can reach its preset mid-cycle but the next comparison only fires after the cycle completes. With modern OB1 cycle times of 1–10 ms on a typical CPU 315-2 PN/DP, a 50 ms floor guarantees at least 5–50 comparison passes before Q asserts, eliminating spurious "instant" outputs caused by:

  • Operator panel entry of T#0ms or T#10ms.
  • Default values from an uninitialised instance DB (PT = T#0ms).
  • Mis-scaled HMI entries where seconds were loaded into a millisecond field (or vice-versa).
  • HMI write races during start-up where PT is read by SFB4 before the operator station has finished its first variable update.

Why Ladder's LT_D Is Unsuitable

The obvious ladder solution — compare the integer PT against 50 with < D, then MUX in the larger value — fails on a typed variable because LT_D works on INT or DINT, not on TIME. STEP 7 does not implicitly coerce TIME to DINT at the contact level, so the compiler rejects the rung or accepts it with a type warning. Two workarounds exist at the ladder level (move PT to a DINT temporary first, then compare and MUX), but both require extra MOVE/MUX networks that obscure the intent. Most engineers fall back to STL precisely because the comparison is the natural instruction there.

STL Comparison Instructions Available for TIME

Mnemonic Operands Operates on Result flags
<I / >I / ==I / <>I 16-bit INT BR / CC1 / CC0
<D / >D / ==D / <>D 32-bit DINT (and TIME – same encoding) BR / CC1 / CC0
<R / >R / ==R / <>R 32-bit REAL BR / CC1 / CC0

The mnemonic < is a single character; in source-code listings it appears as <I, <D, and so on. Both forms are accepted by the STL editor.

Method 1 – Reusable Low-Limit FC (FC 111)

The cleanest library-style solution is a parameterless FC that takes two TIME inputs and returns the larger. The function below is the exact FC 111 from the field report:

FUNCTION FC 111 : TIME
TITLE =
VERSION : 0.1
VAR_INPUT
  tIN   : TIME ;   // candidate value
  tLLIM : TIME ;   // lower limit
END_VAR
BEGIN
NETWORK
TITLE =
      L  #tIN           // load candidate
      L  #tLLIM;        // load lower limit
      <D                // ACCU2 < ACCU1 ?
      JC  L001;          // if tIN < tLLIM, jump over TAK
      TAK;               // swap – keep tIN as result
L001: T  #RET_VAL;       // store to return value
      SET ;
      SAVE ;
END_FUNCTION

How the FC works

  1. L #tIN pushes the candidate into ACCU1, moving the previous ACCU1 to ACCU2.
  2. L #tLLIM pushes the limit into ACCU1; the candidate is now in ACCU2.
  3. <D computes ACCU2 < ACCU1 — i.e. tIN < tLLIM — and sets the BR/CC1/CC0 flags.
  4. JC L001 jumps to the store if the inequality is true (candidate is too small); the TAK is skipped, leaving tLLIM in ACCU1.
  5. TAK swaps ACCU1 ↔ ACCU2 so the larger value (tIN) ends up in ACCU1 for storing.
  6. T #RET_VAL writes ACCU1 to the function's return value.

Calling the FC

In the timer FB, the PT passed to SFB4 is replaced by the clamped result:

      CALL FC 111 (
           tIN   := #On_Delay.PT,
           tLLIM := T#50MS)
      T  #On_Delay.PT

Reuse on multiple timers is now trivial: any number of CALL FC 111 lines in any block clamp their own PT to 50 ms with no further code.

Method 2 – Wrapper FB Around SFB4 (FB 904)

FUNCTION_BLOCK FB 904
TITLE =
VERSION : 0.1
VAR_INPUT
  in : BOOL ;
  PT : TIME ;
END_VAR
VAR_OUTPUT
  Q  : BOOL ;
  ET : TIME ;
END_VAR
VAR
  sfb4a : "TON";        // SFB4 instance (multi-instance compatible)
END_VAR
BEGIN
NETWORK
TITLE =
      L  50;
      L  #PT;
      <D;
      JC  ok;
      TAK;
ok:   T  #sfb4a.PT;
      CALL #sfb4a (
           IN := #in,
           Q  := #Q,
           ET := #ET);
END_FUNCTION_BLOCK

How the wrapper works

  • The first L 50 loads the literal DINT 50 (ms). The literal could equally be written as L L#50 for clarity.
  • L #PT pushes the caller's preset into ACCU1.
  • <D sets flags if 50 < PT — in other words, "PT is large enough".
  • If PT < 50 the jump is not taken; the TAK swaps the 50 into ACCU1 (overwriting the lower PT in ACCU1).
  • If PT ≥ 50 the jump goes to ok; no TAK runs; PT is still in ACCU1.
  • T #sfb4a.PT stores the clamped value into the internal SFB4 instance's PT.
  • The CALL #sfb4a then runs the standard on-delay logic; outputs are returned through the wrapper's outputs.

One subtle point: L 50 writes a 32-bit constant 50, which the S7-300/400 assembler treats as a DINT whenever ACCU1 is loaded in 32-bit mode. The subsequent <D sees ACCU2 = 50 and ACCU1 = #PT in 32-bit signed form — identical to L L#50.

Method 3 – Inline STL Clamping in the Timer's Own Block

The simplest patch when only one or two timers exist is to clamp at the top of the parent block, before the SFB4 call. The author's final STL snippet is:

      L  #On_Delay.PT
      L  L#50
      <I
      JCN j10a
      T  #On_Delay.PT
j10a: NOP 0

      L  #Off_Delay.PT
      L  L#50
      <I
      JCN j10b
      T  #Off_Delay.PT
j10b: NOP 0

Semantics walk-through

  1. L #On_Delay.PT pushes the operator value into ACCU1 (was in #On_Delay.PT as TIME).
  2. L L#50 pushes 50 ms into ACCU1; PT is now in ACCU2.
  3. <I compares 16-bit ACCU2 < ACCU1 (PT < 50?).
  4. JCN j10a — "Jump if CC1 = 0 OR CC0 = 1" — i.e. if the comparison result is not "less than". If PT ≥ 50, jump past the T and keep the original value.
  5. T #On_Delay.PT stores ACCU1 (50) back into the instance DB, overwriting whatever was there.
  6. The NOP 0 is a required jump target; STEP 7 does not allow a label to be the last statement in a network in STL.

The second triplet repeats the same pattern for an off-delay (SFB5 "TOF") on the same instance DB or a sibling one.

Why <I works against L#50 on a TIME operand

TIME is a 32-bit signed integer, but the S7-300 CPU's <I instruction evaluates only the low 16 bits of each accumulator. For all PT values in the range –32768 ms … +32767 ms the low word equals the low word of the equivalent DINT, so the comparison yields the same boolean result as <D. Above 32 768 ms the high word becomes non-zero and <I silently diverges. Because the purpose of this clamp is to enforce a 50 ms minimum, the high word is essentially zero for all plausible operator inputs, and <I is therefore safe in this specific application. For any code path where PT can legitimately exceed ~32 s, switch to <D unconditionally.

Verification

Static check – bit pattern

Open the instance DB in STEP 7 and inspect the PT offset. After one execution of the clamp, the DWord should read 16#0000_0032 (decimal 50) for any entered PT below 50 ms. Use Monitor/Modify with the display format set to DEC or HEX for the DWord at the PT offset.

Online check – VAT / watch table

  1. Open a VAT (Variable Table) and enter the instance DB offsets for PT, ET and Q.
  2. Force PT to T#0ms, then trigger a single OB1 pass.
  3. Confirm PT now reads T#50ms and Q does not assert on the same scan.
  4. Force PT to T#5s and confirm PT is unchanged (no clamp on the upper side).
  5. Force PT to T#-1ms and confirm PT clamps to T#50ms as well — negative inputs are also "too low".

Cross-reference

From the LAD/FBD/STL editor, right-click the instance DB symbol and choose Go to → Cross-references. Confirm that every read site is preceded by either the inline STL clamp, the FC 111 call, or the wrapper FB 904. Anything outside that set is a leak and should be reworked or its PT value re-routed.

Edge Cases, Cycle-Time Effects, and Platform Notes

SFB4 time accuracy vs. OB1 cycle time

SFB4's elapsed-time accumulator is updated by OB1's priority class. With a 10 ms cycle, ET increments in 10 ms steps; the actual Q-edge can therefore be delayed by up to PT + 1 cycle. A 50 ms floor only smooths out the minimum; for high-precision timing, drive SFB4 from a time-of-day OB (e.g. OB10) or migrate to the S7-1500 platform's Ton (FB1864 / "IEC_Timer_0_DB") where the IEC timer is updated by a 1 ms hardware interrupt. The S7-300/400 IEC timer specifics are covered in the System and Standard Functions manual, Chapter 22.

Negative or inverted PT inputs

Both <D and <I are signed comparisons. A PT of T#-100ms (–100, i.e. less than 0) is correctly identified as below the 50 ms floor and clamped upward. This is the desired behaviour for HMI fields that accept typed TIME inputs — the operator cannot enter a negative on-delay and quietly disable the interlock.

HMI scaling faults

Many WinCC flexible / TIA Portal panels send PT as a DINT scaled in milliseconds. A driver that writes 5 when the operator meant 5 seconds (5 000 ms) is below the 50 ms floor and will be clamped — the timer will still fire, but after 50 ms rather than 5 s. The clamp therefore protects safety but cannot replace proper unit-conversion. Add an HMI-side input range check (min 0.05 s) as a parallel defence.

S7-1200 / S7-1500 equivalents

The wrapper-FB approach is portable to the S7-1200/S7-1500 platforms with two adjustments:

  • Replace SFB4 with the IEC TON multi-instance. The system delivers the timer as IEC_Timer_0_DB (default instance) or as a multi-instance of the platform's IEC timer FB.
  • Replace literal L 50 with L 50 or L L#50 — both compile to the same constant. Note that on S7-1500 the STL flavour is replaced by SCL (Structured Control Language); an SCL equivalent is #sfb4a.PT := MAX(IN1 := #PT, IN2 := T#50ms); which is functionally identical and type-safe.

Watch out for instance-DB initial values

An instance DB generated for an FB that contains SFB4 sets PT to T#0ms on first download. Until the operator station writes a real value, the timer's first call will see PT = 0 and the clamp will rewrite it to 50 ms. If the timer must run with a known minimum during start-up, place the clamp before the first CALL SFB4 — exactly what FB 904 and the inline STL methods do. The standalone FC 111 must be called explicitly in this order; an uninitialised PT that is never clamped will silently produce a 0 ms on-delay on the first cycle.

SFB3 / SFB4 / SFB5 quick reference

SFB IEC name Function PT lower-limit clamp code
SFB3 TP Pulse timer Identical (PT is TIME)
SFB4 TON On-delay Identical
SFB5 TOF Off-delay Identical

Troubleshooting matrix

Symptom Likely cause Fix
Q asserts instantly despite 50 ms PT Clamp executes after SFB4 call Move clamp network before CALL SFB4 in the same FB scan.
PT shows 0 ms in VAT but DB is fresh FC 111 not invoked before first SFB4 call Insert CALL FC 111 once per scan before each timer's CALL SFB4, or migrate to FB 904 wrapper.
Compare flag always true above ~32 s PT Used <I on a PT in the high-word range Switch to <D.
Compiler error: type mismatch in ladder LT_D against a TIME variable Convert: MOVE PT -> tempDINT, then LT_D, or convert ladder network to STL.
Timer never reaches Q PT clamped but FC 111 was called with swapped arguments (limit < candidate accidentally) Re-check FC 111 call: tIN := #On_Delay.PT, tLLIM := T#50MS.
HMI shows clamped value, not operator value Clamp writes to the same DB offset the HMI reads Clamp a shadow variable, write the clamped value into PT only on the call; or have the HMI write to a separate "desired PT" field.

Comparative trade-offs

Technique Reusability Type safety Caller visibility Code volume per timer
Inline STL clamp Low – copy/paste High (no extra types) Visible in parent FB ~6 STL lines
FC 111 (MAX on TIME) High – one FC for all timers High (TIME in / TIME out) Visible at each CALL site 3 lines per CALL
FB 904 wrapper High – drop-in replacement for SFB4 High (mirrors SFB4 signature) Hidden inside wrapper 0 at call site

For one-off clamps, inline STL is fastest. For a library of timers, the wrapper FB 904 is the cleanest contract. For shared utilities, FC 111 wins.

FAQ

Can SFB4 accept PT values above 2 147 483 647 ms?

No. PT is a signed 32-bit TIME (DINT) with a hard ceiling of T#2 147 483 647 ms ≈ 24.86 days. STEP 7 flags out-of-range literals at compile time; runtime values written via HMI must be range-checked before being passed to SFB4.

Why does <I work even though PT is 32-bit?

TIME is encoded as a DINT in milliseconds. <I compares only the low 16 bits, but for any PT below ~32 768 ms the high word is zero so the boolean result matches a 32-bit signed comparison. For PT above 32 768 ms, switch to <D to stay correct.

What happens to PT if the instance DB is freshly downloaded?

The initial value is T#0ms. If your code path runs the timer before the HMI writes a real value, the clamp will set PT to T#50ms on the first scan. The wrapper FB 904 and inline clamp handle this automatically; a separate FC 111 call must be placed before the SFB4 invocation to avoid a one-cycle T#0ms on-delay.

Does the clamp affect timer accuracy?

No. SFB4's accuracy is governed by the OB1 cycle time, not by PT. The clamp only raises the floor of PT; once PT is above the floor, the timer's resolution is still ±1 OB1 cycle. For sub-millisecond timing, drive SFB4 from a higher-priority cyclic OB or migrate to an S7-1500 IEC timer.

Can the same 50 ms minimum be applied to TOF (SFB5) and TP (SFB3)?

Yes. SFB3 (TP – pulse), SFB5 (TOF – off-delay) and the legacy timer FBs all use the same TIME-encoded PT input. The clamp code is identical; only the SFB number changes. Wrap each in its own FB (e.g. FB 904 for TON, FB 905 for TOF) or call FC 111 immediately before each CALL SFBn.

Back to blog