S7-300 S_CU Counter: Why Q Turns On After First Pulse
The classic SIMATIC S7-300/S7-400 counter S_CU is one of the most commonly misunderstood blocks in STEP 7 Classic. Engineers writing their first up-counter expect output Q to follow the preset value (turn on at the 5th pulse, for example), but the documentation shows that Q is high whenever the count is greater than zero. This article documents the exact behavior of S_CU and its sibling CU, explains the root cause of the "Q-on-first-pulse" symptom seen in PLCSIM, and provides three field-proven remedies: a comparator ladder, the IEC SFB0 CTU, and a custom FC implementation.
1. Problem Overview
Symptom reported in the field: an S7-300 program with the S_CU block wired with PV = C#5 and I0.0 driving the CU input produces a high Q output after a single pulse, not after five. The same code in TIA Portal using an IEC CTU instance behaves correctly, which leads engineers to believe the legacy counter is broken. The counter is not broken; Q has a different definition than most engineers expect.
| Controller | STEP 7 Version | Counter Block | Behavior of Q |
|---|---|---|---|
| S7-300 (CPU 312 - CPU 319) | V5.5 / V5.6 / V5.7 | S_CU (FC0 in S7-300 standard library) | Q = 1 when count > 0 |
| S7-400 (CPU 412 - CPU 417) | V5.5 / V5.6 | S_CU (same FC0) | Q = 1 when count > 0 |
| S7-1200 (CPU 1211 - CPU 1215) | TIA V13 - V18 | S_CU not available; use CTU IEC FB | Q = 1 when CV >= PV |
| S7-1500 (CPU 1511 - CPU 1518) | TIA V13 - V18 | S_CU not available; use CTU IEC FB | Q = 1 when CV >= PV |
S_CU, S_CD, and S_CUD blocks shipped in the STEP 7 Classic standard library under Standard Library > Counters. The IEC instances SFB0/FB0 (CTU), SFB1/FB1 (CTD), and SFB2/FB2 (CTUD) follow the modern preset-match rule.2. Pin-by-Pin Reference: S_CU (Up Counter)
The S_CU block occupies one counter word in the system memory area of the S7-300 CPU. Each S_CU instance reserves exactly 16 bits of system data; on the CPU 314 and larger these words are addressed as C0..C255. Refer to Siemens entry 44240604 - S7-300 Counter Instructions for the full address map.
| Pin | Direction | Type | Function |
|---|---|---|---|
| CU | Input | BOOL | Count Up: each rising edge increments the current value by 1 |
| S | Input | BOOL | Set: rising edge loads PV into the counter |
| PV | Input | WORD (BCD) | Preset Value, range 0..999, written in BCD as e.g. C#5
|
| R | Input | BOOL | Reset: rising edge clears the counter to 0 and resets the Q bit |
| Q | Output | BOOL | Status: 1 if count > 0, 0 if count = 0 |
| CV | Output | WORD (binary) | Current Value in binary (0..999 decimal) |
| CV_BCD | Output | WORD (BCD) | Current Value in BCD format |
The textbook definition reproduced verbatim from the STEP 7 help: "The signal state at output Q is '1' if the count is greater than zero and '0' if the count is equal to zero." This sentence is the entire root cause of the user's confusion.
3. The Simplified CU Counter
CU is the minimal up-counter also found in the standard library. It exposes only the rising-edge input CU and the Q and CV outputs. CU has no S, no PV, and no R pin; the preset is implied by the BCD word limit of 999.
| Feature | S_CU | CU |
|---|---|---|
| Inputs visible | CU, S, PV, R | CU only |
| Outputs visible | Q, CV, CV_BCD | Q, CV |
| Maximum count | 999 (BCD) | 999 (BCD) |
| Set preset | Yes, via S + PV | No |
| External reset | Yes, via R | No (must reset CV via overwriting logic) |
| Q semantics | Q = (count > 0) | Q = (count > 0) |
| Recommended for new code | No, use IEC CTU | No, use IEC CTU |
Functionally CU and S_CU produce the same Q bit. If you do not need the preset or reset inputs, CU saves ladder space and avoids the temptation to think PV controls Q.
4. Root Cause of the "Q on First Pulse" Symptom
Beginners read the PV input as a "threshold" that, when reached, raises Q. In reality, PV is only used when S pulses, in which case it pre-loads the counter. Q never compares against PV; it compares against zero. Therefore any single pulse causes count to move from 0 to 1, and Q immediately goes high.
4.1 Signal trace in PLCSIM
The typical trace looks like this:
Time | CU (I0.0) | Count | Q
--------+-----------+-------+-----
0.000 s | 0 | 0 | 0
0.050 s | 1 | 1 | 1 <-- first pulse, Q already high
0.100 s | 1 | 2 | 1
0.150 s | 1 | 3 | 1
0.200 s | 1 | 4 | 1
0.250 s | 1 | 5 | 1
0.300 s | R=1 | 0 | 0
The user expected Q to step from 0 to 1 between the fourth and fifth pulse. That expectation comes from the IEC CTU rule (Q = (CV >= PV)) which does not apply to S_CU.
4.2 Why the documentation says PV is a BCD word
The preset is encoded in BCD because the underlying counter cell of the S7-300 stores its value as a BCD word in the system memory area. Writing C#5 to PV loads the literal hex pattern W#16#0005, but the value still goes into a 0..999 BCD range. The S7-1200/1500 generation dropped this representation; counters became plain INT values up to 32767.
5. Solution A - Ladder Comparator on CV
The most idiomatic STEP 7 Classic fix is to ignore Q and generate your own status bit with a comparator. Wire the CV output to a GE_I (greater-than-or-equal, integer) compare block and compare against your target count.
Network 1: count
+----+ +--------+
I0.0-+ CU S_CU Z1 +-CV----+- GE_I +- MyCountDone
| +--------+ |
| | PV +-+- C#5
| +--------+
| | Q (unused) |
+-----+ R |
+------------+
Symbolic equivalent in STL:
A I0.0
CU Z1 // count up
L Z1 // load CV
L 5 // load preset (integer, NOT C#)
>=I // GE_I compare
= M10.0 // MyCountDone
Use >=I not ==I so that if CV ever skips past the preset (debounce glitch, fast input module) you still catch the event. Also, hold MyCountDone with a separate SR latch until you explicitly reset it, otherwise it falls the moment the count moves to 6.
6. Solution B - IEC SFB0 CTU (Preferred)
For new code on S7-300/S7-400, drop the legacy counter and call the IEC instance SFB0 (or its FB0 counterpart if you need multi-instance capability). The IEC block has the modern Q semantics and supports an INT range of 0..32767.
| Parameter | Interface | Type | Description |
|---|---|---|---|
| CU | IN | BOOL | Count up, edge-triggered |
| R | IN | BOOL | Reset to zero, level-triggered |
| PV | IN | INT | Preset, range -32768..32767 (default 0..32767) |
| Q | OUT | BOOL | 1 when CV >= PV, else 0 |
| CV | OUT | INT | Current value 0..32767 |
LAD call example:
+-----+ +--------+ +-----+
I0.0-| CU |---| SFB0 |---| CV--|-- MW100
| R | | DB1 | +-----+
M5.0-| | | |
+-----+ | PV=5 |
+--------+
| Q |-- M20.0 (preset-match bit)
+--------+
Assign an instance DB (DB1 in the snippet) so that CV and the internal edge flag persist across scans. See Siemens entry 45523987 - SIMATIC S7-300/400 IEC Counter Functions for the formal description and the maximum instance DB count per CPU.
R is rising-edge; the IEC R is level-sensitive. If you wire a momentary pushbutton directly to the IEC R, the counter will reset for as long as the button is held, which is rarely what is wanted.7. Solution C - Custom FC Counter
When the application needs a preset, an accumulated value, countdown-to-done, and a done bit all in one block, building a small FC is straightforward. The advantages are:
- No system counter word is consumed, leaving C0..C255 free for legacy
S_CU/S_CD/S_CUDuses. - The
PVrange becomes a fullINT(-32768..32767) or evenDINTif you need more. - You control the polarity and the edge-vs-level reset behavior.
ST source for a minimal "Up Counter with Done" FC:
FUNCTION FC100 : VOID
VAR_INPUT
CU : BOOL; // count input, edge-evaluated inside
RESET : BOOL; // level-sensitive reset
PRESET : INT; // target count
END_VAR
VAR_OUTPUT
DONE : BOOL; // set when CV >= PRESET, sticky until RESET
CV : INT; // current count
END_VAR
VAR
CU_EDGE : BOOL; // edge memory for rising-edge detection
END_VAR
BEGIN
// Reset has priority
IF RESET THEN
CV := 0;
DONE := FALSE;
CU_EDGE := FALSE;
ELSE
// rising edge on CU
IF CU AND NOT CU_EDGE THEN
CV := CV + 1;
IF CV >= PRESET THEN
DONE := TRUE;
END_IF;
END_IF;
CU_EDGE := CU;
END_IF;
END_FUNCTION
Calling sequence in OB1:
CALL FC100
CU := I0.0
RESET := I0.1
PRESET := 5
DONE := M30.0 // "done after 5th pulse"
CV := MW40
If you need the counter value to survive a power cycle or CPU restart, swap the FC for an FB backed by an instance DB. The FB retains its static variables in the instance DB after STOP->RUN, while an FC's VAR area is reinitialized each cycle.
8. Common Pitfalls and Field Caveats
8.1 MW / MB Overlap
On the S7-300, MW0 is the bit-stitched word of MB0 and MB1, so MW0 shares bits with M0.0..M1.7. If any program segment uses M0.0..M1.7 as a one-shot (positive edge memory flag) and another segment uses MW0 as an integer counter, the two will corrupt each other. This is one of the most common reasons a counter shows a "random" initial value in PLCSIM. Use the diagnostic table to verify no overlapping writes exist.
| Word alias | Byte 0 | Byte 1 | Bit range |
|---|---|---|---|
| MW0 | MB0 | MB1 | M0.0..M1.7 |
| MW2 | MB2 | MB3 | M2.0..M3.7 |
| MW4 | MB4 | MB5 | M4.0..M5.7 |
Rule of thumb: dedicate whole word ranges to integer use and whole byte ranges to bit use; never mix.
8.2 PLCSIM Accumulator Persistence
PLCSIM does not fully clear the simulated accumulators (ACCU1, ACCU2) or the work memory between STOP and RUN, especially across reloads of the same project. After multiple "downloads" the same MW may show the leftover value from a prior simulation. To reset:
- Select CPU > Clear/Reset in PLCSIM.
- Power-cycle the simulated PLC (MRES not sufficient on PLCSIM).
- Re-download the hardware configuration before testing.
8.3 Counter Overflow
S_CU overflows silently at 999 and wraps to 0. The classic mistake is to leave the counter running after the target event, causing Q to flap. If the count can exceed 999, use SFB0 CTU with an INT limit of 32767, or implement your own saturating counter in an FB.
8.4 Input Debounce
Mechanical contacts produce multiple edges per press. The S7-300's onboard digital inputs debounce in hardware (filter time set in HW Config, default 6.4 ms), but if the program counts both edges (e.g., a fast counter with no debounce flag), the count can race past the preset. Use a FP (positive edge) instruction before the CU input.
8.5 Re-Triggering PV with the S Input
Many first-time users wire S high continuously and expect the counter to reload every cycle. The S input is rising-edge; a permanently high S loads the preset exactly once. To load on demand, drive S with a one-shot from a comparator output or a pushbutton.
9. Verification Procedure
- Download the project to the CPU or PLCSIM and place it in RUN.
- Open the "Monitor/Modify" table and force
CV,Q, and your done bit visible. - Toggle
I0.0once. ConfirmCV = 1andQ = 1(this is correct forS_CU). - Toggle
I0.0four more times.CVshould now equal 5. - Observe the comparator result or the IEC
CTUQ; it must be 1. - Pulse
R;CVmust return to 0 and the done bit must clear. - Run for 1500 pulses and verify the counter saturates or wraps according to design.
10. Troubleshooting Matrix
| Observed symptom | Likely cause | Remedy |
|---|---|---|
| Q high after first pulse | S_CU Q semantics; user expected preset match | Use comparator on CV, or switch to IEC CTU |
| Q never goes high | PV wired with C# but S never pulsed | Apply a one-shot to S, or use a comparator |
| Q high immediately at startup | PLCSIM accumulator leftover | Clear/Reset PLCSIM, re-download HW config |
| MW0 shows random initial value | Bit flag in M0.0..M1.7 stomps on MW0 | Use a non-overlapping MW range |
| Counter counts two pulses per press | No FP (edge) before CU | Insert positive-edge detector |
| Counter resets spontaneously | R wired as level, not edge | Add FP on R input, or use IEC CTU's R correctly |
| Counter wraps past 999 | S_CU BCD limit exceeded | Switch to SFB0 CTU (INT up to 32767) or FB |
| Done bit goes high but immediately low | Done not latched with SR/RS | Latch with SET/RESET until explicit acknowledge |
11. Migrating to S7-1200/1500
The S7-1200 and S7-1500 generations removed the legacy S_CU, S_CD, and S_CUD blocks entirely. The replacement is the IEC counter system block CTU, placed from the "Instructions" task card under "Counter operations." TIA Portal auto-generates the instance DB. The semantics are exactly those of SFB0 on the S7-300, so any program ported across needs only the signal wiring updated. For context on global data block integration on the S7-1200, see the SiePortal S7-1200 basic questions thread referencing the SIMATIC S7 system manual page 193 for assigning counter values to a global DB.
12. Recommended Defaults
- New S7-300/S7-400 code: use
SFB0 CTUwith an instance DB; do not consume a system counter word. - New S7-1200/S7-1500 code: use the system
CTUblock from the TIA Portal instruction palette. - Legacy
S_CUonly when a pre-existing program already uses a system counter cell and you cannot touch the I/O mapping. - Always latch the "done" bit with an SR flip-flop; never trust the IEC
Qto remain high after the count advances pastPV. - Reserve separate MW ranges for integers and M-byte ranges for booleans to avoid MW/MB overlap.
FAQ
Why does my S_CU Q output turn on after the very first pulse?
Because S_CU defines Q as "1 when count > 0" rather than "1 when count >= PV." Any rising edge on CU moves the count from 0 to 1, which immediately sets Q. Use a GE_I comparator on the CV output, or replace S_CU with the IEC block SFB0 CTU, which has preset-match semantics.
What is the difference between S_CU and CU in STEP 7 Classic?
S_CU exposes the full interface: CU, S, PV, R, and outputs Q, CV, CV_BCD. CU is a stripped-down version showing only CU, Q, and CV; it has no preset, no set, and no reset pins. Both share the same Q semantics.
How do I make Q turn on only at the 5th pulse and stay on?
Replace S_CU with SFB0 CTU (or its FB0 multi-instance), wire PV = 5, and connect Q to an SR flip-flop that you reset explicitly. The IEC Q goes high when CV >= PV and stays high as long as the count is not reset.
Why does my MW0 show a random initial value in PLCSIM?
Two likely reasons: PLCSIM does not fully clear work memory between simulations, and MW0 overlaps with MB0/MB1 (M0.0..M1.7), so any bit-flag use in that range corrupts the word. Use Clear/Reset on the PLCSIM CPU, re-download the hardware, and move the counter to a clean MW such as MW20.
Can S_CU count higher than 999?
No. S_CU stores its value as BCD in a system counter word limited to 0..999. For higher counts use SFB0 CTU (INT, up to 32767) or build your own FC/FB counter with DINT range.