How to Generate a Custom Square Wave in S7-SCL Using IEC Timers

To generate a square wave of a specific frequency and duty cycle in Siemens S7-SCL, calculate the high (on) and low (off) pulse durations in milliseconds from the target frequency (Hz) and duty cycle percentage, convert those values into the TIME data type, and feed them into two cross-coupled IEC timers. The cross-coupled timers toggle an output flag continuously whenever the block is called in a cyclic execution task.

Table of Contents

Technical Cause: Why Standard Clock Memory Bits Are Limited

Siemens SIMATIC S7 controllers provide built-in CPU clock memory byte bits, but these are fixed to predetermined frequencies (e.g., 0.5 Hz, 1 Hz, 10 Hz) with an unchangeable 50% duty cycle. Process applications such as pulse-width modulated (PWM) dosing pumps, periodic sampling triggers, strobe controls, or flashing diagnostic indicators frequently require custom frequencies and asymmetrical duty cycles (e.g., 20% on, 80% off).

Using standard CPU clock bits forces engineers to add complex counter logic to achieve custom timing. Implementing a dedicated Function Block (FB) using internal IEC timer instances allows precise mathematical derivation of high and low states dynamically within S7-SCL.

Prerequisites

Before deploying the square wave generator function block, ensure the following requirements are met:

  • Development Environment: Siemens TIA Portal or STEP 7 supporting S7-SCL (Structured Control Language).
  • Target Controller: SIMATIC S7-300, S7-400, S7-1200, or S7-1500 PLC.
  • Execution Task: Calling organization block (OB1 for standard main execution, or a cyclic interrupt OB such as OB35/OB30 for enhanced timing precision).

Implementation Workflow in S7-SCL

The custom signal generator operates by converting standard engineering units (frequency in Hertz and duty cycle in percent) into discrete timing durations.

Mathematical Conversion Flow

  1. Calculate Total Period (T):

T=frequency1​
2. Calculate On-Time (Ton​) and Off-Time (Toff​):

Ton​=T×(100.0dutyCycle​)Toff​=T−Ton​
3. Convert Seconds to Milliseconds:

Multiply the REAL time results by 1000.0 to yield milliseconds, then cast through DINT to TIME.
4. Cross-Couple Timers:

Pass NOT #out to the input of the ON-timer and #out to the input of the OFF-timer. When the ON-timer elapses, set the output high. When the OFF-timer elapses, set the output low, resetting the loop.

Full S7-SCL Source Code

The following code demonstrates the complete S7-SCL implementation. Define this structure inside a Function Block (FB) to ensure timer instance states are preserved across scan cycles.

Structured Text

FUNCTION_BLOCK "SquareWaveGenerator"
TITLE = Custom Square Wave Generator
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : AutomationEngineers
FAMILY : SignalGen

VAR_INPUT
    frequency : REAL;    // Target frequency in Hz (e.g., 2.0 for 2 Hz)
    dutyCycle : REAL;    // Target duty cycle in % (e.g., 50.0 for 50%, 25.0 for 25%)
END_VAR

VAR_OUTPUT
    squareWave : BOOL;   // Output square wave clock signal
END_VAR

VAR
    onTimer : TON_TIME;  // IEC On-Delay timer for high pulse duration
    offTimer : TON_TIME; // IEC On-Delay timer for low pulse duration
    out : BOOL;          // Internal state tracking bit
END_VAR

VAR_TEMP
    period : REAL;
    real_OnTime : REAL;
    real_OffTime : REAL;
    dint_OnTime : DINT;
    dint_OffTime : DINT;
    time_OnTime : TIME;
    time_OffTime : TIME;
    onTimerOutput : BOOL;
    offTimerOutput : BOOL;
END_VAR

BEGIN
    // Prevent division by zero if frequency input is invalid
    IF #frequency <= 0.0 THEN
        #squareWave := FALSE;
        #out := FALSE;
        RETURN;
    END_IF;

    // Calculate total period (seconds) and split by duty cycle
    #period := 1.0 / #frequency;
    #real_OnTime := (#dutyCycle / 100.0) * #period;
    #real_OffTime := #period - #real_OnTime;

    // Convert seconds to milliseconds (DINT)
    #dint_OnTime := REAL_TO_DINT(1000.0 * #real_OnTime);
    #dint_OffTime := REAL_TO_DINT(1000.0 * #real_OffTime);

    // Convert integer milliseconds to PLC TIME data type
    #time_OnTime := DINT_TO_TIME(#dint_OnTime);
    #time_OffTime := DINT_TO_TIME(#dint_OffTime);

    // Evaluate IEC timers using cross-coupled logic
    #onTimer(IN := NOT #out, PT := #time_OnTime, Q => #onTimerOutput);
    #offTimer(IN := #out, PT := #time_OffTime, Q => #offTimerOutput);

    // Toggle internal output state based on timer completions
    IF #onTimerOutput THEN
        #out := TRUE;
    END_IF;

    IF #offTimerOutput THEN
        #out := FALSE;
    END_IF;

    // Assign internal state to block output
    #squareWave := #out;

END_FUNCTION_BLOCK

Verification

To verify correct signal generation:

  1. Instantiate "SquareWaveGenerator" in OB1 or a Cyclic Interrupt OB.
  2. Supply a known configuration, such as frequency := 1.0 (1 Hz) and dutyCycle := 25.0 (25%).
  3. Open a Watch Table or Trace view in TIA Portal and monitor #squareWave.
  4. Confirm that the signal stays high for 250 ms and low for 750 ms continuously during execution.

Limitations and Operational Cautions

  • PLC Scan Time Dependency: When executed inside OB1 (Main Program Sweep), the precision of the generated frequency is bounded by the PLC scan cycle time. High frequencies (e.g., above 20 Hz) will experience timing jitter if the PLC scan time approaches or exceeds the required pulse duration.
  • High-Precision Applications: For high-speed PWM or exact pulse generation exceeding PLC scan resolution, use dedicated technology objects, high-speed digital output cards (TM PTO/PWM modules), or call the logic within a fast cyclic interrupt OB (e.g., OB35 at 1 ms intervals).
  • Division by Zero Protection: Always ensure input parameter validation (#frequency > 0.0) is enforced prior to calculating the reciprocal period length.

Engineer FAQ

Can I run this square wave generator inside a Function (FC) instead of a Function Block (FB)?

No. The cross-coupled timer logic relies on preserving internal timer states (TON_TIME) and output status (out) across multiple execution scans. Functions (FC) do not possess persistent static memory (VAR), whereas Function Blocks (FB) retain memory within their associated Instance Data Block.

How do I achieve higher frequency accuracy under varying CPU scan loads?

To eliminate scan time jitter, call the square wave generator function block inside a Cyclic Interrupt OB (such as OB35 or OB30) set to a fixed execution interval (e.g., 1 ms or 2 ms), rather than calling it in the main cyclic loop (OB1).

What happens if I set the duty cycle input to 0% or 100%?

Setting the duty cycle to 0.0 results in #time_OnTime evaluating to T#0ms, forcing #squareWave to remain perpetually FALSE. Setting it to 100.0 results in #time_OffTime evaluating to T#0ms, causing #squareWave to remain continuously TRUE.

Related Articles

Related Tags

Code: C243

6 respuestas