CTRL_HSC_EXT SCL Export: TIA Portal V14 to V13 RPM Calculation

David Krause13 min read
SiemensTIA PortalTutorial / 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

Exporting CTRL_HSC_EXT SCL Code from TIA Portal V14 to V13 with RPM Calculation

Engineers maintaining S7-1200/S7-1500 fleets that span multiple TIA Portal versions routinely hit a binary compatibility wall: a project saved in V14 cannot be opened in V13 SP1. When the offending block is a single FB in SCL that wraps the high-speed counter instruction CTRL_HSC_EXT, the cleanest path is to export the source text, recompile it in the older environment, and fix the small set of implicit-conversion issues that the older compiler reports. This reference walks through that procedure, then deepens it with the period-measurement math required to derive RPM from a quadrature or pulse encoder.

Scope. The block referenced is the Siemens example "CTRL_HSC_EXT — Example 1: Measurement of rotational speed and direction" published on the Siemens Industry Online Support portal as entry 109742346. That example, including its numPulsePerRot calculation, is the basis for the code shown below.

1. What CTRL_HSC_EXT Does

CTRL_HSC_EXT is the SCL/ST/FBD wrapper around the S7-1200/S7-1500 high-speed counter (HSC) technology object. It complements CTRL_HSC by exposing the extended parameter set of the Period measurement and Edge-to-edge measurement operating modes introduced with the S7-1500 and back-ported to S7-1200 firmware V4.x and later.

Parameter Direction Data type Purpose
HSC IN HW_HSC Hardware identifier of the HSC technology object (assigned in Device Configuration)
DIR IN BOOL Count direction (TRUE = reverse)
CV IN BOOL Enable new counter value via NEW_CV
RV IN BOOL Enable reference value via NEW_RV
PERIOD IN BOOL Enable period measurement (period / edge-to-edge)
NEW_PERIOD IN/OUT DINT New measurement window / period mode selector
ELAPSED_TIME OUT DINT Accumulated time of the last measurement window in nanoseconds
EDGE_COUNT OUT DINT Number of edges captured during the window
STATUS OUT WORD Function result / error code

The combination of ELAPSED_TIME and EDGE_COUNT is what enables period-based speed measurement. The HSC hardware latches the elapsed time between a configurable number of edges and reports both values through the function block interface, letting the SCL code calculate RPM in software rather than counting pulses over a PLC cycle.

2. The V14-to-V13 Compatibility Problem

TIA Portal project files are forward-only: a V13 SP1/SP2 project opens in V14, but a V14 project refuses to open in V13. The compiler error is typically "The project was created with a newer version of TIA Portal and cannot be opened." The reverse workflow — exporting a single block as plain SCL source — is supported, and that is what we exploit here.

TIA Portal version Can open V13 project? Can open V14 project? Compiles CTRL_HSC_EXT?
V13 SP1 / SP2 Yes No Yes (HSC ≥ V4.0)
V14 Yes Yes Yes
V14 SP1 Yes Yes Yes
V15 / V15.1 / V16 / V17 / V18 Yes (backward read) Yes (backward read) Yes (firmware dependent)

Because the CTRL_HSC_EXT instruction itself exists in both V13 SP1 (with S7-1200 CPU firmware ≥ V4.0) and V14, the only thing that prevents reuse is the project file format, not the language element.

3. Prerequisites

  1. Source project in TIA Portal V14 (or later) containing the example CTRL_HSC_EXT FB.
  2. Target environment: TIA Portal V13 SP1 (Update 4 minimum) or V13 SP2 with an S7-1200 CPU that supports the HSC technology object (CPU 1211C/1212C/1214C/1215C/1217C, firmware V4.0 or newer) or any S7-1500 CPU.
  3. HSC technology object configured in Device Configuration: Counting mode = Period measurement or Edge-to-edge measurement; Input = onboard HSC input (e.g., I0.0 / I0.1 for HSC1 on CPU 1214C).
  4. Encoder wired to the HSC input with the correct Encoder type (A/B quadrature, A/B quadrature four-fold, or single-channel pulse) selected in the technology object.
  5. OB1 (or cyclic OB) with the SCL FB called every cycle, plus the instance DB for the FB.

4. Step-by-Step: Export the SCL Block from V14

  1. In the V14 project tree, expand Program blocks and select the FB that contains the CTRL_HSC_EXT call (default name in the Siemens example: CTRL_HSC_EXT_RPM).
  2. Right-click the block and choose Export block > Export block to external file… (or, for the source text only, Source files > Generate source from blocks).
  3. Save the resulting .scl (or .db for instance DBs) text file to a folder accessible from the V13 environment.
  4. Optionally right-click the Technology objects > HSC_1 entry and choose Export if you also need to share the technology object configuration; a plain SCL export does not include the technology object.

5. Step-by-Step: Import the SCL Block into V13

  1. Open the target project in TIA Portal V13 SP1/SP2.
  2. Right-click the CPU under Program blocks and choose Add new block > Function block with language SCL. Give it the same name and number as the source FB.
  3. Open the new (empty) FB, click in the code editor, then drag-and-drop the exported .scl file into the editor — or use External source files > Add new external file and compile.
  4. Recreate the In, Out, InOut, Static, and Temp interface sections to match the source. The V13 compiler will not infer them from pasted code.
  5. Place an instance DB for the FB (right-click > Add new block > Data block > Instance DB > select the FB).
  6. Call the FB from OB1 and download hardware + software to the CPU.
Watch the V13 compiler. The SCL grammar tightened between V13 and V14. Implicit numeric conversions (DINT → REAL, INT → LREAL) that compile silently in V14 will trigger "Cannot implicitly convert type 'DINT' to 'REAL'" in V13. The fix is an explicit conversion function; see Section 7.

6. The Reference SCL Block

The Siemens example exports a single SCL FB named CTRL_HSC_EXT_RPM. The interface and the relevant calculation logic are reproduced below. Comments mark the lines that must be edited when porting from V14 to V13.

6.1 Block Interface

FUNCTION_BLOCK "CTRL_HSC_EXT_RPM"
{ S7_Optimized_Access := 'FALSE' }
VERSION : 0.1
   VAR_INPUT
      startHSC         : BOOL;   // TRUE = arm period measurement
      numPulsePerRot   : REAL;   // encoder pulses per mechanical revolution
      tCycle           : REAL;   // optional measurement window [s]; 0.0 = HSC default
   END_VAR

   VAR_OUTPUT
      actualRPM        : REAL;   // rotational speed [1/min]
      actualPeriod_s   : REAL;   // last measured period [s]
      statStatus       : WORD;   // HSC status word
      statError        : BOOL;   // TRUE on STATUS <> 0
   END_VAR

   VAR
      statHscId         : HW_HSC;   // assigned in HW configuration, e.g. %I0.0
      statEnablePeriod  : BOOL;
      statNewPeriod     : DINT;
      statElapsedTime   : DINT;     // [ns]
      statEdgeCount     : DINT;
      statPeriod        : REAL;
      statRPM           : REAL;
   END_VAR

   CONST
      BILLION : REAL := 1.0e9;
   END_CONST
BEGIN
   // --- 1. Drive the HSC extended control ----------------------------
   #statEnablePeriod := #startHSC;
   #statNewPeriod    := REAL_TO_DINT(#tCycle * 1.0E9);   // window [ns]

   "CTRL_HSC_EXT_DB".CTRL_HSC_EXT_Instance(  // (use the IEC instance syntax if optimized)
      HSC           := #statHscId,
      PERIOD        := #statEnablePeriod,
      NEW_PERIOD    := #statNewPeriod,
      ELAPSED_TIME  := #statElapsedTime,
      EDGE_COUNT    := #statEdgeCount,
      STATUS        := #statStatus );

   // --- 2. Convert elapsed ns to seconds and divide by edges ---------
   IF #statEdgeCount > 0 THEN
      //  Port-fix: explicit DINT_TO_REAL for V13 compilation.
      #statPeriod := DINT_TO_REAL(#statElapsedTime)
                   / DINT_TO_REAL(#statEdgeCount)
                   / #BILLION;                       // [s / edge]

      // --- 3. Apply pulses-per-revolution to get period [s/rev] ------
      IF #numPulsePerRot > 0.0 THEN
         #statPeriod := #statPeriod * #numPulsePerRot;

         // --- 4. Convert period [s/rev] to RPM [1/min] --------------
         IF #statPeriod > 0.0 THEN
            #statRPM := 60.0 / #statPeriod;
         ELSE
            #statRPM := 0.0;
         END_IF;
      ELSE
         #statRPM := 0.0;
      END_IF;
   ELSE
      #statRPM := 0.0;
   END_IF;

   #actualRPM      := #statRPM;
   #actualPeriod_s := #statPeriod;
   #statError      := (#statStatus <> 16#0000);
END_FUNCTION_BLOCK

6.2 What numPulsePerRot Means

numPulsePerRot is the rated pulse count of the encoder per mechanical revolution, not the number of edges the HSC hardware sees. The relationship between encoder PPR and HSC edges is set by the technology object's Counting mode and Signal evaluation:

Encoder signal Signal evaluation Edges per revolution seen by HSC numPulsePerRot to use
Single-channel pulse 1× 1 × PPR Encoder PPR
A/B quadrature 1× 2 × PPR Encoder PPR × 2
A/B quadrature 2× (two-fold) 4 × PPR Encoder PPR × 4
A/B quadrature 4× (four-fold) 4 × PPR Encoder PPR × 4

A common field error is to set numPulsePerRot to the catalog PPR of the encoder (e.g. 1024) while the HSC is in 4× quadrature evaluation, so the HSC actually counts 4096 edges per revolution. The result is an RPM reading that is low by a factor of four. The fix is to feed the FB the effective PPR — i.e. encoder PPR multiplied by the configured edge count — and leave the technology object on its current setting. An alternative, used in the Siemens example, is to feed encoder PPR and compensate inside the FB by the same factor the HSC is using; the example keeps the factor explicit so that reconfiguration of the technology object does not silently change the result.

7. The DINT-to-REAL Conversion Fix

The Siemens example as published in entry 109742346 performs the division #statElapsedTime / #edgeCount directly. In V14, the compiler accepts the implicit promotion of DINT to REAL because #statPeriod is REAL. In V13 SP1, the same line fails to compile with:

Line 26:  Cannot implicitly convert type 'DINT' to 'REAL'.

Two equivalent fixes are valid:

7.1 Option A — Wrap with explicit conversions

#statPeriod := DINT_TO_REAL(#statElapsedTime)
            / DINT_TO_REAL(#statEdgeCount)
            / 1.0E9;

This is the safest port-fix because it does not change the data types of the interface and works on both V13 and V14. DINT_TO_REAL is a standard IEC conversion function available in all TIA Portal versions from V11 onward.

7.2 Option B — Promote the temporaries to REAL

VAR_TEMP
    tElapsedReal : REAL;
    tEdgeReal    : REAL;
END_VAR

tElapsedReal := DINT_TO_REAL(#statElapsedTime);
tEdgeReal    := DINT_TO_REAL(#statEdgeCount);
#statPeriod  := tElapsedReal / tEdgeReal / 1.0E9;

Use this form when the rest of the block is going to be refactored for clarity anyway. It avoids long expressions that exceed the line-length print margin in V13.

Watch the constant. The literal 1.0E9 is REAL; 1E9 (no decimal) is interpreted as a time literal and will not compile inside a numeric expression. Always use the decimal form for the BILLION divisor.

8. Verification Procedure

  1. Online compile the V13 project and download hardware and software to the CPU in STOP, then run it.
  2. Open the instance DB online and watch the statElapsedTime, statEdgeCount, statPeriod, and statRPM values with a VAT.
  3. Confirm that statStatus returns 16#0000 (no error). The most common non-zero status codes are 16#80A0 (HSC not configured) and 16#80A1 (invalid NEW_PERIOD).
  4. Drive the encoder at a known reference speed (a stroboscope or a second tachometer is ideal) and compare the FB's actualRPM to the reference. Tolerate a few percent, since the HSC's period window quantises the result.
  5. Stop the encoder (0 RPM). Confirm that actualRPM drops to 0.0 within one period window, not at a frozen non-zero value. A frozen value usually means statEdgeCount stayed at its last value and the new period measurement never started; check that PERIOD is being re-asserted each cycle or that the technology object is set to Continuous measurement.
  6. Reverse the encoder direction. Confirm that actualRPM goes negative. The HSC counts up only; direction is signalled by the B-channel phase. If the sign is wrong, swap the A and B encoder wires (do not swap power and ground).

9. Common Faults and Remedies

Symptom Likely cause Remedy
Compile error "Cannot implicitly convert type 'DINT' to 'REAL'" V13 stricter type checking Wrap with DINT_TO_REAL() (Section 7)
actualRPM stuck at 0 with HSC in RUN PERIOD input is one-shot, not held Hold #statEnablePeriod := TRUE in the VAR section; re-assign each cycle
actualRPM low by factor of 2 or 4 Mismatch between numPulsePerRot and HSC signal evaluation Use effective PPR (encoder PPR × evaluation factor)
statStatus = 16#80A0 HSC technology object not downloaded or wrong HW ID Recompile hardware; verify statHscId in Device Configuration
statStatus = 16#80A1 NEW_PERIOD out of range Check the technology object's Min/Max period values; NEW_PERIOD in nanoseconds must lie inside
actualRPM noisy at low speeds Measurement window too short for the speed Increase tCycle (the period window) so more edges are integrated
Direction sign inverted A/B encoder wiring reversed Swap the two signal wires at the encoder side, not the supply
No HSC interrupts in V13 SP1 CPU firmware < V4.0 Update S7-1200 CPU to firmware V4.0 or later; HSC extended parameters require it

10. Period Measurement Math in One Screen

The block derives RPM in four algebraic steps. Engineers reading the code in a hurry will want the formulas spelled out:

        tElapsed_ns                    // raw HSC output: time of window
f_s    = -------------                 // period in seconds PER EDGE
        N_edges * 1.0E9

T_rev  = f_s * PPR_eff                // period per revolution [s]

RPM    = 60 / T_rev                   // revolutions per minute [1/min]

Where PPR_eff = encoder PPR × signal-evaluation factor (1, 2, or 4). A worked example: a 1024-PPR encoder in 4× quadrature (PPR_eff = 4096) turning at 1500 RPM:

  • Period per revolution: 60 / 1500 = 0.040 s
  • Period per edge: 0.040 / 4096 = 9.766 µs/edge
  • Period window needed for 10 edges: 97.66 µs — well within the HSC's sub-microsecond resolution.

At very low speeds (long T_rev) the period window itself becomes the bottleneck; the FB then needs to be re-engineered to use a fixed wall-clock window and count edges, which is the inverse of the algorithm above.

11. Differences from the V14 Original

Aside from the DINT_TO_REAL() wrappers, the ported block is functionally identical to the V14 example. The single material difference is the absence of optimised access on the instance DB in V13 SP1 — the block above uses the default (non-optimised) interface so the instance DB appears in the V13 watch table. If you want optimised access in V13 SP1, mark the FB with { S7_Optimized_Access := 'TRUE' } and accept that the instance DB no longer has symbolic slot addresses.

The V14 example also uses know-how protection in some shipping projects. The Generate source from blocks export strips that protection. The Export block to external file export preserves it. Choose the export mode that matches the project IP policy before sending the file to a V13 site.

12. FAQ

Why will TIA Portal V13 not open a V14 project file?

TIA Portal uses a forward-only project schema. V14 introduced new block types, technology object revisions, and compiler metadata that the V13 schema cannot read. The fix is to export the individual SCL source files (Section 4) and re-import them into a V13 project; the project schema is the only thing that changed, not the SCL language itself.

What does CTRL_HSC_EXT add beyond CTRL_HSC?

CTRL_HSC_EXT extends the high-speed counter with the PERIOD, NEW_PERIOD, ELAPSED_TIME, and EDGE_COUNT parameters. These allow the HSC hardware to perform period or edge-to-edge measurement natively, which is essential for accurate RPM/period calculations on slow-turning shafts where pulse counting over a PLC scan is too coarse.

How do I fix the DINT to REAL conversion error in V13?

Wrap the operands with the IEC conversion function: statPeriod := DINT_TO_REAL(#statElapsedTime) / DINT_TO_REAL(#statEdgeCount) / 1.0E9;. V13 SCL will not promote DINT to REAL implicitly the way V14 does. See Section 7 for the two equivalent fix patterns.

What value should I assign to numPulsePerRot?

Assign the effective pulses-per-revolution seen by the HSC hardware, not the encoder's catalog PPR. Multiply the encoder's rated PPR by the HSC signal-evaluation factor (1, 2, or 4) configured in the technology object. For a 1024-PPR encoder in 4× quadrature the value is 4096.

Why is the RPM reading low or noisy at low speeds?

At low speeds the period per edge becomes large and the HSC's internal period window can quantise the result. Increase the measurement window (the tCycle input, mapped to NEW_PERIOD in nanoseconds) so that more edges are integrated per window. If the period exceeds the technology object's maximum allowed NEW_PERIOD, switch the FB to the inverse algorithm: a fixed wall-clock window and a count of edges.

Back to blog