Siemens S7 STL Alarm Threshold: Using ABS for |DIFF| > MAX

David Krause21 min read
PLC ProgrammingSiemensTIA Portal
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

Siemens S7 STL Absolute-Value Alarm Threshold with ABS

Engineer field notes on the cleanest way to raise an alarm when a process deviation exceeds a symmetric limit in SIMATIC S7-300/400, S7-1500, and TIA Portal. Covers the four common STL spellings, the ladder A()/O() form, the SCL form, the TIA Portal migration path, IEEE-754 edge cases, and a commissioning test plan.

1. Problem Definition and Use Cases

The classic alarm-threshold problem in process automation is symmetric around zero: trigger a boolean output when the magnitude of a measured deviation crosses a fixed limit, irrespective of sign. The textual specification is usually written in one of three equivalent forms:

  • "If the value is greater than +5 or less than -5, then raise the alarm."
  • "If the absolute value of the value is greater than 5, then raise the alarm."
  • Trigger ALARM when |DIFF| > MAX_DIFF.

All three forms are equivalent. The first is the form typically written in a project specification, the second is the most natural mathematical expression, and the third is the form that translates most cleanly into code. The challenge on a SIMATIC controller is that STL historically does not expose a single, intuitive absolute-value comparison. Programmers wrote the logic as two consecutive comparisons with sign reversal, which produced several stylistically different implementations, all correct, but with subtle differences in stack behavior, RLO flow, and code review legibility.

This reference documents the four common spellings of the alarm threshold on a SIMATIC S7-300 or S7-400 (the platforms where STL is still a first-class editor language), evaluates them against the IEC 61131-3 and SCL equivalents, and recommends the form that should be used in new code. The same forms apply unchanged on S7-1500 in TIA Portal, where STL is available as a legacy view, and on WinAC RTX controllers. On S7-1200 and S7-1500 the strategic long-term form is SCL or ladder.

Application DIFF Source MAX_DIFF Typical
Pressure deviation from setpoint PV - SP (REAL) 0.5 bar
Speed error in a closed-loop drive n_set - n_act 20 rpm
Position lag on a servo axis Following error 0.05 mm
Heat-exchanger temperature differential T_in - T_out 2.0 °C
Cylinder force imbalance F_left - F_right 50 N
Level deviation in a tank Level - Setpoint 15 mm

2. Original STL Implementation: Two Comparisons with TAK

The first STL implementation is a literal translation of the textual specification ("greater than +MAX_DIFF OR less than -MAX_DIFF"). It uses the floating-point comparison >=R for both directions and a conditional jump JC to skip the second branch when the first branch is already true. The TAK instruction swaps ACCU1 and ACCU2 to bring #DIFF back into ACCU1 for the second comparison.


// Alarm if DIFF >= +MAX_DIFF OR DIFF <= -MAX_DIFF
      L     #DIFF          // ACCU1 = DIFF (ACCU2 = previous ACCU1)
      L     #MAX_DIFF      // ACCU1 = MAX_DIFF, ACCU2 = DIFF
      >=R                  // RLO := (DIFF >= MAX_DIFF), ACCU unchanged
      JC    ALAR            // jump to ALAR if RLO = 1
      TAK                   // swap ACCU1 and ACCU2; ACCU1 = DIFF again
      L     #MAX_DIFF       // ACCU1 = MAX_DIFF
      NEGR                  // ACCU1 = -MAX_DIFF, ACCU2 = DIFF
      <=R                  // RLO := (DIFF <= -MAX_DIFF)
ALAR: =     #ALARM

Line-by-line:

  1. L #DIFF loads the deviation into ACCU1. The previous contents of ACCU1 shift to ACCU2. ACCU1 = DIFF.
  2. L #MAX_DIFF loads the limit. ACCU1 = MAX_DIFF, ACCU2 = DIFF.
  3. >=R compares ACCU2 (DIFF) against ACCU1 (MAX_DIFF) as REAL. RLO = 1 if DIFF >= MAX_DIFF, else 0. ACCU contents are not modified.
  4. JC ALAR jumps to label ALAR if RLO = 1; otherwise continues.
  5. TAK swaps ACCU1 and ACCU2. After: ACCU1 = DIFF, ACCU2 = MAX_DIFF.
  6. L #MAX_DIFF reloads MAX_DIFF into ACCU1. ACCU1 = MAX_DIFF, ACCU2 = DIFF.
  7. NEGR negates ACCU1 as REAL. ACCU1 = -MAX_DIFF.
  8. <=R compares ACCU2 (DIFF) against ACCU1 (-MAX_DIFF). RLO = 1 if DIFF <= -MAX_DIFF.
  9. The label ALAR receives the RLO and assigns it to the boolean #ALARM.

The form is correct but has three characteristics that draw criticism in code reviews:

  1. Inclusive comparison. The textual spec reads "greater than 5" (strict), but the code uses >=R, which is inclusive. For most process values the distinction is academic, but if the spec is intentionally inclusive ("raise the alarm when the deviation reaches the limit") this is correct; if the spec means "raise the alarm only when the deviation is strictly outside the band," use >R on both ends.
  2. The JC / TAK pattern. The conditional jump is fine in a small block, but in a long FC the SCAN counter is incremented and the prior RLO is consumed. In some plants the convention is "no jumps inside a function block," which this form violates.
  3. Stack pressure. The form leaves both accumulators loaded at the end of the network, which is harmless in a single block but worth noting when the block is included as a snippet inside a larger function with its own accumulator expectations.

3. Switched-Load STL Form: Eliminating the TAK

The second form swaps the order in which the operands are loaded so that the result of the first comparison is already in the right accumulator for the second. The TAK instruction is no longer required:


      L     #MAX_DIFF      // ACCU1 = MAX_DIFF
      L     #DIFF          // ACCU1 = DIFF, ACCU2 = MAX_DIFF
      <=R                  // RLO := (MAX_DIFF <= DIFF) i.e. (DIFF >= MAX_DIFF)
      JC    ALAR            // jump to ALAR if RLO = 1
      L     #MAX_DIFF       // ACCU1 = MAX_DIFF, ACCU2 = DIFF
      NEGR                  // ACCU1 = -MAX_DIFF
      <=R                  // RLO := (DIFF <= -MAX_DIFF)
ALAR: =     #ALARM

The trick is that the first comparison is written as <=R with the operands reversed (MAX_DIFF in ACCU2, DIFF in ACCU1), which is logically equivalent to DIFF >= MAX_DIFF. After the JC, the accumulators are ACCU1 = DIFF, ACCU2 = MAX_DIFF. Loading MAX_DIFF again shifts DIFF into ACCU2; NEGR gives -MAX_DIFF in ACCU1 with DIFF still in ACCU2. The second <=R then evaluates DIFF <= -MAX_DIFF directly, without the swap.

Trace at each step:

Instruction ACCU1 ACCU2 RLO
L #MAX_DIFF MAX_DIFF (prev)
L #DIFF DIFF MAX_DIFF
<=R DIFF MAX_DIFF (MAX_DIFF <= DIFF)
JC ALAR DIFF MAX_DIFF consumed
L #MAX_DIFF MAX_DIFF DIFF
NEGR -MAX_DIFF DIFF
<=R -MAX_DIFF DIFF (DIFF <= -MAX_DIFF)
= #ALARM -MAX_DIFF DIFF assigned

Both comparisons are inclusive. The form is functionally identical to the TAK form (with the same inclusive-vs-strict caveat) but eliminates one instruction and reads more cleanly. This is the form favored in plants where JC/TAK are discouraged but the team is not yet ready to migrate to SCL.

4. Ladder / FBD Form with A() and O() Brackets

The third form uses the ladder-style A( and O( bracket operators that are well suited to FBD and KOP editors. The expression is "DIFF >= MAX_DIFF OR (DIFF <= -MAX_DIFF)":


      A(
      L     #MAX_DIFF
      L     #DIFF
      <=R
      )
      O(
      L     #MAX_DIFF
      NEGR
      <=R
      )
      =     #ALARM

The A( opens a new RLO expression; the enclosed comparison produces a 0 or 1 RLO that the closing ) consumes. The O( opens a parallel branch; its enclosed comparison produces a second RLO. The closing ) ORs the two branches together and writes the result to #ALARM. This is the cleanest "no jumps" form and is preferred in plants where jumps are banned by convention.

The same logic in native ladder (KOP) is:


      MAX_DIFF      DIFF                DIFF
   ---[ >= ]---+---[/NEGR]---[ <= ]---
                |                       |
                +-----------[ OR ]------+
                            |
                            ALARM
                         ---( )---

In FBD, the negation of MAX_DIFF is performed by a NEG_R function block feeding the upper input of a GE_R / LE_R pair, with the outputs combined by an OR_FB and the result written to the ALARM output. The ladder and FBD forms are functionally identical to the STL bracket form.

5. The ABS Instruction Form (Recommended)

The recommended form uses the absolute-value instruction ABS. The instruction exists in four variants on S7-300/400 and S7-1500:

Instruction Operands Effect on ACCU1
ABS 16-bit INT (ACCU1-L) ACCU1-L := |ACCU1-L|; ACCU1-H, ACCU2 unchanged
ABS 32-bit DINT (ACCU1) ACCU1 := |ACCU1|; ACCU2 unchanged
ABS REAL (ACCU1) ACCU1 := |ACCU1|; ACCU2 unchanged
ABS LREAL (ACCU1, S7-1500) ACCU1 := |ACCU1|; ACCU2 unchanged

The STL block becomes a one-line comparison:


      L     #DIFF
      ABS
      L     #MAX_DIFF
      >=R
      =     #ALARM

This is the form that should be used in new code. The reasons are:

  1. Legibility. The expression "if the absolute value of DIFF is greater than MAX_DIFF, alarm" is exactly what the code says. A maintenance engineer reading the block does not need to trace ACCU1/ACCU2 contents to verify the sign has been handled correctly.
  2. One comparison instead of two. The block executes one floating-point comparison instead of two. On a 315-2 PN/DP the difference is 1-2 microseconds, irrelevant in OB1 but noticeable in a fast OB with many similar blocks.
  3. No RLO gymnastics. No JC, no TAK, no A(/O( nesting. The block is four lines of STL and reads top-to-bottom.
  4. Type-generic. The ABS instruction is overloaded by operand width, so the same code shape works for INT, DINT, REAL, and LREAL.

For a function block with interface variables, the body becomes:


FUNCTION_BLOCK FB_AlarmThreshold
VAR_INPUT
  DIFF      : REAL;       // current deviation
  MAX_DIFF  : REAL;       // symmetric alarm limit, must be >= 0.0
END_VAR
VAR_OUTPUT
  ALARM     : BOOL;
END_VAR
VAR_TEMP
  // empty
END_VAR
BEGIN
      L     #DIFF;
      ABS;
      L     #MAX_DIFF;
      >=R;
      =     #ALARM;
END_FUNCTION_BLOCK

Edge case to be aware of: in STEP 7 V5.x, the ABS instruction does not explicitly propagate NaN. A NaN input passes through ABS unchanged (IEEE 754 specifies that abs(NaN) = NaN), and the subsequent >=R comparison evaluates to FALSE (NaN compares as unordered). The alarm will therefore stay low on NaN. If the process requires that NaN raise the alarm, the block must explicitly detect NaN and OR the NaN flag into ALARM. On +INF and -INF the behavior is correct: ABS(+INF) = +INF, ABS(-INF) = +INF, and +INF > MAX_DIFF is true, so the alarm raises on overflow.

6. SCL Implementation

In SCL the form is one expression and is the most natural of all options. The complete function block is:


FUNCTION_BLOCK FB_AlarmThreshold_SCL
{ S7_Optimized_Access := 'TRUE' }
VERSION : '1.0'
VAR_INPUT
  DIFF      : REAL;       // deviation, may be positive or negative
  MAX_DIFF  : REAL;       // symmetric alarm limit, must be >= 0.0
  bStrict   : BOOL;       // TRUE = strict >, FALSE = non-strict >=
END_VAR
VAR_OUTPUT
  ALARM     : BOOL;
END_VAR
BEGIN
  // Defensive: a negative limit means the alarm is always active
  // for any non-zero deviation. Force the limit to zero.
  IF MAX_DIFF < 0.0 THEN
    MAX_DIFF := 0.0;
  END_IF;

  IF bStrict THEN
    ALARM := (ABS(DIFF) > MAX_DIFF);
  ELSE
    ALARM := (ABS(DIFF) >= MAX_DIFF);
  END_IF;
END_FUNCTION_BLOCK

Compare against the equivalent SCL that uses the two-comparison form, which the IEC 61131-3 grammar also allows but is harder to read:


IF (DIFF >= MAX_DIFF) OR (DIFF <= -MAX_DIFF) THEN
  ALARM := TRUE;
ELSE
  ALARM := FALSE;
END_IF;

For the common case of a single threshold, ABS is preferred. The two-comparison form is useful only when the two thresholds are genuinely asymmetric (different positive and negative limits), in which case the SCL becomes:


IF (DIFF > MAX_POS) OR (DIFF < MAX_NEG) THEN
  ALARM := TRUE;
ELSE
  ALARM := FALSE;
END_IF;

Note that this is the same code shape as the original STL form, but it scales to asymmetric limits without modification. In the symmetric case the two thresholds satisfy MAX_NEG = -MAX_POS, and ABS(DIFF) > MAX_POS is the canonical expression.

7. Instruction Reference

For code-review purposes, the following is the relevant subset of the STEP 7 V5.x and TIA Portal V16+ instruction set used in this article. All references are to the official Siemens SIMATIC documentation.

Instruction Operands Description Reference
L <operand> All types Load operand into ACCU1; previous ACCU1 shifts to ACCU2. SIMATIC S7-300/400 STL Reference Manual
ABS INT / DINT / REAL / LREAL Form the absolute value of ACCU1; ACCU2 unchanged. SIMATIC S7-300/400 STL Reference Manual
NEGR REAL Negate ACCU1 as REAL; ACCU2 unchanged. SIMATIC S7-300/400 STL Reference Manual
>=R REAL Compare ACCU2 >= ACCU1 as REAL; set RLO. SIMATIC S7-300/400 STL Reference Manual
<=R REAL Compare ACCU2 <= ACCU1 as REAL; set RLO. SIMATIC S7-300/400 STL Reference Manual
>R / <R REAL Strict greater / less, set RLO. SIMATIC S7-300/400 STL Reference Manual
JC <label> Conditional jump to label if RLO = 1. SIMATIC S7-300/400 STL Reference Manual
TAK Swap ACCU1 and ACCU2. SIMATIC S7-300/400 STL Reference Manual
A( / O( / ) BOOL expression Open / close AND or OR bracket expression. SIMATIC Ladder Logic (LAD/FBD) Reference
ABS() (SCL) REAL / LREAL / INT / DINT Built-in function returning absolute value. SIMATIC SCL Programming Manual

For the most current S7-1500 instruction reference, see the TIA Portal help portal at Siemens Industry Online Support and search for "ABS" or "NEGR" in the STL/SCL instruction list for the S7-1500 CPU family.

8. Edge Cases and Floating-Point Considerations

Floating-point alarm thresholds behave differently from integer thresholds on corner-case inputs. The following table summarizes the behavior of the four STL forms and the SCL form on IEEE-754 special values. All values assume REAL (32-bit IEEE 754).

DIFF MAX_DIFF Form 1 (JC + TAK) Form 2 (Switched Loads) Form 3 (A()/O()) Form 4 (ABS) SCL ABS
0.0 5.0 FALSE FALSE FALSE FALSE FALSE
5.0 5.0 TRUE (inclusive) TRUE (inclusive) TRUE TRUE (inclusive) TRUE (inclusive)
-5.0 5.0 TRUE TRUE TRUE TRUE TRUE
5.00001 5.0 TRUE TRUE TRUE TRUE TRUE
NaN 5.0 FALSE (unordered) FALSE FALSE FALSE FALSE
+INF 5.0 TRUE (DIFF >= MAX) TRUE TRUE TRUE (ABS = +INF) TRUE
-INF 5.0 TRUE (DIFF <= -MAX) TRUE TRUE TRUE (ABS = +INF) TRUE
-0.0 5.0 FALSE (DIFF <= -MAX false) FALSE FALSE FALSE (ABS = +0) FALSE
subnormal 5.0 FALSE (no alarm) FALSE FALSE FALSE FALSE
Sign of zero: IEEE 754 defines both +0.0 and -0.0. In all four forms the behavior is consistent: ABS(-0.0) = +0.0 and the comparison is FALSE. The signed-zero case does not affect the alarm logic, but engineers should be aware that it exists and is preserved by NEGR (NEGR applied to +0.0 yields -0.0, which is distinct from -MAX_DIFF in the strict equality sense but identical in the ordering sense).

For integer types (INT/DINT), the situation is simpler. The MIN_INT (16-bit: -32768, 32-bit: -2147483648) has no positive counterpart, so ABS(MIN_INT) overflows. On S7-300 the result saturates at MIN_INT; on S7-1500 the behavior is implementation-defined but in practice saturates. For symmetric alarm thresholds in process control the deviation is rarely near the integer minimum, so this is a non-issue in practice, but if the input type is INT, prefer DINT or LREAL to avoid the corner case.

NaN policy: NaN propagation is the most important edge case for safety-critical alarms. A NaN deviation causes all four STL forms and the SCL form to return FALSE (NaN is unordered against any value, including itself). In a safety system this means the alarm silently fails to raise on a sensor dropout that produces NaN. The defensive pattern is to test DIFF <> DIFF (TRUE only for NaN) and OR the result into ALARM, or to use the input driver's quality byte from PCS 7 / APL to force ALARM to a defined state on BAD quality. Refer to the PCS 7 APL documentation for the standard "CH_DI" / "CH_AI" blocks that produce a quality byte alongside the value.

9. TIA Portal Migration: S7-300/400 to S7-1500

On S7-1200 and S7-1500 the strategic long-term form is SCL. The STL form L #DIFF; ABS; L #MAX_DIFF; >=R; = #ALARM; is still valid on S7-1500 CPUs (TIA Portal V16 and later support STL as a legacy view), but the SCL form ALARM := ABS(DIFF) > MAX_DIFF; is preferred for three reasons:

  1. Optimized block access. SCL blocks default to optimized access on S7-1500, which means symbolic tags are used directly without absolute address resolution. STL on S7-1500 with optimized blocks requires the % prefix or explicit symbolic load instructions, which clutters the code.
  2. Type checking. The SCL compiler checks that ABS is called with a numeric type and that the comparison operands match. STL >=R is implicitly REAL and does not catch type mismatches until the runtime test in the online watch table.
  3. Cross-reference and find-usages. The TIA Portal cross-reference, go-to-definition, and find-usages features work directly on SCL but only partially on STL. In a project with thousands of tags, this difference is significant for maintenance.

When migrating a STEP 7 V5.x STL block to TIA Portal, the recommended procedure is:

  1. Open the source FB in STEP 7 V5.5 and export the STL source as a .src file from the menu Source > Generate Source.
  2. In TIA Portal, create a new FB with the same interface (rename DIFF, MAX_DIFF, ALARM as needed to match the existing instance DB).
  3. Convert each STL network to SCL: replace the two-comparison form with IF ABS(DIFF) > MAX_DIFF THEN ALARM := TRUE; ELSE ALARM := FALSE; END_IF;, or use the direct assignment ALARM := ABS(DIFF) > MAX_DIFF; for the common case.
  4. Compile and download. Use the TIA Portal "Compare" tool to verify the interface matches the STEP 7 V5.5 instance DB; resolve any tag-name differences manually.
  5. Run the commissioning test plan in Section 11 against the new SCL block to confirm equivalence with the original STL behavior.

For projects that must remain in STL on S7-1500, the ABS form is portable unchanged. The TAK and switched-load forms should be replaced with the ABS form during migration for readability, and the ladder A()/O() form should be re-authored in SCL or KOP.

10. Best Practices and Conventions

The following conventions are recommended for alarm-threshold blocks in production PLC code.

1. Always use ABS for symmetric thresholds. The two-comparison form should be reserved for genuinely asymmetric limits. In a code review, the presence of two >=R/<=R instructions in the same network should trigger the question "is this ABS?"

2. Name the threshold MAX_DIFF or LIMIT_ABS, not THRESHOLD. The name should make the symmetry of the limit explicit. A variable named THRESHOLD is ambiguous because it could be interpreted as the upper limit of an asymmetric range.

3. Document the inclusive vs strict choice. In the FB header or in the instance DB comment, state whether the alarm raises on the threshold (inclusive, >=) or only outside the threshold (strict, >). The most common convention in process control is inclusive: "alarm when the deviation reaches the limit."

4. Validate the input range. Add an input check that rejects negative MAX_DIFF values, or clamps them to zero. A negative limit is almost always a configuration error and produces continuous alarms for any non-zero deviation. The SCL example in Section 6 demonstrates the clamp.

5. Use a hysteresis. A pure absolute-value comparison with no hysteresis will chatter the alarm if the process variable hovers near the limit. Implement a separate CLEAR_LIMIT (typically 90-95 % of MAX_DIFF) and latch/clear the alarm in a separate network. The hysteresis is not part of the threshold comparison itself; it is downstream.

6. Decouple the comparison from the annunciation. The comparison block produces a boolean output; the alarm-annunciation block (which writes to the HMI alarm tag, the WinCC message tag, or the PCS 7 alarm block) is a separate network or a separate FB. Mixing the two makes the block untestable.

7. Add a quality flag for floating-point inputs. In a PCS 7 environment, the input driver delivers a REAL value plus a quality byte. The alarm block should accept the quality byte and force ALARM = FALSE on BAD quality, or raise a separate "bad-quality" alarm. The threshold comparison is meaningless on a bad-quality input.

8. Provide a per-instance enable tag. Each alarm instance should have a BOOL enable tag so that the alarm can be inhibited during commissioning, maintenance, or sensor calibration. A runaway alarm that cannot be inhibited is a safety hazard.

11. Verification and Commissioning Tests

The alarm block must be tested at the boundary, not just inside the safe region. The following test vectors should be executed in the commissioning phase, in a watch table (STEP 7 V5.x) or in the Monitor & Force tool (TIA Portal).

Test # DIFF MAX_DIFF Expected ALARM (inclusive) Expected ALARM (strict) Notes
1 0.0 5.0 FALSE FALSE no deviation
2 4.999 5.0 FALSE FALSE inside band
3 5.0 5.0 TRUE FALSE threshold value
4 5.001 5.0 TRUE TRUE just outside band
5 -4.999 5.0 FALSE FALSE inside band, negative
6 -5.0 5.0 TRUE FALSE threshold value, negative
7 -5.001 5.0 TRUE TRUE just outside band, negative
8 0.0 0.0 TRUE (0 >= 0) FALSE limit = 0 misconfiguration
9 1.0E-30 0.0 TRUE TRUE tiny positive deviation
10 NaN 5.0 FALSE (default) / TRUE (NaN-policy) same verify NaN handling per spec
11 +INF 5.0 TRUE TRUE overflow positive
12 -INF 5.0 TRUE TRUE overflow negative
13 5.0 -1.0 clamp to 0.0 -> TRUE clamp to 0.0 -> TRUE invalid negative limit
14 5.0 5.0 TRUE then FALSE on hysteresis FALSE then TRUE verify hysteresis logic downstream
Test 8 (MAX_DIFF = 0): With the inclusive form ABS(DIFF) >= 0, ALARM is TRUE for any non-zero DIFF and FALSE for DIFF = 0.0 exactly. With the strict form ABS(DIFF) > 0, ALARM is TRUE for any non-zero DIFF and FALSE for DIFF = 0.0. The two forms diverge on the value 0.0 only because of the inclusive/strict choice. The alarm-block convention should reject MAX_DIFF = 0 at configuration time because it almost always indicates a misconfiguration (a sensor range error or an uninitialized HMI input).

The watch-table commissioning procedure is:

  1. Open the instance DB of the alarm FB in the online view (STEP 7: PLC > Monitor/Modify; TIA Portal: Project tree > PLC > Monitoring and forced tags).
  2. Force DIFF to each test value in turn using the modify function.
  3. Force MAX_DIFF to the configured value (e.g., 5.0).
  4. Observe ALARM in the same DB. Verify the expected boolean state against the table above.
  5. Repeat for each test vector. Capture screenshots for the FAT/SAT documentation.

For SCL blocks on TIA Portal, the same procedure is followed using the Monitor & Force tool. For ladder/FBD blocks, the same procedure is followed and the ALARM coil is observed in the online ladder view. For PCS 7, the standard CH_AI / CTRL_PID blocks provide the test entry points; the alarm block receives its input from a process tag and is monitored through the standard alarm acknowledgment workflow.

12. Frequently Asked Questions

Why does the original implementation need the TAK instruction?

After the first L #DIFF; L #MAX_DIFF; >=R sequence, the accumulators are ACCU1 = MAX_DIFF and ACCU2 = DIFF. The second branch needs ACCU1 = DIFF and ACCU2 = -MAX_DIFF. Without TAK, the second L #MAX_DIFF shifts DIFF (currently in ACCU2) further into ACCU3 or out of the visible register file, and ACCU1 = MAX_DIFF. The TAK swaps the accumulators so that ACCU1 = DIFF and ACCU2 = MAX_DIFF, restoring the operand layout expected by the second comparison. The ABS form eliminates the need for TAK by reducing the problem to one comparison with a pre-negated operand.

When should I use ABS vs two comparisons in STL?

Use ABS for any symmetric limit (the limit has the same magnitude in the positive and negative direction). Use two comparisons only when the limits are genuinely asymmetric, e.g., "alarm if DIFF > +10 OR DIFF < -3," which cannot be expressed with a single absolute value. In practice, more than 90 % of process-control alarm thresholds are symmetric, and the ABS form should be the default. The two-comparison form survives in legacy code from the STEP 7 V2 / V3 era when ABS on REAL was less universally available.

Does the ABS instruction work on S7-1500 with LREAL and DINT types?

Yes. The ABS instruction is overloaded in TIA Portal V16 and later to accept INT, DINT, REAL, and LREAL operands. The SCL built-in ABS() function works on all four types as well. For the 16-bit INT corner case where the input is -32768 (the most negative INT), the result on S7-1500 is -32768 because the negation overflows; for S7-300 the result is also -32768. For symmetric alarm thresholds prefer DINT or REAL to avoid this corner case; in practice, the deviation is never near MIN_INT in any real process.

How do I handle NaN floating-point values in the alarm block?

A NaN input causes all comparisons to return FALSE because NaN is unordered against any value, including itself. The alarm will stay low. If the process requires that NaN raise the alarm, the block must explicitly detect NaN with a self-comparison (DIFF <> DIFF evaluates to TRUE only for NaN) and OR the NaN flag into the alarm output. In a PCS 7 environment, prefer the quality-byte approach: if the input driver reports BAD quality (for example via the CH_AI / CTRL_PID standard blocks), force ALARM to a defined state (typically FALSE) and raise a separate "input bad quality" alarm on the HMI. Refer to the PCS 7 Advanced Process Library documentation for the standard input driver blocks.

Is SCL preferred over STL for new S7-1500 projects?

Yes. SCL is the recommended language for new S7-1200 and S7-1500 code because it integrates with the TIA Portal type system, supports optimized block access by default, and is required for some advanced features (such as multi-instance DBs with arrays of FBs, or the Get/Set multi-instance pattern). STL on S7-1500 is supported as a legacy view but is not the strategic direction. For new alarm-threshold blocks, the SCL form ALARM := ABS(DIFF) > MAX_DIFF; should be used. The STL ABS form is a fine choice for maintaining existing S7-300/400 code or for code blocks that need to remain portable between S7-300/400 and S7-1500.

What is the difference between >=R and >R in this context, and which is correct?

>=R (greater than or equal) raises the alarm at the threshold value, while >R (strictly greater) raises the alarm only for values strictly outside the band. The choice depends on the process specification: "raise an alarm when the deviation reaches the limit" is inclusive (use >=R); "raise an alarm when the deviation exceeds the limit" is strict (use >R). The two forms are equivalent except on the threshold value itself, which in floating point is statistically rare but in integer or stepped analog signals is reachable. Document the choice in the FB header and in the FAT documentation so that maintenance engineers do not have to guess.

Back to blog