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:
-
L #DIFFloads the deviation into ACCU1. The previous contents of ACCU1 shift to ACCU2. ACCU1 = DIFF. -
L #MAX_DIFFloads the limit. ACCU1 = MAX_DIFF, ACCU2 = DIFF. -
>=Rcompares ACCU2 (DIFF) against ACCU1 (MAX_DIFF) as REAL. RLO = 1 if DIFF >= MAX_DIFF, else 0. ACCU contents are not modified. -
JC ALARjumps to label ALAR if RLO = 1; otherwise continues. -
TAKswaps ACCU1 and ACCU2. After: ACCU1 = DIFF, ACCU2 = MAX_DIFF. -
L #MAX_DIFFreloads MAX_DIFF into ACCU1. ACCU1 = MAX_DIFF, ACCU2 = DIFF. -
NEGRnegates ACCU1 as REAL. ACCU1 = -MAX_DIFF. -
<=Rcompares ACCU2 (DIFF) against ACCU1 (-MAX_DIFF). RLO = 1 if DIFF <= -MAX_DIFF. - 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:
-
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>Ron both ends. - 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.
- 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:
- 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.
- 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.
-
No RLO gymnastics. No
JC, noTAK, noA(/O(nesting. The block is four lines of STL and reads top-to-bottom. -
Type-generic. The
ABSinstruction 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 |
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.
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:
-
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. -
Type checking. The SCL compiler checks that
ABSis called with a numeric type and that the comparison operands match. STL>=Ris implicitly REAL and does not catch type mismatches until the runtime test in the online watch table. - 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:
- Open the source FB in STEP 7 V5.5 and export the STL source as a
.srcfile from the menu Source > Generate Source. - In TIA Portal, create a new FB with the same interface (rename
DIFF,MAX_DIFF,ALARMas needed to match the existing instance DB). - 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 assignmentALARM := ABS(DIFF) > MAX_DIFF;for the common case. - 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.
- 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 |
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:
- 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).
- Force
DIFFto each test value in turn using the modify function. - Force
MAX_DIFFto the configured value (e.g., 5.0). - Observe
ALARMin the same DB. Verify the expected boolean state against the table above. - 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.