Overview
Speed setpoints in industrial automation are typically expressed in two ways: as an engineering unit (RPM, m/s, Hz) on the engineering and commissioning side, and as a percentage of rated speed on the operator HMI side. Converting between the two domains is a routine scaling task that lives in the PLC's logic, not in the HMI tag database. The reason is simple: the maximum rated RPM depends on the connected motor (or on a gearbox output shaft), and a motor change at commissioning time must propagate to every operator panel without rewriting HMI scripts.
This reference documents the canonical RPM-to-percentage conversion implemented in Siemens STEP 7 Statement List (STL) for the S7-300/S7-400 family, with direct equivalents for S7-1200/S7-1500 in TIA Portal using SCL. The same formula is portable to any IEC 61131-3 controller (ladder, FBD, structured text) and to any PLC vendor; only the operand syntax and integer/real conversion primitives change.
The mathematical relationship is linear:
% = 100 × RPM / RPMmax
Where RPMmax is the rated full-load speed of the motor (or the user-defined full-scale speed for the controlled shaft). The PLC stores RPM scaled by a factor of 10 (one decimal place) as a 16-bit integer; the conversion output stores percentage scaled by 100 (two decimal places) as a 16-bit integer. Both choices keep the maths inside the 16-bit signed range and avoid floating-point I/O at the fieldbus boundary.
Prerequisites
Before implementing the conversion block, confirm the following:
- Hardware platform. S7-300 (CPU 31x), S7-400 (CPU 41x), or ET200S with a CPU that supports STL. For S7-1200/S7-1500, use SCL instead – STL is deprecated on those platforms but the algorithm is identical.
- Firmware. STEP 7 V5.5 or V5.6 for the classic S7-300/400 line, or TIA Portal V16/V17/V18 for S7-1200/1500. Refer to the SIMATIC S7-300 Automation System manual and the SIMATIC S7-1500 Automation System manual for the firmware/OS update matrix.
- Speed feedback source. Either an encoder wired to a fast counter module (FM 350-1/350-2, ET200S 1Count24V) or a PROFIdrive telegram from a SINAMICS drive (telegram 1/2/3/4 with NSOLL and NIST values). The encoder-to-RPM conversion is documented separately below.
-
Data block for constants. A DB block (often named
CONSTorMotor_Data) that holdsRPM_MAX_x10and other commissioning-tunable values. - HMI tag mapping. The percentage output is mapped to a tag displayed as a number with two decimal places on the HMI; the integer value (e.g., 11234) is interpreted by the HMI as 112.34 %.
Mathematical Foundation and Scaling Ranges
The conversion must be dimensionally consistent. Two scaling factors are used in the reference implementation:
| Variable | Scaled Representation | Integer Range (16-bit signed) | Engineering Range |
|---|---|---|---|
| Input RPM | RPM × 10 | -32767 .. +32767 | -3276.7 .. +3276.7 RPM |
| Rated RPM constant | RPMmax × 10 | 0 .. 32767 | 0 .. 3276.7 RPM |
| Output % | % × 100 | -32767 .. +32767 | -327.67 % .. +327.67 % |
Why RPM × 10 and % × 100? Because the smallest meaningful increment on the engineering side is 0.1 RPM (one decimal place on the HMI) and on the operator side is 0.01 % (two decimal places). Multiplying by these factors and rounding once at the end of the calculation preserves both resolutions inside a 16-bit signed word.
The signed range covers reversing drives and over-speed excursions up to ~327 % of rated. Most drives will fault before that, but the code handles bidirectional setpoints correctly without conditional branches.
For a typical 4-pole induction motor on 50 Hz mains, synchronous speed is 1500 RPM and full-load speed (after slip) is ~1468 RPM. Use 14680 as RPM_MAX_x10. For 60 Hz mains, full-load speed is ~1750 RPM and the constant becomes 17500. The motor data sheet or the SINAMICS parameter list (P0310 rated frequency, P0311 rated motor speed) is the authoritative source for these values.
STL Implementation (Reference Solution)
The following STL block is the original reference implementation, placed inside an FC (Function) so that the inputs and outputs are passed as parameters rather than read from absolute addresses. Constants are pulled from a dedicated data block called DB CONST.
// FC 100 — RPM_x10 to Percent_x100
// Input : RPM_x10 (INT) @ MW 100 // e.g., 14680 = 1468.0 RPM
// Output : Pct_x100 (INT) @ MW 104 // e.g., 10000 = 100.00 %
// Constants from DB CONST (DB 200):
// DB_CONST.RPM_MAX_x10 : INT = 14680 // rated RPM x 10
L MW 100 // Load input RPM_x10
ITD // INT -> DINT (sign-extend)
DTR // DINT -> REAL
L DB200.DBD 0 // Load DINT RPM_MAX_x10
DTR // DINT -> REAL
/R // REAL division
L 1.000000e+004 // Load 10000.0
*R // Multiply (result in % x 100)
RND // Round to nearest DINT
T MW 104 // Transfer to output word
SET // Set RLO = 1 (BR bit = 1)
SAVE // Save RLO to BR
Instruction notes (per the STEP 7 STL reference manual):
- L — Loads the operand into ACCU1.
- ITD — Integer (16-bit) to Double Integer (32-bit). Required before DTR to avoid overflow on values that exceed 32767.
- DTR — Double Integer to Real (IEEE 754 single precision). Result is in ACCU1 as a 32-bit float.
- /R — Real division: ACCU2 / ACCU1 → ACCU1.
- *R — Real multiplication: ACCU2 * ACCU1 → ACCU1.
- RND — Round to nearest integer; returns a DINT.
- T — Transfer from ACCU1 to operand. The high word of the DINT result is written first; in S7-300/400 the byte order is big-endian so a 16-bit transfer of ACCU1-L reads the rounded value if the result fits in INT range.
- SAVE — Sets the BR (binary result) bit so the calling block sees a clean ENO.
ITD after L MW guarantees sign-extension to DINT. Skipping ITD and loading a 16-bit value directly into the floating-point path is a common commissioning bug that causes the high RPM half of the range to wrap negative.SCL Equivalent for S7-1200 / S7-1500 (TIA Portal)
The STL code translates almost verbatim into SCL. The syntax differences are the assignment operator (:=), the absence of explicit accumulator manipulation, and the fact that SCL uses REAL natively.
// FB "RPM_to_Pct" — TIA Portal V17 / S7-1500
// Input : i_RPM_x10 : INT // RPM x 10, e.g. 14680 = 1468.0 RPM
// Output : o_Pct_x100 : INT // % x 100, e.g. 10000 = 100.00 %
// Static : s_RPMmax_x10 : INT := 14680; // rated speed (commissioning tunable)
IF s_RPMmax_x10 <> 0 THEN
o_Pct_x100 := REAL_TO_INT(
INT_TO_REAL(i_RPM_x10) * 100.0 / INT_TO_REAL(s_RPMmax_x10) * 100.0
);
ELSE
o_Pct_x100 := 0; // divide-by-zero guard
END_IF;
The REAL_TO_INT function rounds toward zero in SCL by default; if banker's rounding or round-half-up is required, use ROUND or CEIL/FLOOR explicitly. The same divide-by-zero guard is implicit in the STL code (a 0 denominator returns 0 because of the RND after /R with an undefined result; many CPUs substitute INF and the subsequent *R yields INF, which RND rounds to the largest representable value). Adding the explicit guard in SCL is the recommended hardening step.
Ladder Logic Equivalent (FBD/LAD)
Engineers who prefer graphical languages can build the same conversion using the following FBD network. The chain is:
-
MW100→I_DI(INT to DINT) →DI_R(DINT to REAL) - Constant
DB200.DBW0→I_DI→DI_R - Real division block (DIV_R)
- Constant 10000.0 (REAL)
- Real multiplication block (MUL_R)
-
ROUND(REAL to DINT) - Transfer to
MW104viaMOVE(DINT to INT with overflow check)
In LAD, the multiplication by a constant is performed by an *R block with the constant wired to one input. In FBD it is identical. Both languages will execute the network in a single OB1 scan provided that the floating-point math executes within the OB1 priority class — on older S7-300 CPUs (CPU 312/314) this is fine, but on a CPU 312 IFM (limited local data) keep the FC in a separate priority class to avoid OB1 overflow.
Data Block Layout for Commissioning Constants
Centralizing the rated-speed constant in a data block is the difference between a one-line motor swap and a four-hour code hunt. The recommended DB layout:
DATA_BLOCK "Motor_Constants"
STRUCT
RPM_MAX_x10 : INT := 14680; // 1468.0 RPM @ 50 Hz / 4-pole
RPM_MAX_x10_BK : INT := 17500; // backup for 60 Hz / 4-pole (1750 RPM)
PCT_FULL_SCALE : INT := 10000; // 100.00 % — usually left at default
ENC_PPR : INT := 1024; // encoder pulses per revolution
GEAR_RATIO : REAL := 1.0; // gearbox ratio (output / input)
REV_ENABLE : BOOL := TRUE; // permit negative setpoints
END_STRUCT;
END_DATA_BLOCK
The constants can be made HMI-visible so that a maintenance engineer changes a single tag after a motor swap, rather than touching the FC source. Use the DB-only access attribute in the DB properties so that STEP 7 online monitoring shows the values but they remain read/write from the HMI.
Encoder Pulse-to-RPM Conversion
If RPM is not delivered directly by the drive, it must be calculated from encoder pulses. The standard relationship for a quadrature encoder is:
RPM = (fpulses / PPR) × 60 / (4 × G)
Where fpulses is the pulse frequency in Hz, PPR is the pulses per revolution of the encoder, the leading 4 accounts for quadrature decoding (x1, x2, x4), and G is the gearbox ratio. A typical calculation table:
| Target RPM | PPR | Pulses per second (x4 decode) | Pulses per gate time of 100 ms |
|---|---|---|---|
| 1 | 1024 | 68.27 | ~7 |
| 10 | 1024 | 682.7 | ~68 |
| 60 | 1024 | 4096 | ~410 |
| 1500 | 1024 | 102400 | 10240 (overruns 16-bit in 100 ms) |
| 1500 | 1024 | 102400 | use 1 s gate time |
The conversion between RPM and count rate is reciprocal: 1 RPM ↔ PPR / 60 × 4 counts/second. Common shortcuts used in the field:
- 1 RPM = 600 counts for 10 PPR at x4 decode (1 × 10 × 60 = 600)
- 2 RPM = 300 counts
- 5 RPM = 120 counts
- 10 RPM = 60 counts
- 60 RPM = 10 counts
- 600 RPM = 1 count — resolution floor for 10 PPR
For Siemens FM 350-1/350-2 counters, the measured count is loaded directly into a DB word via the assigned load value. The CPU then scales by gate time internally if you use the FM 350 function blocks. For ET200S 1Count24V, the user is responsible for the gate-time logic — a 1 Hz cyclic interrupt (OB35 with 1000 ms period) is the standard approach.
Linearization and Non-Linear Curves
The RPM-to-percentage mapping is linear by definition. What is not always linear is the underlying physics of the actuator:
- PWM duty cycle to fan RPM. Below the motor's natural commutation threshold the fan stalls or runs at a fraction of the duty-cycle-predicted speed. Empirically, a 50 % duty cycle on a small DC fan may produce only ~25-40 % of the rated RPM because the back-EMF dominates the low-end torque. Plot the measured curve and apply a piecewise linear lookup or a polynomial correction in a separate FC.
- Hydraulic proportional valves. Valve spool position is roughly proportional to current, but flow is not proportional to spool position because of orifice area curves. For RPM conversion this rarely matters, but for any speed-derived setpoint downstream of the same analogue output it does.
- V/f drives at low frequency. A SINAMICS V20 in linear V/f mode boosts voltage below 5 Hz to maintain torque. The resulting RPM at 10 % setpoint may be 4 % rather than the 10 % the formula predicts. Switch to sensorless vector control (SLVC) if closed-loop accuracy below 10 % is required.
For these cases, leave the FC as the linear scaling engine and insert a linearization FC between the RPM input and the FC's RPM_x10 input. Keeping the math in one place and the corrections in another is the only sustainable architecture.
Edge Cases, Limits, and Fault Handling
| Condition | Behaviour | Recommended Handling |
|---|---|---|
| RPM_MAX_x10 = 0 | Division by zero; STL yields INF, SCL traps (or returns 0 with guard) | Guard in OB startup; reject zero from HMI commissioning |
| RPM_x10 overflow beyond INT range | Wraps; STL shows as negative | Clamp input or use DINT input throughout |
| RPM_x10 negative (reversing drive coasting) | Result is correctly negative percentage | If reversing not permitted, clamp to 0 before FC call |
| Result beyond INT range (±327.67 %) | Wraps; loses sign | Clamp output to ±32000 (=±320.00 %); raise fault if saturated |
| PLC in STOP during scale change | DB retains old constant; no transition glitch | Acceptable; document in FMEA |
| Encoder wire break | Counter freezes at last value; RPM stays at last reading | Add zero-speed watchdog (RPM must reach 0 within t seconds of stop command) |
| PROFIdrive telegram life-sign fail | SINAMICS sets NIST = 0 with control word bit 13 | Detect via life-sign counter in FB; fault if increments stall |
A robust pattern is to put the FC inside a wrapper FB that performs input clamping, divide-by-zero detection, output saturation, and life-sign monitoring. The wrapper raises a structured fault (S7-300/400 via SFC 36/37 MSG_LOCK / MSG_UNLOCK for diagnostics; S7-1500 via GET_DIAG or the diagnostic buffer) so that the HMI can display a colour-coded alarm.
Verification and Commissioning
Use the following commissioning checklist before sign-off:
-
Static test (motor de-energized, drive disabled). Force
MW100to known values: 0, 1000 (100.0 RPM), 14680 (rated RPM), -14680 (reverse rated). VerifyMW104reads 0, ~681 (6.81 %), 10000 (100.00 %), -10000 (-100.00 %). -
Ramp test. Use the S7-PLCSIM or PLCSIM Advanced simulator to feed a linearly increasing
MW100from 0 to 16383 (1638.3 RPM). Verify the output ramps linearly and saturates near 11000 (111.55 %) without wrap. - Round-trip test. Build the inverse FC (percentage-to-RPM) and chain the two. Apply a known percentage; the resulting RPM_x10 should equal the input RPM_x10 within ±1 LSB.
-
Cross-check against drive actual. With the drive running at a fixed setpoint (e.g., 50 % on the HMI), read
r0021(actual speed smoothed) on the SINAMICS and compare to the PLC'sMW104. Acceptable error: ±0.5 %. -
Motor swap simulation. Change
DB200.DBW0from 14680 to 17500 and verify the operator's 50 % setpoint now commands 875 RPM instead of 734 RPM. This is the principal reason for putting the constant in a DB rather than hard-coding it in the FC. -
Fault injection. Force
DB200.DBW0to 0 and verify the divide-by-zero fault is raised and the HMI displays the configured alarm.
Cross-Platform and Vendor Portability Notes
The formula is universal. The implementation details that change between vendors:
| Platform | Recommended Language | Integer-to-Real Primitive | Round Function |
|---|---|---|---|
| Siemens S7-300/400 STEP 7 | STL, FBD, LAD | ITD + DTR | RND |
| Siemens S7-1200/1500 TIA Portal | SCL, FBD | INT_TO_REAL | REAL_TO_INT or ROUND |
| Allen-Bradley ControlLogix RSLogix 5000 / Studio 5000 | Structured Text | DINT_TO_REAL | REAL_TO_DINT (truncates) or use ROUND instruction from Add-On |
| Allen-Bradley SLC 500 / MicroLogix RSLogix 500 | Ladder | MUL with floating-point file (F8:) | DEG/FRD inverse or CPT block |
| Codesys / Beckhoff TwinCAT 3 | Structured Text | INT_TO_REAL / DINT_TO_REAL | REAL_TO_INT (truncates) or LREAL with explicit ROUND |
| Schneider Modicon M340 Unity Pro | ST, FBD | INT_TO_REAL | REAL_TO_INT (truncates) |
| Mitsubishi GX Works (iQ-R, Q/L) | ST, FBD | INT_TO_REAL / DINT_TO_REAL_E | REAL_TO_INT_E or ROUND_E |
When porting the STL block to Allen-Bradley Structured Text, the equivalent is:
// AOI or routine in Studio 5000
Pct_x100 := REAL_TO_DINT(
(DINT_TO_REAL(RPM_x10) * 100.0) / DINT_TO_REAL(RPMmax_x10) * 100.0
);
Note that Studio 5000's REAL_TO_DINT truncates rather than rounds. For symmetric rounding, use ROUND from the Math Functions Add-On or pre-bias the value: Pct_x100 := REAL_TO_DINT(value + 0.5); for positive-only values, and a separate branch for negatives.
FAQ
Why scale RPM by 10 and percentage by 100 instead of using floating point throughout?
Legacy S7-300 CPUs (CPU 312, 314, 315) have limited floating-point performance — a DTR plus /R plus *R chain takes roughly 30-50 µs of OB1 time. By staying in 16-bit integer I/O and limiting the floating-point chain to one pass per scan, the FC fits inside OB1 of a CPU 314. Newer S7-1500 CPUs have hardware FPUs and the scaling factor is purely a question of fieldbus bandwidth and HMI resolution.
How do I handle a 4-quadrant drive with negative setpoints?
The STL block already handles negative values correctly because both ITD and the subsequent float math preserve sign. Confirm that RPM_MAX_x10 is stored as a positive constant and that the HMI allows negative tag values. If the operator should not be able to command reverse, clamp the input MW100 to zero in a wrapper FB before calling the FC.
The drive reports 1472 RPM but the HMI shows 100.27 % instead of 100.00 %. Why?
This is rounding noise from the constant choice. With RPM_MAX_x10 = 14680 (rated 1468 RPM) and a measured 1472 RPM, the raw ratio is 100.27 %. The difference is the slip margin between the motor's rated point and its actual operating point. Either widen the rated-speed definition to the motor's full-load slip speed, or display the ratio on the HMI with a tolerance band ("within ±1 %").
Can I use this block with a single-phase drive or a stepper motor?
Yes. The block is purely arithmetic; the underlying motor topology does not affect the formula. For a stepper, replace the encoder feedback with the step rate (steps per second) divided by steps per revolution, then convert to RPM with the same × 60 factor. For a single-phase AC drive on 50 Hz, the maximum synchronous RPM is 3000 (two-pole); set RPM_MAX_x10 to the rated full-load value — typically ~2880 RPM, so RPM_MAX_x10 = 28800.
What happens if I forget the ITD before DTR?
The accumulator holds a 16-bit INT loaded into the low word of ACCU1. Without ITD, the DTR interprets only the low word as a DINT, leaving the high word undefined. For positive values the result is usually correct by coincidence, but for negative values the high word contains the previous ACCU content (or stack residue) and the float becomes garbage. Always issue ITD explicitly after L MW.
Is there a Siemens standard FC for this conversion?
Siemens ships Scale and Unscale function blocks in the Standard Library (TI-S7 Converting Blocks / Standard Library → TI-S7 → FC105 SCALE and FC106 UNSCALE for analogue inputs, and the IEC blocks NORM_X / SCALE_X in TIA Portal). Those blocks handle the 0-27648 analogue raw count to engineering unit conversion. They are not a direct substitute for the RPM-to-percentage conversion because the latter requires a user-defined RPM_MAX_x10 denominator that the standard blocks do not expose, but the pattern is identical.