Modifying ET of Siemens S7-300 IEC_TIMER TON Mid-Run

David Krause16 min read
S7-300SiemensTutorial / How-to
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

Modifying ET of a Siemens S7-300 IEC_TIMER (TON) While It Is Running

When you need to inject a value into the elapsed-time word (ET) of a TON block on a Siemens S7-300 (for example a CPU 315F-2 PN/DP), a direct MOVE into the ET tag will appear to "not work": the block immediately overwrites it on the next scan because, in the IEC timer model, ET is an output of the timer function block rather than a writable variable. The remedy is to stop abusing the system block and build a small software timer out of a counter and a clock pulse. The counter's current value (CV) behaves like a writable ET, so the same staged-logic design (0–20 s, 20–40 s, 40–60 s) can be realised and you gain a deterministic preset.

1. Problem Definition

The requirement is concrete and common in process-cell interlocks:

  • An FB contains a TON with a 60 s preset (PT := T#60s).
  • Three logic branches must be active at distinct time windows:
    • Logic A while ET is in [0 s, 20 s)
    • Logic B while ET is in [20 s, 40 s)
    • Logic C while ET is in [40 s, 60 s]
  • When a digital trigger (e.g. M0.0) is asserted, the elapsed-time equivalent must snap to 20 s and continue counting, regardless of the previous value.

What the original author observed is correct: the IEC_TIMER's ET is on the output side of the function-block interface (it is what the block publishes to the outside world). Even if you address the corresponding static variable inside the instance DB, the block's algorithm re-computes ET on every call, so the value you wrote will be replaced the next time the block executes.

Terminology note: Siemens documentation refers to the standard IEC timers as SFB3 (TP), SFB4 (TON) and SFB5 (TOF) in STEP 7 V5.x and as IEC_TIMER data-type blocks (TP_TIME, TON_TIME, TOF_TIME) in TIA Portal. The ET semantic is identical in both: read-only output.

2. Why the Standard TON Refuses a Mid-Run Preset

The IEC 61131-3 TON contract, as implemented by Siemens, exposes four signals:

Pin Direction Purpose
IN Input Start / enable; level-sensitive
PT Input Preset time (TIME, e.g. T#60s)
Q Output TRUE when ET ≥ PT
ET Output Elapsed time since IN rose; written by the block

Inside the instance DB, ET is held as a TIME (DWORD, 32-bit, ms resolution). The block updates it on every call. A MOVE from your application into the same DWORD will be overwritten within the same OB1 cycle. The same restriction exists for TP and TOF. PT, by contrast, is an input — you can change it freely while IN is high, and the block's comparison logic honours the new value on the next scan. That is why trying to write to ET looks "frozen" but writing to PT works.

Two options follow from this:

  1. Build a custom timer whose internal accumulator is a counter or a writable DWORD. This is the recommended path because the timing resolution, scaling, and preset semantics are entirely under your control.
  2. Pre-load the instance DB byte that holds ET with a non-zero value, then call the SFB, and accept that the block will still add the elapsed ms of the last cycle on top. This is fragile, scan-time dependent, and officially unsupported — do not use it in F-CPUs (CPU 315F-2 PN/DP is a failsafe PLC and the F-runtime will flag an unexpected data change in the safety-relevant instance DB).

3. Architecture of the Software Timer

The replacement timer has three components:

  1. A tick generator (clock pulse) — typically 1 Hz, derived from a system clock byte or OB35.
  2. A writable accumulator — a CTU counter, an IEC counter (CTU_UD), or a plain DWORD/INT variable incremented on each tick.
  3. An overflow / PT comparator that sets the done bit (Q) when the accumulator ≥ the desired PT.

Because the accumulator is a normal tag, a MOVE into it from anywhere (OB1, another FB, HMI) is honoured. That is the semantic you wanted from ET.

3.1 Tick Source Selection

Source Typical period Where to configure Notes
System clock byte (M area) 10 ms / 100 ms / 200 ms / 500 ms / 1 s / 2 s / 5 s / 10 s HW Config → CPU → Properties → "Clock memory", tick a byte (e.g. MB10) Cheapest; eight periods from one byte. Bit n is period 2^n × base.
OB35 (cyclic interrupt) 100 ms default, 1 ms – 60 s configurable HW Config → CPU → Cyclic Interrupts → OB35 Time-slice priority; tolerant of long OB1 cycles.
Programmed pulse generator (timer-pulse train) User-defined FB/OB1 Use only if system clock is disabled or unavailable.
S7-300 hardware time (SFC 1 / SFC 0) 1 ms Application code Read into ms DINT and increment; overkill for 1 s resolution.

For a 1 s tick, enable the system clock byte and pick the period that corresponds to bit 0 at the chosen base. If the base is 100 ms, then bit 3 of the clock byte is 800 ms — wrong. Use the period selector: setting the byte to frequency 1.0 Hz gives you a 1 s tick on bit 7 (or whichever the dialog shows as 1.0 s). The simplest is to use a base of 1.0 s and read bit 0.

4. STEP 7 V5.x Implementation in STL

Create a new FB, e.g. FB100 "SW_Timer", with the following interface:

Name Type Direction Comment
IN_RUN BOOL Input Run input (replaces TON's IN)
IN_PT_s INT Input Preset time in seconds (1 s resolution)
IN_PRESET_EN BOOL Input When TRUE, ET := IN_PRESET_s (replaces ET write)
IN_PRESET_s INT Input Value to write into ET when IN_PRESET_EN is high
OUT_ET_s INT Output Elapsed time, seconds (your custom ET)
OUT_Q BOOL Output TRUE when ET ≥ PT
STAT_LAST_TICK BOOL Static Edge memory for the 1 s tick
STAT_ET_INT INT Static Internal accumulator

STL body of FB100 (cycle = OB1, 1 s tick from clock byte MB10 bit 0):

// ----- NETWORK 1: Preset override (priority over run) -----
A     #IN_PRESET_EN
JCN   N1A
L     0
T     #STAT_ET_INT       // clear accumulator
L     #IN_PRESET_s
T     #STAT_ET_INT       // load preset value
JU    N1B
N1A:  NOP   0

// ----- NETWORK 2: Rising edge of 1 s tick -----
A     M10.0              // 1 Hz system clock bit
FP    #STAT_LAST_TICK
=     #STAT_ET_INT.DBX0  // RLO true for one cycle on the 1 s edge
N1B:  NOP   0

// ----- NETWORK 3: Increment accumulator on the 1 s edge -----
A     M10.0
FP    #STAT_LAST_TICK
JCN   N3A
L     #STAT_ET_INT
L     1
+D
T     #STAT_ET_INT
N3A:  NOP   0

// ----- NETWORK 4: Run gating -----
A     #IN_RUN
JCN   N4A                // when IN_RUN = 0, freeze the accumulator
L     0
T     #STAT_ET_INT
JU    N4B
N4A:  NOP   0

// ----- NETWORK 5: Saturate at preset -----
L     #STAT_ET_INT
L     #IN_PT_s
>I
JCN   N5A
L     #IN_PT_s
T     #STAT_ET_INT
N5A:  NOP   0

// ----- NETWORK 6: Outputs -----
L     #STAT_ET_INT
T     #OUT_ET_s

L     #STAT_ET_INT
L     #IN_PT_s
>=
=     #OUT_Q

Key points in the STL:

  • Network 1 unconditionally writes the preset value into STAT_ET_INT when IN_PRESET_EN is true, before the 1 s tick is processed in the same cycle. The next increment will move it to 21 s on the following edge.
  • Network 3 uses an FP (rising-edge) detector. Without the edge, the counter would advance by 1 for every OB1 cycle (typically 5 – 50 ms), saturating the timer in microseconds.
  • Network 4 demonstrates a freezing behaviour. If you want the timer to keep running while IN = 0, delete N4A and the unconditional reset, or move the freeze into a separate input IN_HOLD.
  • Network 5 clamps the accumulator to PT so the integer does not roll over if you never assert OUT_Q-driven stop logic. Replace the > with a saturating ITD / LIMIT if you extend to TIME/DINT.

5. STEP 7 V5.x Implementation in LAD/FBD

If you prefer LAD/FBD, the equivalent is short enough to fit in one network per function:

  1. Place a CTU (IEC counter, from "Bit logic → Counter") inside the FB. CU = M10.0 (1 s tick). R = NOT(IN_RUN). PV = IN_PT_s. CV = STAT_ET_INT. Q = OUT_Q.
  2. To "write to ET": add a MOVE from IN_PRESET_s into STAT_ET_INT, gated by IN_PRESET_EN. Place it before the CTU network. Because the counter is reset every cycle by R (network order matters in LAD — the MOVE network must execute first), the next edge will see the preset as the starting point.
  3. For the three-stage branching, add three comparators:
    // Logic A: 0 ≤ ET < 20
    L     #STAT_ET_INT
    L     0
    >=I
    A(    )
    L     #STAT_ET_INT
    L     20
    <I
    =     #LogicA
    
    // Logic B: 20 ≤ ET < 40
    L     #STAT_ET_INT
    L     20
    >=I
    A(    )
    L     #STAT_ET_INT
    L     40
    <I
    =     #LogicB
    
    // Logic C: 40 ≤ ET ≤ PT
    L     #STAT_ET_INT
    L     40
    >=I
    A(    )
    L     #STAT_ET_INT
    L     #IN_PT_s
    <=
    =     #LogicC
    

6. Structured Text Variant (Portable Across STEP 7 V5.x and TIA Portal)

If the project is migrated to TIA Portal later, porting STL is painful. ST survives. The same FB, in SCL, looks like this:

FUNCTION_BLOCK SW_Timer
VAR_INPUT
    IN_RUN        : BOOL;
    IN_PT_s       : INT;
    IN_PRESET_EN  : BOOL;
    IN_PRESET_s   : INT;
END_VAR
VAR_OUTPUT
    OUT_ET_s : INT;
    OUT_Q    : BOOL;
END_VAR
VAR
    Tick1Hz       : BOOL;   // wired to MB10.0 in OB1
    Tick1Hz_Old   : BOOL;
    Accumulator_s : INT;
END_VAR
BEGIN
    // Preset has priority over the tick
    IF IN_PRESET_EN THEN
        Accumulator_s := LIMIT(0, IN_PRESET_s, IN_PT_s);
    ELSIF NOT IN_RUN THEN
        Accumulator_s := 0;
    ELSIF Tick1Hz AND NOT Tick1Hz_Old THEN
        Accumulator_s := LIMIT(0, Accumulator_s + 1, IN_PT_s);
    END_IF;
    Tick1Hz_Old := Tick1Hz;

    OUT_ET_s := Accumulator_s;
    OUT_Q    := Accumulator_s >= IN_PT_s;
END_FUNCTION_BLOCK
Edge handling: the rising-edge pair (Tick1Hz AND NOT Tick1Hz_Old) is the IEC 61131-3 equivalent of the R_TRIG function block. The previous-cycle Tick1Hz_Old must live in the static / instance area, not in TEMP, otherwise it is lost on every cycle and the timer increments by one each OB1 scan.

7. F-CPU Specific Constraints (CPU 315F-2 PN/DP)

Because the target is a CPU 315F-2 PN/DP (6ES7315-2FH14-0AB0 or 6ES7315-2EH14-0AB0), the F-runtime imposes additional rules:

  1. Place the software timer outside the F-runtime group (F-FB, F-CB). Mixing standard FBs and F-blocks in the same runtime group is allowed, but the custom timer must not be classified as a safety block, or the F-signature check will treat its non-safety writes as a violation. The standard pattern is to instantiate SW_Timer in OB1 (or a non-F cyclic OB) and feed only its boolean outputs to the F-programme via the standard → safety gateway.
  2. Do not put the IN_PRESET_EN or IN_PRESET_s tags inside an F-DB. The F-CPU's "modified data" check may flag the runtime write as an integrity violation. Keep preset triggers in the standard process image (e.g. M or DB from the standard project).
  3. Use the failsafe OB35 (if configured) for time-tick generation only after verifying that your F-programme does not depend on a strict OB1-only call pattern. The F-library F_ApplicationBlocks v6.x is documented to tolerate OB35-sourced ticks.

8. Step-by-Step Wiring Procedure

  1. Open HW Config → double-click the CPU 315F-2 PN/DP → tab "Cycle/Clock Memory".
  2. Tick "Clock memory". Pick a free byte, e.g. MB10. Pick a frequency, e.g. 1.0 s. Click OK and save/compile HW Config.
  3. Compile the S7 program and download HW Config to the PLC.
  4. Verify in Monitor / Modify that M10.0 blinks once per second.
  5. Create the FB SW_Timer (SCL or STL) from §4 or §6.
  6. Create a DB DB100 "Timer_Inst" with a single instance of SW_Timer.
  7. In OB1, call the instance:
    CALL "Timer_Inst", DB100
         IN_RUN       := "Start_Button"
         IN_PT_s      := 60
         IN_PRESET_EN := "Preset20s_Trigger"
         IN_PRESET_s  := 20
         OUT_ET_s     := MW200
         OUT_Q        := M202.0
    
  8. Wire the three comparators of §5 to MW200 for stages A, B, C.
  9. Download all blocks to the CPU.
  10. Go online and use Monitor to watch MW200 climb from 0 → 60 in 1 s steps.

9. Verification

Test Procedure Expected result
Free run Set Start_Button = 1, leave Preset20s_Trigger = 0 MW200 rises 0, 1, 2, …, 60 s in 1 s steps; M202.0 goes true at 60 s and stays true; LogicA/B/C cycle A → B → C in the right order
Preset during run Set Start_Button = 1, watch MW200 reach 5 s, then pulse Preset20s_Trigger for one cycle MW200 jumps to 20 s in the same OB1 cycle, then continues 21, 22, …
Preset at PT Let MW200 reach 60, pulse Preset20s_Trigger MW200 = 20; if Start_Button is still 1, it climbs again from 21
Run off → freeze Set Start_Button = 0 mid-run MW200 holds its last value; no Q
Reset Toggle Start_Button 0 → 1 MW200 clears to 0 and starts a new 60 s cycle
F-CPU integrity Force Preset20s_Trigger repeatedly from the HMI No F-stop; no diagnostic buffer entry "Data inconsistency in F-DB"

10. Edge Cases and Caveats

  • OB1 scan > 1 s: with a heavily loaded CPU, the system clock byte can skip a tick. Switch the tick source to OB35 (period 1 000 ms) for guaranteed timing regardless of OB1 duration.
  • Power cycle: the static STAT_ET_INT resides in the instance DB; a cold restart of the CPU resets it to 0 (initial value in the DB). If you need the timer to survive a power dip, mark the DB as non-retentive explicitly and use a separate retentive DWORD as the accumulator.
  • Multiple instances of the same FB: each DB has its own static area, so the FP-edge memory is per-instance. Do not share the STAT_LAST_TICK across instances.
  • Time-tick resolution finer than 1 s: replace the INT with a DINT in milliseconds. Drive the CTU from a 100 ms clock bit or from OB35 with period 100 ms. Multiply the comparison thresholds accordingly.
  • Negative preset: protect with LIMIT(0, IN_PRESET_s, IN_PT_s) to avoid a wrap-around that could trip the comparators.
  • S5_TIMER alternative (deprecated): the legacy S5 timer (e.g. SE, SV, SA, SS, SF) stores its time as a BCD-encoded word in a separate timer word area (T0…T511). That word is technically writable, but Siemens explicitly recommends against modifying it externally; on F-CPUs the F-runtime can detect a "time word modified during run" and raise a diagnostic. The IEC_TIMER / counter approach is the only supported path on a CPU 315F-2 PN/DP.

11. TIA Portal Migration Notes

If the project is later opened in TIA Portal (V16 or newer):

  • The SCL source from §6 compiles without changes — copy the FUNCTION_BLOCK into an SCL source file in the TIA Portal project.
  • Replace M10.0 with the TIA Portal clock bit configuration (Device Configuration → CPU → Properties → "System and clock memory" → enable "Clock memory", set MB to 10, frequency 1.0 Hz).
  • The IEC_TIMER data type is available in TIA Portal as IEC_TIMER / TIME. The semantic of ET is unchanged: still read-only. The custom FB SW_Timer remains the correct replacement.

12. Comparison: Standard IEC_TIMER vs Software Timer

Property IEC_TIMER (SFB4 / TON_TIME) Custom SW_Timer (FB with CTU)
ET writable mid-run No (output only) Yes (MOVE into CV or STAT_ET_INT)
Resolution 1 ms (TIME data type) User-defined (tick frequency)
Multi-instance friendly Yes (instance DB) Yes (instance DB)
F-CPU compatible Yes (with F-library) Yes (if outside F-runtime group)
Standalone preset trigger No (write to PT works only when IN = 0) Yes, priority-based
Internal counter overhead None 2 – 4 bytes per instance

13. Diagnostic Checklist if the Preset "Does Not Work"

  1. Open the FB instance DB online and confirm that STAT_ET_INT actually changes immediately after you pulse the preset input. If it does but the output OUT_ET_s does not, the network order in the FB is wrong — the comparator network is reading the value before the MOVE network writes it. Reorder networks.
  2. If the DB value flickers back to the old ET on the next cycle, you are still calling SFB4 / IEC_TIMER and the system block is overwriting the variable. Search the program for the FBs/FCs named "TON" or "IEC_TIMER_DB" instances and remove them.
  3. Confirm that the system clock byte is enabled. In Monitor, force M10.0 to TRUE in a one-second pattern. If it never toggles, the clock memory has not been downloaded — re-download HW Config.
  4. If the counter increments by more than 1 per second, the edge detector is missing. With an FP on M10.0 in the same network as the CTU, an OB1 scan of 50 ms would add 50 ms × 20 = 1 s of wrong accumulation if you used an unconditional A M10.0 instead of FP. The FP is the fix.
  5. For the F-CPU, check the diagnostic buffer (CPU → Information → Diagnostic Buffer) for entries mentioning F-runtime data inconsistencies after the preset. If present, the software timer is being called from inside the F-programme — move the call to OB1.

14. Related Building Blocks in the S7-300 Library

  • SFB3 (TP) — pulse timer. Same ET semantic, same restriction.
  • SFB5 (TOF) — off-delay. Same ET semantic.
  • SFC 1 (READ_CLK) and SFC 0 (SET_CLK) — for wall-clock time stamping if the 1 s tick is not adequate.
  • SFC 64 (TIME_TCK) — 32-bit ms tick counter; useful if you want ms resolution without system clock bits.

For full reference of the IEC timer SFBs, see the System Software for S7-300/400 System and Standard Functions manual in the Siemens Online Support: S7-300 CPU 31xC and CPU 31x manual (entry ID 109751586) and the SFB4 / TON block description (entry ID 44240604). For the F-variant of the CPU, see S7-300 F-CPU manual (entry ID 109751608).

Why does a MOVE into the ET of an IEC_TIMER have no effect?

ET is a function-block output. The block recomputes ET on every call and overwrites the storage location before your user code can read it back. To get a writable elapsed-time, replace the IEC_TIMER with a software timer built around a CTU counter or a static DWORD incremented by a clock pulse.

Can I simply change PT mid-run on a TON to get a similar effect?

Yes, but only when IN = 0. PT is a function-block input and is honoured on the next scan. If IN is already TRUE, the TON continues with the original PT until IN goes low. That is usually not what people want, and it does not let you "jump the ET to 20 s" while the timer is actively running.

Which clock source is best on the CPU 315F-2 PN/DP?

For 1 s resolution, the system clock byte is the simplest. Enable it in HW Config (CPU → Properties → Cycle/Clock Memory), pick a free MB (e.g. MB10) and frequency 1.0 Hz. For higher accuracy under heavy OB1 load, use OB35 with a 1 000 ms period and set the tick inside the OB35 body.

Is it safe to use this pattern inside the F-programme of the CPU 315F-2 PN/DP?

No. The F-runtime treats non-F writes to F-DBs as a possible integrity violation. Keep the custom timer FB outside the F-runtime group (call it from OB1 or a non-F cyclic OB) and route its boolean outputs to the F-programme through the standard → safety gateway.

Does the S5 timer (T0…T511) allow direct modification of the time word?

Technically yes — the time word is BCD-encoded and sits in the timer word area. Siemens, however, explicitly discourages external modification, and the F-runtime can flag a "time word modified during run" diagnostic. The supported path on the S7-300 platform is the IEC_TIMER / custom counter approach described in this article.

Back to blog