Problem Statement: Why the Built-In 1-Second Period Is Not Enough
The S7-1200 high-speed counter (HSC) provides a built-in frequency measurement mode with a configurable averaging window. On firmware V4.0 and later, the integrated frequency function is limited to a maximum averaging period of 1.0 s. At low pulse rates - for example, a proximity-switch pulse train producing 15 Hz to 18 Hz - the absolute quantization error of a 1 s window becomes the dominant contributor to the final value. Each whole pulse during the integration window equals 1 Hz, so the displayed value steps in integer-Hz increments (15, 16, 17, 18 Hz) with no intermediate resolution (15.4, 16.8, 17.2 Hz).
The native HSC frequency mode is convenient for stable, high-rate signals, but it is not a general-purpose tachometer. When the source is a single-channel proximity sensor whose mark-space ratio varies with speed, the only practical way to improve resolution is to lengthen the observation window beyond 1 s and derive frequency by division. The S7-1200 HSC supports this approach natively: configure the HSC as a simple counter, read the cumulative count in a time-triggered cyclic interrupt OB, and compute frequency as the count delta divided by the OB period. The complete S7-1200 system manual is available on the Siemens Industry Online Support portal.
This guide walks through the full implementation in TIA Portal V17/V18/V19/V20/V21: HSC hardware configuration, the cyclic interrupt OB, the CTRL_HSC_EXT instruction, the count-delta calculation, and field-verification steps. The maximum recommended OB phase time is 60 000 ms (60 s), which gives 0.0167 Hz per-count resolution at 60 s, or 0.1 Hz per-count resolution at 10 s.
Prerequisites
Before starting, confirm the following items are available:
- Siemens S7-1200 CPU, firmware V4.0 or later. HSC functionality is part of the standard instruction set from V4.0; CTRL_HSC_EXT is documented in the TIA Portal V21 help system.
- TIA Portal V16 or later (V17-V21 recommended for the latest CTRL_HSC_EXT help text and device firmware catalog).
- An unused HSC-capable digital input. On a CPU 1214C DC/DC/DC, the supported HSC inputs are:
- HSC1: I0.0 (A) / I0.1 (B), with optional I0.3 (Z) and I0.4 (gate/reset)
- HSC2: I0.2 (A) / I0.3 (B), with optional I0.5 (Z) and I0.6 (gate/reset)
- HSC3: I0.4 (A) / I0.5 (B), with optional I0.7 (Z) and I1.0 (gate/reset)
- HSC4: I1.0 (A) / I1.1 (B), with optional I1.2 (Z) and I1.3 (gate/reset)
- HSC5: I1.3 (A) / I1.4 (B), with optional I1.5 (Z) and I1.6 (gate/reset)
- HSC6: I1.5 (A) / I1.6 (B), with optional I1.7 (Z) and I2.0 (gate/reset)
- A 3-wire PNP (sourcing) proximity sensor wired to the HSC input. The 24 V sensor supply can come from the CPU's 24 VDC sensor power output or an external power supply, provided the input common (1M) is referenced correctly.
- Access to the TIA Portal project for online download, watch-table use, and trace recording.
Feature Comparison: Native Frequency vs Counter + Cyclic Interrupt
| Attribute | Built-in Frequency Mode | Counter + Cyclic OB |
|---|---|---|
| Max window | 1.0 s (firmware limit) | 60 000 ms (60 s) |
| Resolution at 10 Hz input | 1.0 Hz (10% FS) | 0.1 Hz at 10 s (1% FS), 0.0167 Hz at 60 s |
| Update latency | Window length + 1 scan | OB phase time + 1 scan |
| Code complexity | None (parameter only) | 1 OB + delta math |
| Affected by mark-space ratio | Yes (high impact) | No (edge count is independent of duty) |
| Sub-Hz intermediate values | No | Yes |
| Count wrap handling required | No | Yes (32-bit signed, ~24.8 days at 1 kHz) |
HSC Hardware Configuration in TIA Portal
Open the device view, select the CPU, and navigate to Properties > High-speed counters (HSC). Select the HSC you intend to use (for this example, HSC1). Enable the channel and configure it as follows:
- Function: Enable "User-defined". This unlocks the counter sub-mode selection.
- Count mode: Select "Count". Do not select "Frequency measurement"; that mode hard-limits the integration window to 1.0 s and is not appropriate for this application.
- Count direction: "Single phase, count up" is sufficient for a one-channel pulse source. The HSC counts each rising edge of I0.0 and the count value is latched into the input process image at ID1000.
- Initial count value: Set to 0. The count is signed (DInt) and will wrap at +2 147 483 647 down to -2 147 483 648; for a 10 s update at 1 kHz this gives 9.3 days of headroom before wrap.
- Input filter: For a proximity sensor with a 50/50 mark-space at slow speeds, the default 6.4 ms filter is too aggressive and will swallow low-rate pulses. Use the digital-input filter group setting (Device configuration > Digital inputs) to set the HSC input group to the minimum filter time the application can tolerate - typically 0.8 us or 3.2 us for HSC inputs. This is set per input-point group, not per HSC.
- Hardware interrupt on count value: Leave disabled; the cyclic interrupt OB handles timing.
The HSC instance data block is created automatically when you place the CTRL_HSC_EXT instruction. The instance DB contains the configuration mirror and runtime values - in particular, the "New count value" (ID1000 for HSC1) which is the live cumulative count read directly by the OB. The detailed block interface is documented in the CTRL_HSC_EXT TIA Portal V21 help.
Configuring the HSC in Counter Mode
The HSC in count mode is purely incremental; the PLC does no math on the pulses. The only configuration the HSC needs is the initial value, the count direction, and the optional gate/reset/Capture inputs. In the device configuration you can also set a hardware interrupt threshold, but for frequency measurement by time-window division you do not need that.
For this example, select "Single phase, count up" with no reset and no gate. If your application ever needs to latch the count on an external event, enable the Capture input (I0.3 for HSC1) and use the CTRL_HSC_EXT EnCapture parameter to arm it.
| Parameter | Value | Notes |
|---|---|---|
| HSC instance | HSC1 | Hardwired to %ID1000 / %ID1002 |
| Input A | I0.0 | Rising-edge count |
| Initial value | 0 | DInt signed |
| Count direction | Count up | Single phase |
| Capture input | Disabled | Not required for this task |
| Reset input | Disabled | Not required |
| Digital input filter | 0.8 us (HSC group) | Set in DI filter group, not in HSC block |
Compile the hardware configuration and download it to the CPU. The HSC now counts every rising edge of I0.0 into the cumulative register at %ID1000, but the PLC does not do anything with that value until you add program code.
Creating a Cyclic Interrupt OB
The cyclic interrupt OB (OB30-OB38, depending on priority and time base) is the time base for the frequency calculation. Each time the OB fires, you read the HSC count, compute the delta from the previous reading, and divide by the OB period to get Hz.
To create the OB, in the project tree right-click Program blocks > Add new block > Organization block. Choose "Cyclic interrupt" (German: "ZeitOB"). Select OB30 (the lowest priority, time base 5 ms by default). The S7-1200 supports OB30 through OB38 with selectable phase times from 1 ms to 60 000 ms (60 s). For a 10 s frequency window, set the OB30 phase time to 10 000 ms.
Inside OB30, instantiate the following:
- A single instance of
CTRL_HSC_EXT(or the olderCTRL_HSCif using firmware < V4.2). The extended instruction exposes theEnCaptureandEnSyncinputs that the basic instruction does not have. The full interface is in the TIA Portal V21 help page. - A static DInt tag for
LastCount(the previous OB entry's count value). - A static DInt tag for
PulsesPerPeriod(the count delta). - A static Real tag for
FrequencyHz. - A constant Real for the OB period (e.g., 10.0 s).
CTRL_HSC_EXT Instruction Parameters
The CTRL_HSC_EXT instruction controls the HSC instance and provides optional capture, sync, and direction-change functions. For the frequency-by-division application, only a subset of inputs is used:
| Parameter | Direction | Type | Purpose | Used in this design |
|---|---|---|---|---|
| HSC | IN | HW_HSC | HSC hardware identifier (e.g., HSC1) | Yes |
| DIR | IN | Bool | Count direction (FALSE = up, TRUE = down). Ignored if hardware direction input is used. | FALSE |
| CV | IN | Bool | Enable "NewCountValue" preset | FALSE |
| NewCountValue | IN | DInt | New count value to load when CV rises | 0 (only used for preset) |
| RV | IN | Bool | Enable "NewReferenceValue" preset for high limit | FALSE |
| NewReferenceValue | IN | DInt | New reference (high) value | n/a |
| PERIOD | IN | Bool | Enable "NewPeriod" preset for frequency mode | FALSE |
| EnCapture | IN | Bool | Activate the Capture input | FALSE |
| EnSync | IN | Bool | Activate the Sync input | FALSE |
| Busy | OUT | Bool | TRUE while the instruction is processing | Read-only |
| Status | OUT | Word | 0 = OK; non-zero = error (see Status table) | Read-only |
The most important output of the HSC is the live count value, which is read from the input process image at %ID1000 (for HSC1). This is the "NewCountValue" register latched every PLC scan and is the source of the pulse count for the frequency calculation.
Pulse Counting and Frequency Calculation Code
The full OB30 logic in SCL (Structured Control Language) for a 10 s window:
// SCL example for OB30, 10-second window
// Tags declared in the OB's static section
// Read the current HSC1 cumulative count
CurrentCount := "HSC_1".Count; // Symbolic name, or use %ID1000
// Compute pulse delta over this window
PulsesPerPeriod := CurrentCount - LastCount;
LastCount := CurrentCount;
// Compute frequency in Hz (REAL arithmetic)
FrequencyHz := INT_TO_REAL(PulsesPerPeriod) / 10.0;
// Call the HSC control block with all disables false
"iHSC_Ctrl"(
HSC := 1,
DIR := FALSE,
CV := FALSE,
NewCountValue := 0,
RV := FALSE,
NewReferenceValue := 0,
PERIOD := FALSE,
EnCapture := FALSE,
EnSync := FALSE,
Busy => iHSC_Busy,
Status => iHSC_Status);
The CurrentCount tag is read from the HSC's instance DB. In TIA Portal V17+, you can either use the symbolic instance name (e.g., HSC_1.Count) or read directly from the input process image at %ID1000. Both methods return the same DInt value; the symbolic path is preferred for program readability. The arithmetic must always be done in REAL; DInt division truncates and loses sub-Hz resolution. The general formula is:
f_Hz = (N_current - N_previous) / T_windowwhere N is the cumulative count and T_window is the OB phase time in seconds. For a 10 s window the per-count resolution is 0.1 Hz; for a 60 s window, 0.0167 Hz. The trade-off is update latency: a 60 s window means the displayed frequency lags the real value by up to 60 s. Choose the window to match the process dynamics, not the sensor noise.
ID1000 New Count Value and Edge Cases
The address %ID1000 is the input process image double-word that holds the current HSC1 count. The HSC hardware updates this register on every PLC scan regardless of how often the cyclic OB fires, so the value seen in OB30 is the count as of the start of that OB invocation.
Two edge cases to handle in production code:
- PLC scan longer than OB period. If the OB30 phase time is set below the longest OB1 scan, you can miss OB invocations. Watch for OB30 phase overrun (SF LED + diagnostic buffer entry "OB30 phase error") and either increase the OB phase time or shorten OB1.
- CPU STOP during a window. If the CPU stops mid-window, the HSC may keep counting (CPU 1214C keeps the HSC clock running in STOP) or may freeze (some older firmware). Test the actual behaviour in your revision. If the HSC keeps counting in STOP, the count is correct after the next RUN; if it freezes, the first delta after restart will be wrong - apply the startup initialisation described below.
For higher accuracy, you can also use the HSC's hardware interrupt on count value (configured in the device properties) to trigger an OB40 hardware interrupt, and time the interval between two events with the system clock (read RD_SYS_T into a DTL, subtract). This gives a frequency resolution limited only by the source jitter and the CPU's cycle time, at the cost of more code and a careful diagnostic on overflow.
Startup Initialisation in OB100
To prevent a wrong first-window reading, force the OB30 static variables to a known state in the warm-restart OB:
// OB100 - Warm restart
"instOB30".LastCount := 0;
"instOB30".PulsesPerPeriod := 0;
"instOB30".FrequencyHz := 0.0;
"instOB30".FirstCycleDone := FALSE;
Then in OB30, gate the math behind FirstCycleDone:
IF NOT "instOB30".FirstCycleDone THEN
"instOB30".LastCount := CurrentCount;
"instOB30".FirstCycleDone := TRUE;
RETURN; // discard first delta
END_IF;
This ensures the first displayed frequency is the second-window value, not a delta that includes pulses from before OB30 was scheduled.
Verification and Commissioning Checks
Before relying on the frequency value in closed-loop control or for trips, perform these field checks:
- Pulse-train verification. Connect an oscilloscope to the HSC input terminal and confirm the pulse shape. The proximity-switch output should swing rail-to-rail (24 V high, 0 V low) with a clean edge. A slow rise time indicates a long cable or a 2-wire sensor; add a pull-down resistor or use a different input filter setting.
- Counter verification. With a known pulse source (a function generator set to 50 Hz, 50% duty, 24 V amplitude), set the OB30 phase time to 10 s and confirm the displayed frequency is 50.0 Hz within +/- 0.1 Hz. If the value is low, the input filter is suppressing edges; if it is high, the sensor is bouncing.
- OB phase time. Use a watch table to read the OB30 phase time. If you ever see "OB30 phase error" in the diagnostic buffer, the OB is overrun and your frequency is invalid.
-
Tag retention. Verify that the
LastCountandFrequencyHztags are in the OB's static section, not in the M (memory) area. M area can be reset on power cycle; OB static is retained across RUN-to-STOP-to-RUN. - Long-term drift. Run the system for at least one full OB period and watch the frequency in the watch table. Sub-Hz stability over multiple windows confirms the system is clean.
Troubleshooting Matrix
| Symptom | Likely cause | Corrective action |
|---|---|---|
| Frequency reads 0 with pulses present | Input filter is set too high, swallowing edges | Set the HSC digital-input group filter to 0.8 us |
| Frequency reads low by a fixed ratio | Sensor is NPN (sinking) but HSC input expects PNP | Wire to a PNP sensor or use a signal inverter |
| Frequency jumps in step changes | OB30 phase time too short for the pulse rate | Increase OB30 phase time to 10 s or more |
| Frequency reads double the expected value | Counting both rising and falling edges | Confirm HSC mode is "Count up" on rising edge only, not "Count both edges" |
| Diagnostic buffer: "OB30 phase error" | OB1 scan time exceeds OB30 phase time | Increase phase time to at least 2x the worst OB1 scan |
| Frequency negative | HSC configured "Count down" or initial value non-zero | Set count direction to "Count up" and initial value to 0 |
| Frequency value frozen | OB30 not being called (priority blocked by other OBs) | Check priority of OB30 vs OB1, OB80, OB82, etc. |
| Status word non-zero | See CTRL_HSC_EXT help for status codes | Verify HSC instance matches the hardware configuration |
| First reading wildly wrong | First-cycle delta includes pre-startup pulses | Add OB100 initialisation and gate first cycle in OB30 |
| Frequency reads slightly high | Sensor contact bounce on rising edge | Increase DI filter to 3.2 us for HSC group, or add hardware RC |
Field-Proven Caveats
A few practical notes observed in real installations:
- Two-wire proximity sensors. A 2-wire (leakage-current) sensor draws 1-2 mA even when "off". This can be enough to keep the HSC input high and count zero edges. Use a 3-wire PNP sensor with a low-leakage specification or add a shunt resistor to pull the input below the OFF threshold.
- Shielded cable. For long cable runs (over 5 m), use a shielded twisted pair with the shield grounded at one end only, and route away from VFD and motor power cables. Induced noise on the HSC input produces extra counts and a falsely high frequency.
- Process image partition. If you use the HSC count in a PROFIBUS or PROFINET cyclic data block, copy it to a non-HSC process image partition first. Some older firmware revisions updated %ID1000 only in the standard process image, leading to stale data on the bus.
- Frequency mode on a 1217C. The CPU 1217C supports higher HSC rates, but the 1.0 s period limit on the built-in frequency mode is firmware-imposed, not hardware-imposed. The same counter-mode + cyclic interrupt approach works identically on 1217C.
-
Sub-Hz output smoothing. If 0.1 Hz per-pulse resolution is still too coarse, low-pass-filter the
FrequencyHztag with a first-order lag (tau = 3-5 s) before using it for control. Pure raw division will always have +/- 1-count jitter at the boundary of the window.
What is the maximum HSC frequency-measurement window on the S7-1200?
The built-in frequency mode is capped at 1.0 s by firmware. To go beyond 1 s, switch the HSC to counter mode and read the count in a cyclic interrupt OB (OB30-OB38). The maximum OB phase time is 60 000 ms (60 s), giving 0.0167 Hz per-count resolution.
Which PLC tag holds the live HSC1 count value?
The HSC hardware writes the cumulative count into the input process image at %ID1000 on every PLC scan. You can read it symbolically from the HSC instance DB (HSC_1.Count) or directly from %ID1000. The same pattern applies: HSC2=%ID1004, HSC3=%ID1008, HSC4=%ID1012, HSC5=%ID1016, HSC6=%ID1020 on a CPU 1214C.
Why does the first OB30 frequency reading look wrong?
Because the first delta (CurrentCount - LastCount) is computed against an uninitialised or zero LastCount, so it includes every pulse that arrived before OB30 started. Fix it by initialising the OB30 static tags in OB100 and discarding the first delta in OB30 with a FirstCycleDone flag.
Can I count both rising and falling edges for higher resolution?
Yes - select the HSC mode "Count both edges". This doubles the effective count rate without changing the sensor, but it also doubles the sensitivity to contact bounce. Confirm the input filter and the sensor's edge quality on a scope before relying on it.
How do I prevent HSC count wrap on long-running systems?
The HSC is a 32-bit signed counter; at 1 kHz it wraps every 24.8 days. For longer runs, configure the HSC's hardware-interrupt-on-high-count to trigger an OB40 and increment a separate DInt "rollover counter" in the OB. Combine the two for an effective 64-bit monotonic count.