Siemens S7 Timer Parameter Passing FB to FC Error Resolution

David Krause11 min read
SiemensTIA PortalTroubleshooting
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 structuring a SIMATIC S7 program in TIA Portal or STEP 7 Classic, a common engineering pattern is to centralize timing logic inside a Function Block (FB) and expose a timer-related parameter so that downstream blocks (often a Function, FC) can start, reset, or evaluate the same timing element. The pattern fails at compile time in many configurations, producing errors such as:

  • "The data type of the actual parameter does not match the data type of the formal parameter."
  • "Parameter declaration: Timer cannot be used as input parameter of FC."
  • "IN/OUT/STAT parameter type TIMER is not permitted."

Directly entering a timer such as T0 inside the FC compiles cleanly, which masks the underlying rule. The block interface rejection is not a bug in the editor; it is the direct consequence of the way FC parameter passing is implemented in the S7 runtime model.

Root Cause Analysis

The S7 firmware differentiates between two block categories based on how they own memory:

  • Function (FC) — a code-only block. FCs do not have an associated Instance Data Block (DB). Any "memory" the FC uses is the temporary local stack (L stack) of the active priority class, plus the global bit memory (M), process image (I/Q), and timer/counter word areas.
  • Function Block (FB) — a code block paired with an Instance DB. The Instance DB stores the static variables declared in the FB's VAR/STAT section, including any timers declared as multi-instance or background-instance elements.

When an FB declares a formal parameter of type TIMER (S5TIME) or IEC_TIMER, the actual parameter passed by the caller is bound by reference (symbolic name → system memory word pair). The FB stores the reference inside its Instance DB. Inside the FB body, instructions like SD "MyTimer" resolve the reference against the Instance DB, which persists between cycles.

An FC cannot receive a timer by reference. The FC parameter-passing mechanism for elementary data types is strictly call-by-value. As documented in the TIA Portal programming reference, the user program passes FC parameters as call-by-value for simple data types such as INT, DINT, and REAL (see Passing parameters to blocks). The compiler copies the numeric contents of the formal parameter into the L stack frame of the FC. Because TIMER is a 16-bit word whose value carries no execution context for the scheduler, copying it yields a meaningless operand that cannot be acted on by timer instructions.

Permitted Data Types when Passing Parameters

The STEP 7 / TIA Portal help system exposes a matrix titled "Permitted Data Types when passing Parameters." The relevant subset for FC inputs is shown below.

Formal parameter type in FC Elementary type Permitted as FB→FC pass? Pass mechanism
BOOL Yes Yes Call-by-value
INT / DINT / REAL / WORD / BYTE Yes Yes Call-by-value
S5TIME (time preset value) Yes Yes (as numeric) Call-by-value
TIMER (timer reference) No (complex) No Requires reference
COUNTER (counter reference) No (complex) No Requires reference
IEC_TIMER / IEC_COUNTER / TON / TOF / TP / TONR No (DUT) No (FC cannot own instance) Requires instance DB
BLOCK_FB / BLOCK_FC / DB No Yes (pointer) Call-by-reference (pointer)
VARIANT / ANY No Yes (pointer) Call-by-reference
Arrays, STRUCT, UDT No Yes Call-by-reference (pointer)

Key rule: FC formal parameters may be declared only with elementary data types plus BLOCK_*, VARIANT, and ANY. Types that require storage persistence or instance binding — including TIMER, COUNTER, and the IEC timer/counter data types — are not permitted. This rule is enforced in TIA Portal V15 through V18 and remains in STEP 7 V5.7. See the official TIA Portal parameter-passing documentation.

Why Timer Parameters Fail

A timer in S7 is not a simple 16-bit value. The system reserves two consecutive words in the system data area for each timer word (T0–T15 on a CPU 1214C, up to T2047 on larger S7-1500 CPUs). The runtime scheduler needs to know the timer's word address to:

  1. Read and decrement the time value every scan.
  2. Update the BI/BCD output word.
  3. Signal DONE on the output bit.

When the FB stores MyTimer : TIMER; in its Instance DB, it stores the symbol binding. When SD MyTimer executes, the CPU resolves the symbol against the Instance DB, which provides the absolute address of the timer word. An FC has no Instance DB; the L stack slot that would hold the timer reference vanishes at the end of the cycle. Therefore, any FC formal parameter declared as TIMER cannot compile.

Why does T0 work inside the FC? When you type the absolute timer identifier T0 in the FC body, the compiler resolves it to a hard-coded system word address. No reference binding is required. This bypasses the parameter-passing restriction but at the cost of making the timer a global resource that no caller can reconfigure.

S5TIME vs IEC Timer Differences

Attribute S5 Timer (T, SD/SE/SS/SF/SP) IEC Timer (TP / TON / TOF / TONR)
Storage location System memory word pair Instance DB (FB static) or global DB
Time base resolution 10 ms – 9990 s (BCD) 1 ms – 24 d+ (TIME format)
Multi-instance capable No (single-instance, global) Yes (FB static multi-instance)
Reusable across blocks No — fixed identity T0…Tn Yes — symbolic instance name
Retentive No Only TONR
Suitable for FC parameter No No (requires instance)

IEC timers are the modern, recommended approach in TIA Portal because they can be declared as multi-instance in an FB's static area. They still cannot live inside an FC because the FC has no static memory. Both timer families require a binding to a persistent storage location.

Solution 1 — Restructure: Call the FB Inside the FC

The simplest architectural fix is to reverse the call direction. Instead of OB1 → FB2 → FC1, use OB1 → FC1 → FB2, with the FB owning the timer as a static or multi-instance element.

FB2 declaration (TiaPortal)

FUNCTION_BLOCK "FB_MotorTimer"
VAR
    TimerIEC : IEC_TIMER;        // multi-instance, owned by FB
    tPreset  : TIME := T#5s;
END_VAR
BEGIN
    "TP_DB"(IN := bStart, PT := tPreset, Q => bRun, ET => tElapsed);
END_FUNCTION_BLOCK

FC1 wrapper

FUNCTION "FC_Control" : Void
VAR_INPUT
    bStart : BOOL;
    bStop  : BOOL;
END_VAR
VAR
    sMotorTimer : FB_MotorTimer;   // FB instance lives in FC? NO — see note.
END_VAR
Important: The above snippet is illustrative only. An FC cannot itself declare an FB instance as a static variable because the FC has no instance DB. The correct pattern is to instantiate FB_MotorTimer in OB1 or a global DB and pass the instance into FC1 only as VARIANT/ANY (advanced), or to call the FB directly from OB1 and have FC1 receive only the already-computed timing bits (BOOL/TIME) as call-by-value.

Solution 2 — Pass Derived Values, Not the Timer Reference

The pragmatic, supported solution is to keep the timer logic inside an FB and expose its computed outputs to the FC as simple types. The FB owns the timer; the FC consumes the result.

FB2 — owns the timer

FUNCTION_BLOCK "FB_PulseCtrl"
VAR_INPUT
    bTrig  : BOOL;
    tWidth : TIME := T#2s;
END_VAR
VAR_OUTPUT
    bRun   : BOOL;
    tElapsed : TIME;
END_VAR
VAR
    PulseTon : TON;        // multi-instance
END_VAR
BEGIN
    PulseTon(IN := bTrig, PT := tWidth);
    bRun    := PulseTon.Q;
    tElapsed := PulseTon.ET;
END_FUNCTION_BLOCK

FC1 — consumes the outputs

FUNCTION "FC_Sequencer" : Void
VAR_INPUT
    bEnable   : BOOL;
    tPulse    : TIME;
END_VAR
VAR_OUTPUT
    bActive   : BOOL;
    tRemaining : TIME;
END_VAR
VAR_TEMP
    tInfo : TIME;
END_VAR
BEGIN
    // FC can freely use BOOL and TIME
    bActive := bEnable AND (tPulse > T#0ms);
    tRemaining := tPulse;
    tInfo := tRemaining;  // call-by-value works fine
END_FUNCTION

OB1 — wiring

// OB1 cycle
"iPulseCtrl"(bTrig := bStartRequest,
             tWidth := tUserPreset,
             bRun => bMotorRun,
             tElapsed => tMotorElapsed);

"FC_Sequencer"(bEnable := bMotorRun,
               tPulse  := tMotorElapsed,
               bActive => bStageActive,
               tRemaining => tStageRemain);

This pattern is fully supported by TIA Portal, matches the call-by-value rule for FCs, and keeps the timer instance persistent across scans.

Solution 3 — IEC Timer with Global Instance DB

If the timer must be visible from many blocks, declare a global instance DB and access it symbolically from any block.

  1. Add a new DB "GlobalTimers" with a static HoldTimer : IEC_TIMER;.
  2. From FB2 call "GlobalTimers".HoldTimer(IN := bRun, PT := T#10s);
  3. From FC1 call "GlobalTimers".HoldTimer(); to read Q and ET directly. FCs can read DB tags because they are global symbols, not parameters, so the call-by-value restriction does not apply.
Caution: Sharing a global timer across blocks makes it a hidden coupling. Restrict this pattern to well-documented utility blocks. For unit-testable, reusable code prefer Solution 2 (FB ownership + FC consumption of derived values).

Memory Layout Reference

For diagnostic purposes, the timer word assignments can be inspected in the PLC's system data:

  • S7-1200 (CPU 1214C DC/DC/DC, FW 4.6): timers T0–T15 (16 instances), T16–T31 reserved.
  • S7-1500 (CPU 1515-2 PN, FW 2.9): timers T0–T2047 maximum, configurable in PLC properties → System and clock memory.
  • STEP 7 Classic S7-300 (CPU 315-2 PN/DP): timers T0–T127 by default, expandable up to T0–T2047 via hardware configuration.

Each timer consumes 16 bytes of system memory: 2 words for the time value (BCD), 2 words for the trigger/reset control, plus 12 bytes of scheduler metadata on S7-1500. See the CPU-specific manual under "Memory areas and addressing" for exact byte counts.

Verification Steps

After restructuring, validate the program with this checklist:

  1. Compile — TIA Portal: Project → Compile → Software (rebuild all). Confirm no errors and no warnings about parameter types.
  2. Block consistency — right-click the program blocks folder → Check block consistency. All call paths must show green.
  3. Download — ensure the CPU is in STOP. Download blocks. Switch to RUN.
  4. Online monitor — open FB2 and FC1 online. Confirm the timer instance in FB2 shows Q = TRUE at PT expiry and the FC reads the same value.
  5. Watch table — add the FB instance DB tags and the FC tElapsed tag. Toggle the input and observe the timing trace.
  6. Force test — force bTrig := FALSE and confirm the timer does not retrigger when FC1 is called independently.
  7. Cross-reference — Show cross-references on the timer symbol. The reference must appear only in FB2 (or the global timer DB), never directly inside FC1.

Troubleshooting Matrix

Symptom Compiler / online message Likely cause Corrective action
Compile error in FC input declaration "TIMER not permitted as IN" Timer formal parameter declared in FC Move timer to FB; pass BOOL/TIME outputs to FC
Compile error in call "Actual parameter cannot be converted" Passing an FB-internal timer symbol to FC Use Solution 2 or Solution 3 above
FC always reads Q = FALSE Online monitor shows constant FALSE Hard-coded T0 inside FC never started by FB Drive the absolute timer from one owner block only
Timer drifts / double-trigger Process events fire twice Two blocks calling SD on the same T0 Single owner (FB) controls the timer; FC reads only
TIA Portal V16 — IEC_TIMER not visible Type unknown in FC Library version mismatch Update to "Libraries → Standard → IEC Timer" V1.3 or later
Online: ET never changes Static watch shows ET stuck FC instance of IEC timer has no DB backing Place the IEC timer in a global DB or FB static area
Cross-reference missing No usage shown for timer symbol Timer declared but never referenced Verify the TP/TON/TOF/TONR block call is inside an enabled network

Related Best Practices

  • Prefer IEC timers over S5 timers in all new code. They support multi-instantiation, exact TIME precision, and clearer naming.
  • Treat timers as the property of the FB that owns the related machine state. Avoid scattering timing logic across FCs.
  • Use UDTs to bundle timer + BOOL outputs into a reusable "TimerFace" interface, then pass the UDT by reference between FBs.
  • When migrating STEP 7 Classic code to TIA Portal, replace SD / SE / SS / SF / SP instructions with TP / TON / TOF / TONR blocks and store them in the FB static area.
  • Reserve absolute T identifiers only for legacy routines; document their ownership in the variable comments.

FAQ

Can I declare a TIMER as an input parameter of an FC in TIA Portal?

No. FC formal parameters are limited to elementary data types (INT, DINT, REAL, BOOL, S5TIME-as-value, WORD, BYTE), plus pointer types (BLOCK_FB, BLOCK_FC, DB, VARIANT, ANY). TIMER, COUNTER, IEC_TIMER, and TON/TOF/TP/TONR require a persistent instance binding that an FC cannot provide.

Why does typing T0 directly inside an FC work, but passing a timer symbol from an FB does not?

Hard-coding T0 resolves to a fixed system memory address at compile time and bypasses the parameter mechanism. Passing a timer as a parameter requires call-by-reference semantics, which FCs only support for pointer-type parameters (BLOCK_FB, DB, VARIANT, ANY) — not for TIMER.

What is the correct way to share a timer between an FB and an FC?

Keep the timer instance inside the FB and expose its output bits (Q) and elapsed/remaining times (ET) as BOOL/TIME output parameters. Call the FB first, then pass those derived values into the FC as call-by-value parameters.

Are IEC timers (TP / TON / TOF / TONR) supported inside FCs?

Not as FC-internal instances. IEC timer blocks are implemented as FBs with required instance DB storage. They must be instantiated either as FB multi-instances or inside a global DB, then accessed from any block including FCs.

Does this rule also apply to STEP 7 Classic (S7-300 / S7-400) and to S7-1500?

Yes. The restriction on FC parameter types is consistent across STEP 7 V5.x, TIA Portal V13–V18, and S7-1200/1500 firmware families. The error message wording differs slightly, but the underlying rule (call-by-value only for elementary types) is unchanged. Refer to the TIA Portal parameter-passing documentation for the current firmware-specific matrix.

Back to blog