Siemens S7-300 LAD: 0-99 Counter Driving 7-Segment Displays

David Krause15 min read
S7-300SiemensTutorial / 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

Overview: Counting 0-99 with Two 7-Segment Displays on a Siemens S7-300

Direct-driving two common-cathode 7-segment LED displays from a Siemens SIMATIC S7-300 PLC is a classic ladder-logic training exercise. The objective is straightforward: maintain an integer counter value from 0 to 99, split it into a tens digit and a units digit, and energize seven output bits per digit (14 total) to render the number visually. While production machines normally use an HMI panel, a BCD-to-7-segment decoder IC, or a smart display module, the all-discrete implementation is the cleanest way to understand Ladder (LAD) bit logic, integer arithmetic, and combinational decoding on the S7-300/S7-400 platform.

This reference documents the canonical Siemens approach using the CTU (Count Up) instruction, integer DIV/MOD arithmetic for digit separation, and parallel OR-terms per LED segment derived from a truth table. A cascaded-counter variant and a BCD decoder hardware alternative are also covered. All instruction behavior matches the official S7-300/400 Ladder Logic programming manual.

Reference: Siemens S7-300/400 Ladder Logic (LAD) Programming Reference.

Prerequisites

  • SIMATIC S7-300 CPU (e.g., CPU 314, CPU 315-2 DP) or S7-400 CPU with STEP 7 V5.x or TIA Portal V16+.
  • SM 322 digital output module sized for 14 outputs (Q 0.0 - Q 1.5 covers it on most bases).
  • SM 321 digital input module providing at least three inputs for COUNT_UP, COUNT_DOWN, and RESET.
  • Two common-cathode 7-segment LED displays (one for tens, one for units). Each requires current-limiting resistors sized per LED Vf and forward current If (typically 330 Ω to 1 kΩ for 24 V sink sourcing with the display rated 2.0 V / 10-20 mA).
  • STEP 7 LAD/FBD/ST editor with permission to download to the PLC.
Note: S7-300 transistor outputs sink/source at 24 V DC with 0.5 A per channel on the SM 322 DO32. For LED loads of 10-20 mA per segment the DO32 is overkill but works fine. Watch the aggregate thermal budget if you keep all 14 segments lit simultaneously (worst case 14 × 0.02 A = 0.28 A from a single 8-channel group is well within the 4 A per group limit).

Hardware: Segment Pin Mapping

Each 7-segment display contains seven segment LEDs (a-g) plus an optional decimal point (dp). Labeling follows the standard topology:

PLC Output Display Segment Position
Q 0.0 a (top horizontal) 11 o'clock - 1 o'clock
Q 0.1 b (top right vertical) 1 o'clock - 3 o'clock
Q 0.2 c (bottom right vertical) 5 o'clock - 7 o'clock
Q 0.3 d (bottom horizontal) 7 o'clock - 11 o'clock
Q 0.4 e (bottom left vertical) 5 o'clock - 7 o'clock mirror
Q 0.5 f (top left vertical) 11 o'clock - 7 o'clock mirror
Q 0.6 g (middle horizontal) center
Q 0.7 (unused / optional dp) decimal point
Q 1.0 a (tens digit) as above for second display
Q 1.1 b (tens digit) as above for second display
Q 1.2 c (tens digit) as above for second display
Q 1.3 d (tens digit) as above for second display
Q 1.4 e (tens digit) as above for second display
Q 1.5 f (tens digit) as above for second display
Q 1.6 g (tens digit) as above for second display

The mapping above is identical in shape for both digits - only the output byte changes. With this convention, a single function block (FB) can drive both digits by parameterizing the output base address.

7-Segment Truth Table (Decimal 0-9)

Each digit is rendered by energizing a fixed combination of seven segments. Bit positions in the table are read as: a b c d e f g (Q 0.0 through Q 0.6 for the units digit). 1 = LED on, 0 = LED off.

Digit a (Q0.0) b (Q0.1) c (Q0.2) d (Q0.3) e (Q0.4) f (Q0.5) g (Q0.6) Hex pattern (gfedcba)
0 1 1 1 1 1 1 0 0x3F
1 0 1 1 0 0 0 0 0x06
2 1 1 0 1 1 0 1 0x5B
3 1 1 1 1 0 0 1 0x4F
4 0 1 1 0 0 1 1 0x66
5 1 0 1 1 0 1 1 0x6D
6 1 0 1 1 1 1 1 0x7D
7 1 1 1 0 0 0 0 0x07
8 1 1 1 1 1 1 1 0x7F
9 1 1 1 1 0 1 1 0x6F

Column reduction reveals which digits light each segment, which is exactly the combinational logic the LAD network must produce:

  • Segment a on for: 0, 2, 3, 5, 6, 7, 8, 9 (all except 1 and 4)
  • Segment b on for: 0, 1, 2, 3, 4, 7, 8, 9 (all except 5 and 6)
  • Segment c on for: 0, 1, 3, 4, 5, 6, 7, 8, 9 (all except 2)
  • Segment d on for: 0, 2, 3, 5, 6, 8, 9 (all except 1, 4, 7)
  • Segment e on for: 0, 2, 6, 8 (only)
  • Segment f on for: 0, 4, 5, 6, 8, 9 (all except 1, 2, 3, 7)
  • Segment g on for: 2, 3, 4, 5, 6, 8, 9 (all except 0, 1, 7)

Step 1: Implement the Counter with CTU

The CTU (Count Up) instruction increments a 16-bit integer Counter word (C) on each rising edge of its CU input. The instruction per the S7-300/400 LAD manual takes the form:

   CU     CV                ┌──────────────┐
──┤ ├─────┬─────────────────┤              │
          │                 │   CTU        │
   R      │   PV            │              │
──┤/├─────┴─────────────────┤              │
                            │  C10         │
                            └──────────────┘
Parameter Description Typical Assignment
CU Count Up input (rising-edge sensitive) I 0.0 (pushbutton NO)
R Reset (level-sensitive) I 0.2 (pushbutton NO)
PV Preset Value (count ceiling) 100 (or 99 with comparison)
CV (hex) Current counter value in BCD/INT C10 (the counter word)
Q Status: CV ≥ PV M 0.0 (rollover flag)

The counter value is read as a 16-bit integer from word C10. The legacy S_CU block works identically and is preferred in STEP 7 V5.x; CTU is the IEC 61131-3 equivalent available in TIA Portal and S7-1500 projects, both share the same behavior on S7-300.

Critical: PV is a BCD-compared value when using S_CU. The IEC CTU compares in integer mode. Always confirm the instruction type before writing PV. To roll over from 99 back to 00, wire the Q output (CV ≥ PV) through a comparison to a coil that resets the counter via the R input, or write directly with MOVE 0 to CV when Q is high.

Step 2: Split the Counter into Tens and Units Digits

The 16-bit counter value (range 0-99) is split into two 4-bit nibbles using integer division and modulo. The arithmetic lives in two network segments in OB1:

Network 3: Tens digit
   ┌──────────┐      ┌──────────┐
   │  DIV_I   │      │  MOVE    │
   │  EN  ENO │      │  EN  ENO │
   └────┬─────┘      └────┬─────┘
        │                  │
   IN1: MW10 (C10)    IN : MW20
   IN2: 10            OUT : MB22  (tens digit, INT)
   OUT: MW12

Network 4: Units digit
   ┌──────────┐      ┌──────────┐
   │  MOD     │      │  MOVE    │
   │  EN  ENO │      │  EN  ENO │
   └────┬─────┘      └────┬─────┘
        │                  │
   IN1: MW10 (C10)    IN : MW20
   IN2: 10            OUT : MB23  (units digit, INT)
   OUT: MW14

Result: for counter value 47, MW12 = 4 (tens), MW14 = 7 (units). The values 0-9 are then routed to the segment decode network. Edge cases: the modulo instruction returns 0 when the count is a multiple of 10, which correctly displays '0' in the units column without any extra zero-suppress logic.

Step 3: Decode the Digit into Seven Segment Bits

One network per segment is the textbook approach. Each segment output is driven by an OR of NO contacts that close when the digit variable equals the specific value lighting that segment. With MB22 (tens digit) feeding the comparison:

Network 5: Segment a (Q 1.0 for tens, Q 0.0 for units)
      ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐
──(==I)─(==I)─(==I)─(==I)─(==I)─(==I)─(==I)─(==I)─┤ ( Q 1.0 )  ← segment a (tens)
     0     2     3     5     6     7     8     9

Network 6: Segment b
──(==I 0)──(==I 1)──(==I 2)──(==I 3)──(==I 4)──(==I 7)──(==I 8)──(==I 9)──( Q 1.1 )

Network 7: Segment c
──(==I 0)──(==I 1)──(==I 3)──(==I 4)──(==I 5)──(==I 6)──(==I 7)──(==I 8)──(==I 9)──( Q 1.2 )

Network 8: Segment d
──(==I 0)──(==I 2)──(==I 3)──(==I 5)──(==I 6)──(==I 8)──(==I 9)──( Q 1.3 )

Network 9: Segment e
──(==I 0)──(==I 2)──(==I 6)──(==I 8)──( Q 1.4 )

Network 10: Segment f
──(==I 0)──(==I 4)──(==I 5)──(==I 6)──(==I 8)──(==I 9)──( Q 1.5 )

Network 11: Segment g
──(==I 2)──(==I 3)──(==I 4)──(==I 5)──(==I 6)──(==I 8)──(==I 9)──( Q 1.6 )

For the units digit, repeat networks 5-11 with output byte Q 0 and digit source MB23. Each ==I comparator has the form ==I [operand] [reference value]; the operand is the digit word and the reference is the decimal digit 0-9.

Optimization: if the application can tolerate an extra output bit, drive the segments directly from the truth table as 7-bit constants using a small lookup FB with a CASE statement in SCL. A CASE-based implementation is typically 80% shorter in source lines and is the recommended pattern for production code.

Step 4: Optional Count-Down Support with CTD

Many training specifications call for a count-down input as well. The S7-300 CTUD (Count Up/Down) instruction extends CTU with a CD input and a load input LD. Wire it as:

Signal Address Effect
CU (count up) I 0.0 Increments CV on rising edge
CD (count down) I 0.1 Decrements CV on rising edge, floor at 0
R (reset) I 0.2 Clears CV to 0 (level)
LD (load) I 0.3 Loads CV with PV
PV 99 (INT) Top count for rollover / load value

The QU output asserts when CV ≥ PV; QD asserts when CV ≤ 0. Use QU in an SCL IF to reset CV to 0 for a clean 99→00 rollover, or compare with ==I in LAD.

Alternative: Cascaded Two-Counter Approach

An alternative architecture uses two cascaded counters. Counter A counts 0-9 in the units place; on its terminal count, it pulses Counter B which counts the tens digit. Reset of Counter A also provides the count pulse to Counter B's CU input.

Counter A (units): CTU C1, PV = 10, CU = I 0.0, R = I 0.2
                   C1.Q (CV >= 10) → Counter B CU via edge

Counter B (tens):  CTU C2, PV = 10, CU = rising edge of C1.Q, R = I 0.2
                   C2.Q (CV >= 10) → reset both via pulse timer (1 cycle)

This pattern keeps each counter at a small range and allows direct segment decoding from the BCD value C1 and C2 if a BCD-to-7-segment decoder IC (CD4511, SN7447) is wired to the output word. It does not, however, give the application a single 0-99 integer to use elsewhere, so any downstream math (scaling, comparison, log) becomes harder.

Alternative: BCD-to-7-Segment Decoder IC

The hardware alternative is dramatically simpler in software. Use a CD4511 (CMOS) or SN7447 (TTL) BCD-to-7-segment latch/decoder/driver per digit. Feed 4 bits of BCD per digit, the IC handles the truth table internally and drives the LEDs directly (with appropriate current-limiting resistors). The PLC only needs to provide 4 outputs per digit (8 total) and the BCD conversion is handled by the BCD output of the S7-300 counter (the S_CU instruction exposes a BCD value at CV when used in the legacy form). For integer counters using CTU, convert INT to BCD with the I_BCD instruction (16-bit to BCD-word) and split nibbles with AW (AND Word) and OW (OR Word) masking.

Snubber / wiring: The CD4511 has internal output current limiting only on some variants. For 24 V PLC-driven BCD buses, prefer 5 V signaling with the SN7447 or use a TPIC6B595 power shift register driven from a single PLC output byte.

Verification and Commissioning

  1. Download the FC/FB and DB to the CPU. Switch to RUN-P mode.
  2. In the PLC's variable table (VAT), force C10 = 0 and verify both displays read 00. The middle horizontal segment (g) should be off; all other segments lit.
  3. Force C10 through values 0, 1, 5, 9, 10, 47, 55, 88, 99, 100. At each, observe that the LED segments match the truth table and the rollover from 99 → 00 occurs (CV resets to 0 and the Q status pulses).
  4. Pulse CU (I 0.0) and watch CV increment. Verify the tens digit rolls from 0 to 9 without skipping.
  5. Pulse CD (I 0.1) and verify CV decrements from 99 to 0 with floor at 0.
  6. Press R (I 0.2) and verify CV clears to 0 in a single scan.
  7. Disconnect the field wiring at the display terminal block and verify each output bit with a multimeter. Output should be 24 V DC when the corresponding segment is supposed to be lit, 0 V when not.

Troubleshooting Matrix

Symptom Likely Root Cause Corrective Action
Display always shows 00 Counter not incrementing; CTU CU input not seeing rising edge; PV=0 Monitor I 0.0 and C10 in VAT; confirm PV is loaded; check that R input is not held high
Display always shows 88 All seven segments energized - common wiring inverted or all OR-terms in decode are stuck Check common cathode/anode wiring; verify the 7 OR-terms for segment decode are not shorted to V
Display shows garbage at higher counts DIV / MOD using wrong byte or counter value type (BCD vs INT mismatch) Confirm DIV_I operand is INT (MW) not BCD; check that CTU integer is read, not the BCD version
Tens digit only updates at 9, not 10 PV=9 instead of 10; or counter not cascaded correctly Set PV=10 for the units counter; check the pulse routing from units.Q to tens.CU
Counter overflows past 99 No rollover reset wired; PV=100 but no action taken on Q Add network: IF C10.Q THEN MOVE 0 → C10 END_IF
Segments dim when both digits light '8' Aggregate current exceeds PLC output group limit, or resistor values too high Recalculate per-segment resistor for 10-15 mA target; check SM 322 group current ≤ 4 A
One digit flickers, other stable Common-cathode connection floating on the flickering digit Verify both common pins are tied to 0 V; check terminal block seating
Display updates one cycle late Decode network placed before counter network; OB1 cycle ordering Reorder: counter network first, then DIV/MOD, then decode

Edge Cases and Field-Proven Caveats

  • BCD vs INT confusion: the legacy S_CU instruction stores its current value in BCD in the upper byte of the counter word, and integer in the lower byte. Reading the wrong half of the word (e.g., MW10 when you meant MB11) is a frequent cause of "the count is always 0" or "the count is always hex" symptoms.
  • Integer overflow: DIV_I on S7-300 returns 0 remainder if IN1 is negative, but the counter never goes negative in this design. MOD returns 0 when IN1=0, so the units display correctly shows '0' at count 0.
  • Scan-time jitter: if the count input is a 1 kHz pulse train, the OB1 cycle time of ~5-10 ms will miss pulses. Use a high-speed counter (SFB 47 HSC on the CPU 314 IFM) or the FM 350-1 counter module for high-rate counting.
  • Retentivity: by default, S7-300 counters are not retained on power down. If the count must survive a power cycle, mark the counter as retentive in the CPU properties (Retentive Memory: Number of counters starting at C 0 = at least 1).

Comparison: Architecture Choices

Approach PLC Outputs Required Software Complexity Hardware Complexity Recommended For
Direct drive (this article) 14 Medium (7 OR-terms × 2 digits) Low (resistor per segment) Training, prototype
BCD decoder + 4-bit bus 8 Low (BCD conversion only) Medium (CD4511 per digit) Cost-sensitive production
Cascaded two-counter 14 (or 8 with decoder) Low (no DIV/MOD needed) Low When single 0-99 INT not needed downstream
Shift register (TPIC6B595) 3 (clock, data, latch) Medium (serial bit-bang) Medium (one IC per digit) Long cable runs, multiple digits
HMI panel 0 (uses PLC tag) Minimal HMI cost Any production deployment

FAQ

Which Siemens instruction increments a counter from 0 to 99 on a S7-300?

Use the IEC CTU (Count Up) instruction in OB1 with PV = 99 (or 100 with a reset network on Q) and CV stored in a counter word such as C10. The legacy S_CU block in STEP 7 V5.x is functionally equivalent. See the S7-300/400 LAD reference for parameter details.

How do I split the counter into tens and units digits in LAD?

Use integer division DIV_I with divisor 10 to get the tens digit into MW12, and modulo MOD with divisor 10 to get the units digit into MW14. Both instructions take a 16-bit INT input word (the counter value) and return a 16-bit INT result. Place these two networks in OB1 between the counter network and the segment decode networks.

How many PLC outputs are needed to drive two 7-segment displays directly?

14 outputs: 7 per digit for segments a-g, plus a separate byte for each digit so the decode logic can address them independently. A common optimization is to use a BCD-to-7-segment decoder IC (CD4511 or SN7447) and drop to 4 outputs per digit (8 total), or to multiplex both digits on 7 shared outputs with two common-anode select lines (9 outputs total) at the cost of scan-time flicker.

Can I count up and down with the same network?

Yes - use the CTUD (Count Up/Down) IEC instruction. Wire CU to the count-up input, CD to the count-down input, R to the reset, and LD to the load input. CV provides the current value, QU asserts when CV ≥ PV, QD asserts when CV ≤ 0. The CTU/CTD pair in legacy STEP 7 V5.x gives the same behavior through two separate blocks.

Why does my display show garbage at counts above 9?

The most common cause is reading the BCD half of the counter word instead of the integer half, or running DIV_I on a BCD value. Confirm the counter is a true INT counter (IEC CTU) and that the MW feeding the DIV is the integer CV, not the BCD CV. Forcing MW10 in a VAT and stepping through 0, 10, 47, 99 will quickly reveal whether the divide is seeing the expected value.

Back to blog