S7-1200 Analog Output Rate Limiter Using TIA Portal V13 Cyclic OB

David Krause18 min read
S7-1200SiemensTutorial / 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

S7-1200 Analog Output Rate Limiter Using TIA Portal V13 Cyclic OB

Field-tested procedure for ramping a SIMATIC S7-1200 analog output toward a commanded setpoint at a fixed rate (200 raw units per second), with working SCL and LAD code in TIA Portal V13 SP1 / STEP 7 Basic V13 SP1. The pattern is generic and applies to any CPU 1211C/1212C/1214C/1215C/1217C.

Read this first. The High Speed Counter extension block (CTRL_HSC_EXT) is the wrong tool for this job. HSC blocks count encoder pulses, they do not enforce a rate of change on an INT tag. The correct mechanism for time-based ramp generation on S7-1200 is a Cyclic Interrupt OB (OB30–OB38), where the OB's phase offset and cycle time are fixed by configuration and the user program runs deterministically at the requested interval.

1. Problem Statement and Requirements

An operator enters a percentage between 0 and 100 on an HMI tag. The PLC must convert that percentage to a raw analog value in the Siemens integer range 0–27648 (the canonical range for 0–10 V or 0/4–20 mA on the onboard analog outputs of the CPU 1214C and on SM analog output modules).

If the operator jumps from 0 % to 100 % in a single step, the raw integer is not allowed to step directly to 27648. The process variable must rise in increments of 200 units per second. Likewise, on a decreasing command it must fall by 200 units per second until it reaches the commanded value. The conversion can be summarized as:

Symbol Meaning Type Range
Sp_Pct Operator setpoint INT (HMI) 0 – 100
Sp_Raw Scaled command INT 0 – 27648
Out_Raw Ramped output value INT 0 – 27648
StepRate Allowed change per cycle INT 200 (units/s)
CycleTime OB30 phase TIME 1000 ms
AQ Analog output word INT 0 – 27648

The scaling from percentage to raw value uses the standard Siemens linear conversion:

Sp_Raw = (Sp_Pct * 27648) / 100

For a CPU 1214C DC/DC/DC (6ES7214-1AG40-0XB0) the two onboard analog outputs are addressed as %AQ0 and %AQ2 (the two-word alignment is fixed by the analog output channel granularity). For the DC/DC/RLY variant (6ES7214-1BG40-0XB0) you must add an SB 1232 or SM 1232 analog output module – there is no onboard AQ on the relay variant. Refer to the S7-1200 Programmable Controller System Manual, section 6.3 "Analog outputs", for channel address layout.

2. Why CTRL_HSC_EXT Is the Wrong Block

CTRL_HSC_EXT (and its smaller cousin CTRL_HSC) are HSC (High Speed Counter) control blocks from the "Counters and Measurement" technology object family in TIA Portal. They are designed to:

  • Configure a hardware counter channel (CU, CD, or incremental encoder).
  • Load a comparison value, set a hardware interrupt threshold, and reset the counter.
  • Read measured frequency / period / velocity from the HSC hardware.

None of these functions enforce a rate of change on an INT tag. CTRL_HSC_EXT cannot be told "increase this INT by 200 every 1000 ms until it equals Sp_Raw". It operates on a 32-bit counter hardware register, not on user memory, and it does not call a user-defined ramp function. If you wire CTRL_HSC_EXT into the program you will compile successfully but the rate-limit logic will never run.

Discard the HSC path. Use a cyclic interrupt OB instead.

3. Architecture Overview

HMI Tag Sp_Pct (0-100) Scale block 0-100 → 0-27648 Sp_Raw (INT) command Out_Raw (INT) ramped Cyclic OB30 Δ = ±200 / 1000 ms Saturate 0 – 27648 Out_Raw := Out_Raw + Δ with sign(Sp_Raw - Out_Raw) %AQ0 / %AQ2 Onboard AQ or SM 1232 0-10 V / 4-20 mA field signal Cycle: OB30 fires every 1000 ms. Each firing compares Sp_Raw vs. Out_Raw, computes Δ = SIGN(Sp_Raw - Out_Raw) · 200, then writes Out_Raw + Δ into %AQ0. Maximum slew: 27648 / 200 = 138.24 s end-to-end at 100 % step. Saturation guards prevent underflow below 0 and overflow above 27648.

4. Prerequisites

Item Required Notes
STEP 7 Basic V13 SP1 (TIA Portal) V13 SP1 Update 4 or later Update 5 (released 2015) recommended for stability.
CPU 1214C firmware V4.0 minimum, V4.2+ recommended Allows OB30–OB38 cyclic interrupts.
Hardware CPU 1214C DC/DC/DC for onboard AQ, or DC/DC/RLY + SM 1232 See S7-1200 System Manual.
User program blocks OB1 (main), OB30 (cyclic), FB "RampLimiter", DB instance, global tags DB can be omitted if FB uses multi-instance instead.
HMI tag mapping INT tag "Sp_Pct" 0–100 Use absolute mode scaling on the HMI for direct INT bound.

If TIA Portal V13 SP1 is not yet installed, obtain the package from the Siemens Industry Online Support portal. Newer TIA Portal versions (V15, V16, V17) are backward-compatible with the V13 SP1 project but V13 SP1 cannot open projects saved by V14+.

5. Step 1 — Define the Tags

In the PLC tag table, add the following global tags. Right-click PLC tags → Add new tag table, then create:

Name Data type Address Initial value Comment
Sp_Pct INT %MW100 0 Operator command 0–100 %
Sp_Raw INT %MW102 0 Scaled command 0–27648
Out_Raw INT %MW104 0 Ramped output value
StepRate INT %MW106 200 Units per cycle
AQ_Out INT %AQ0 – Analog output word, channel 0
Memory layout. %MW100, %MW102, etc. are word addresses in the S7-1200 bit-memory area. They survive a STOP→RUN transition only if you mark the DB as retentive or use System clock byte / Retentive memory settings under PLC properties. Mark Out_Raw as retentive if you want the ramp to resume from its current value after a power cycle, otherwise it will reset to 0 and snap back up at 200 units/s.

6. Step 2 — Create the Function Block "RampLimiter"

The FB encapsulates the rate-limit math so it can be reused if you later add a second analog channel with a different slew rate. In the project tree, add Program blocks → Add new block → Function Block, name it RampLimiter, language SCL.

6.1 SCL Implementation

FUNCTION_BLOCK "RampLimiter"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT
      Sp_Raw       : INT;     // commanded value 0..27648
      StepRate     : INT;     // units per OB cycle (default 200)
      Enable       : BOOL;    // 1 = ramp, 0 = hold
   END_VAR
   VAR_OUTPUT
      Out_Raw      : INT;     // ramped value
      Done         : BOOL;    // 1 when reached setpoint
      Error        : WORD;    // 0 = OK, 16#0001 = invalid range
   END_VAR
   VAR
      Diff         : DINT;    // signed difference
   END_VAR
   VAR RETAIN
      Out_Raw_R    : INT;     // internal retained copy
   END_VAR
BEGIN
   Error := 16#0000;
   Done := FALSE;

   // ---- 1. Range guard ----
   IF (Sp_Raw < 0) OR (Sp_Raw > 27648) OR (StepRate <= 0) THEN
       Error := 16#0001;
       Out_Raw := Out_Raw_R;
       RETURN;
   END_IF;

   // ---- 2. Initialise retained memory on first call ----
   IF NOT Enable THEN
       Out_Raw := Out_Raw_R;
       RETURN;
   END_IF;

   // ---- 3. Compute signed difference ----
   Diff := INT_TO_DINT(Sp_Raw) - INT_TO_DINT(Out_Raw_R);

   // ---- 4. Apply rate-limited step ----
   IF ABS(Diff) <= INT_TO_DINT(StepRate) THEN
       // Within one step of target — snap to setpoint
       Out_Raw_R := Sp_Raw;
       Done := TRUE;
   ELSIF Diff > 0 THEN
       // Ramping up
       Out_Raw_R := Out_Raw_R + StepRate;
   ELSE
       // Ramping down, guard against INT underflow
       Out_Raw_R := Out_Raw_R - StepRate;
   END_IF;

   // ---- 5. Saturate ----
   IF Out_Raw_R < 0 THEN Out_Raw_R := 0; END_IF;
   IF Out_Raw_R > 27648 THEN Out_Raw_R := 27648; END_IF;

   Out_Raw := Out_Raw_R;
END_FUNCTION_BLOCK

Key engineering points:

  • INT + INT can overflow. Two INT values up to 27648 cannot exceed 32767 (max INT), so Out_Raw_R + StepRate is safe. If you later raise the maximum to 32767 or higher you must convert to DINT before addition.
  • The "snap-to-target" branch prevents the output from oscillating forever around the setpoint when the residual error is smaller than one step.
  • Retain on Out_Raw_R preserves the ramp position through a CPU restart. If you do not want that behaviour, remove RETAIN and the output will reset to 0 on every restart.

6.2 LAD Equivalent (for Engineers Who Prefer Ladder)

If SCL is not available or your site standard mandates LAD, the equivalent is a network of compares, additions, and a SET/RESET flip-flop. The structure is identical; here is a network-by-network summary.

// Network 1 — range guard
A    Sp_Raw
L    0
<I
S    Error_Flag         // on if negative
A    Sp_Raw
L    27648
>I
S    Error_Flag         // on if above max
A    Error_Flag
BEC                     // skip remainder if invalid

// Network 2 — load retained Out_Raw_R into working word
L    Out_Raw_R
T    Out_Raw

// Network 3 — done / next-step direction
L    Sp_Raw
L    Out_Raw_R
-I
T    Diff
L    0
>I
=    RampUp             // Diff > 0 → must go up
L    Diff
ABS
L    StepRate
>=I
=    WithinOneStep

// Network 4 — apply step
A    WithinOneStep
JCN  NO1
L    Sp_Raw
T    Out_Raw_R
S    Done
JU  END_STEP
NO1: A  RampUp
JCN  NO2
L    Out_Raw_R
L    StepRate
+I
T    Out_Raw_R
JU  END_STEP
NO2: L  Out_Raw_R
L    StepRate
-I
T    Out_Raw_R
END_STEP: NOP 0

// Network 5 — saturate at 0 and 27648
L    Out_Raw_R
L    0
<I
JCN  SAT1
L    0
T    Out_Raw_R
SAT1: L    Out_Raw_R
L    27648
>I
JCN  SAT2
L    27648
T    Out_Raw_R
SAT2: L    Out_Raw_R
T    Out_Raw

For a visual representation, the I/O is identical to the SCL version. Network 4 implements the up/down/snap branches with conditional jumps.

7. Step 3 — Create the Cyclic Interrupt OB30

Add Program blocks → Add new block → Organization Block → OB type: Cyclic interrupt. Name it Main_Ramp_Cyclic. After creation, open its properties and set the Cycle time to 1000 ms and the Phase offset to 0 ms (or any value that does not collide with other cyclic OBs).

OB30 property Value Reason
OB number 30 Standard cyclic interrupt, 5 ms default, configurable.
Cycle time 1000 ms Matches the "200 units per second" rate requirement.
Phase offset 0 ms Deterministic start of first execution after RUN.
Priority 9 (default) Lower than OB1 (1) so OB1 I/O stays consistent.

Inside OB30, call the FB once per firing:

// Main_Ramp_Cyclic — fires every 1000 ms
IF TRUE THEN
    "Ramp_DB"(   // instance DB for RampLimiter
        Sp_Raw   := "Sp_Raw",
        StepRate := "StepRate",
        Enable   := TRUE,
        Out_Raw  => "Out_Raw",
        Done     => "Done_Tag",
        Error    => "Err_Tag"
    );

    // Hand ramped value to physical analog output
    %AQ0 := "Out_Raw";
END_IF;

OB30 runs independently of the main scan. Even if OB1 spends 50 ms on a heavy block, OB30 still fires on the configured 1000 ms grid because the cyclic interrupt is dispatched by the S7-1200 firmware scheduler before the next user cycle finishes.

8. Step 4 — Scaling in OB1

OB1 only needs to convert Sp_Pct (0–100) to Sp_Raw (0–27648). Keep this in the main cycle because the operator HMI tag changes asynchronously and 1 ms latency is fine.

// OB1 Segment 1 — percent to raw conversion
"Sp_Raw" := ("Sp_Pct" * 27648) / 100;

If you prefer floating-point and round-trips through REAL, the equivalent is:

// REAL scaling variant
#sp_real := INT_TO_REAL("Sp_Pct") / 100.0;
"Sp_Raw" := REAL_TO_INT(#sp_real * 27648.0);

For the integer variant, watch the rounding direction. Siemens rounds toward zero. For an operator entry of "100" the result is exactly 27648. For "1" the result is 276. For "37" the result is 10229. If your process cannot tolerate this quantisation, use the REAL path and round explicitly with ROUND or CEIL before REAL_TO_INT.

9. Step 5 — Configure the Analog Output

In Devices & Networks, double-click the analog output channel of the CPU 1214C (or SM 1232). Set:

Parameter Value Comment
Output type Voltage 0–10 V (or Current 4–20 mA) Select on the channel's properties.
Output range 0–10 V / 0–20 mA / 4–20 mA Match the actuator's input card.
Diagnostics Wire-break enable (for 4–20 mA only) Generates an I/O fault if loop opens.
Substitute value 0 Value written when CPU is in STOP, prevents actuator bump.

The CPU writes the integer 0 to 27648 into the analog channel and the firmware converts it to the physical voltage or current. See the S7-1200 System Manual, section "Representation of analog values".

10. Verification and Commissioning

  1. Download the project to the CPU 1214C and place the CPU in RUN.
  2. Watch table test: open PLC → Watch & force tables → Add watch table. Add Sp_Pct, Sp_Raw, Out_Raw, %AQ0, Done_Tag.
  3. Force Sp_Pct = 0 and let Out_Raw settle at 0. Confirm %AQ0 reads 0.
  4. Force Sp_Pct = 100. Out_Raw should rise by exactly 200 each second: 0, 200, 400, 600, ... up to 27648. The transition takes 138.24 seconds. Confirm monotonic increase in the watch table (force periodic refresh <= 1 s).
  5. Force Sp_Pct = 50 mid-ramp. Out_Raw should reverse direction within one OB30 cycle, falling at 200 units/s until it hits 13824 (50 % of 27648).
  6. Force Sp_Pct = 49 (within one step of current value if currently at 13824). Done_Tag should turn on at the next OB30 firing because the snap-to-target branch fires.
  7. Disconnect the field loop (4–20 mA only). The wire-break diagnostic should trigger an I/O fault. OB30 keeps running and updating Out_Raw, but %AQ0 will not physically drive the field if hardware output is disabled. Always re-evaluate the safety implication of this behaviour in your risk assessment.
  8. STOP→RUN test: cycle the CPU mode. If Out_Raw_R is RETAIN it resumes at its last value; otherwise it resets to 0 and climbs again.

11. Edge Cases and Field Caveats

11.1 Faster Cycle Time

If you need a finer ramp granularity, drop OB30 cycle to 100 ms and scale StepRate to 20 (still 200 units/s). The S7-1200 minimum cycle time for OB30 is 1 ms, but anything below 5 ms is unsafe in cyclic interrupt OB 30 — use OB35–OB38 (configured 1 ms minimum) if you need sub-millisecond timing. The arithmetic remains identical.

11.2 Multi-Channel

If you need two channels ramping independently, declare two instance DBs ("Ramp_DB_Ch0", "Ramp_DB_Ch1") and call the FB twice inside OB30. The CPU 1214C DC/DC/DC has two onboard outputs (%AQ0 and %AQ2). Add a separate StepRate tag per channel if rates differ.

11.3 HMI Smoothing

If the operator knob on the HMI is also being polled, the HMI may not see a smooth ramp because it reads Sp_Pct (the command), not Out_Raw. Bind the HMI display to Out_Raw via a separate tag (Sp_Display). Otherwise the operator will see the command jump to 100 even though the field value is still ramping up.

11.4 Network Dropout / HMI Disconnect

If the HMI connection fails, Sp_Pct retains its last value because it lives in the PLC tag table. Out_Raw continues to ramp toward the last command. This is the desired behaviour for most processes — a stuck ramp at the last command is safer than a snap to zero — but document the behaviour in your FMEA.

11.5 Saturation Math

If Sp_Raw suddenly flips from 27648 to 0 the ramp falls 200 per second. At that rate it takes 138.24 s to reach 0. Some processes (e.g. pneumatic valves) require a different down-rate than up-rate. Add a StepRate_Dn input to the FB and select inside the down-branch:

// Replace the down-step in the FB with:
ELSIF Diff < 0 THEN
    IF StepRate_Dn > 0 THEN
        Out_Raw_R := Out_Raw_R - StepRate_Dn;
    ELSE
        Out_Raw_R := Out_Raw_R - StepRate;
    END_IF;
END_IF;

11.6 Skip OB30 on a Slow Scan

If the user program overruns and the cyclic OB misses its scheduled phase, the S7-1200 firmware generates an OB80 (time error) and either calls the OB80 error handler or logs a diagnostic entry. By default the CPU stays in RUN and the late OB30 fires immediately. If your application depends on a precise ramp time you must enable OB80 and count skipped firings.

12. Common Errors and Error Matrix

Symptom Likely cause Fix
Out_Raw stays at 0, never moves OB30 not generated or not wired into the call Verify OB30 → Properties → Cycle time is enabled; confirm DB instance exists.
Out_Raw snaps instantly to Sp_Raw Code placed in OB1 instead of OB30, or OB30 cycle too short (e.g. 1 ms) and step accumulates over many calls Verify block is called inside OB30 only; check cycle time in OB properties.
Out_Raw oscillates around Sp_Raw Snap-to-target branch missing or StepRate larger than the smallest Sp_Raw delta Add the within-one-step branch shown in section 6.1.
Output signal has high ripple StepRate is too large and the actuator hunts Lower StepRate or increase cycle resolution.
Output never exceeds 0 or never goes above a value Saturate branch missing or wrong direction in subtract Re-verify saturation guards in section 6.1 step 5.
Err_Tag = 16#0001 Sp_Raw out of range, or StepRate <= 0 Check HMI bounds; PLC accepts negative or >27648 integers without scaling.
Ramp resets on every power cycle RETAIN not set on Out_Raw_R Open DB instance, mark the tag RETAIN.
CPU goes STOP after first ramp step OB80 time error or analog output diagnostic not handled Check diagnostic buffer in TIA Portal → Online → Diagnostics.
AQ reads a different value than Out_Raw Another block writes %AQ0 in OB1 Search for all writes to %AQ0; only OB30 should write the analog output word.

13. Alternate Implementations

13.1 IEC 61131-3 Standard Timer Block (No Cyclic OB)

If your plant standard disallows cyclic OBs, you can implement the ramp in OB1 using a TP (pulse timer) at 1000 ms triggering a single increment. This works but ties the ramp to the main scan, which can drift under heavy CPU load. Use it only if the rate precision is non-critical.

13.2 S7-1500 Equivalent

On S7-1500 the same logic applies, but use the Runtime OB (OB30) under the new naming convention and enable optimisations in the FB. The IEC timer resolution is higher and you can drop StepRate to 1 with a 10 ms OB30 to get smoother ramps. See the SIMATIC S7-1500 Automation System Manual.

13.3 Using the PLC Web Server

On CPU 1214C firmware V4.0 and above with the Web Server activated, you can read Out_Raw in a browser at http://<plc-ip>/. Useful for sanity checks during commissioning when the HMI is not yet connected.

14. Performance and Resource Budget

Resource Used Available
User program memory ~ 600 bytes for RampLimiter FB + 200 bytes OB30 50 KB on CPU 1214C for user code (work memory) and 2 MB load memory.
Instance DB ~ 60 bytes 2048 DBs available (DB1–DB2047), 64 KB DB size limit.
OB30 priority class 9 Classes 1–26 available.
Scan time impact ≈ 30 µs per OB30 firing on 1214C Negligible against 1 ms main scan.

The ramp adds essentially no scan-time load and consumes less than 1 % of the 1214C's user memory budget.

15. Putting It Together — The Full Control Loop

Once the ramp block is in place, the entire process loop is:

  1. Operator entry — HMI tag Sp_Pct (0–100).
  2. Scale — OB1 converts Sp_Pct to Sp_Raw (0–27648) using integer math.
  3. Ramp — OB30 fires every 1000 ms; FB "RampLimiter" walks Out_Raw toward Sp_Raw at 200 units/s, with saturation and snap-to-target.
  4. Output — OB30 writes Out_Raw to %AQ0; the onboard analog converts it to 0–10 V or 4–20 mA.
  5. Feedback (optional) — An analog input monitors the field; compare to Out_Raw to detect stuck actuator. This is outside the ramp logic but is the natural place to add process safety.

For further background on Siemens analog value representation and the S7-1200 cyclic interrupt model, refer to the SIMATIC S7-1200 Programmable Controller System Manual, the STEP 7 Basic V13 SP1 Programming and Operating Manual, and Siemens application entry "Cyclic interrupt OBs" in the Industry Online Support knowledge base.


What is the correct block to rate-limit an INT tag on S7-1200?

Use a Cyclic Interrupt OB (OB30–OB38) configured to fire every 1000 ms. Inside the OB, call a user-written FB that compares the setpoint against the current value and adds or subtracts 200 each cycle, saturating at 0 and 27648. The High Speed Counter block CTRL_HSC_EXT is for hardware counters and cannot enforce a rate of change on a tag.

How long does a 0–100 % step take at 200 units per second?

The maximum raw integer is 27648, and the step rate is 200 units/s. The end-to-end ramp time is therefore 27648 / 200 = 138.24 s. For 50 % it is half that (≈ 69 s). Set StepRate = 200 and OB30 cycle time = 1000 ms to match the spec exactly.

Can I use a 100 ms cycle time and a 20-unit step to get the same 200 units/s ramp?

Yes. Change OB30 cycle time to 100 ms and StepRate to 20. The result is identical in average rate, but the per-cycle granularity is finer, so the actuator moves in smaller, more frequent steps. This is useful if the downstream valve or drive is sensitive to step size.

Why does my analog output jump immediately to the setpoint even though OB30 is configured?

Three common causes: (1) the ramp FB is being called inside OB1 instead of OB30, so the main scan executes the math every cycle; (2) another block writes %AQ0 in OB1 and overrides the ramp; (3) OB30 cycle time is set to 1 ms by default and StepRate accumulates many steps per second, hiding the ramp visually. Verify the call site and the OB properties.

How do I make the ramp remember its position after a power cycle?

Declare the internal retained variable Out_Raw_R with the RETAIN keyword in the FB. The S7-1200 firmware preserves RETAIN variables through STOP→RUN, warm restart, and power-off if the CPU has a retentive memory area configured (default is 10 KB on the 1214C). Mark only the variables you need to preserve as RETAIN, otherwise you consume that 10 KB budget quickly.

Does this work on S7-1500 or LOGO! as well?

Yes on S7-1500 — the same FB body works with optimisations enabled and a higher-resolution cyclic OB. On LOGO! the approach is different because LOGO! has no cyclic interrupt OB; the equivalent uses a pulse relay and an analog multiplexer, or the LOGO! Soft Comfort built-in ramp function block (no SCL available). For LOGO! refer to the LOGO! 8 System Manual instead.

Back to blog