Siemens FM350-1 Counter Module: Encoder Reset and CMP Read-Back Troubleshooting
The Siemens FM350-1 (order number 6ES7350-1AH03-0AE0) is a single-channel, 32-bit counter module for SIMATIC S7-300 and ET 200M. It accepts 24 V or RS422 incremental encoder signals up to 500 kHz and provides two on-board digital outputs that can be triggered when the count crosses a programmed comparison value. Two recurring field issues complicate otherwise straightforward applications:
- ACT_CMP1 / ACT_CMP2 read-back stays at 0 after writing CMP_V1 / CMP_V2 to the output process image.
- Direct SW-gate reset of ACT_CNTV drops encoder pulses between the reset command and the next OB1 cycle.
Both problems are not firmware bugs. They are a consequence of the module's hardware-comparator and gate-control timing, and they are documented in the FM 350-1 operating instructions (manual A5E03648639-01, 05/2011). This reference presents the underlying root cause analysis and a software-comparator pattern that eliminates both issues without losing a single encoder pulse.
FM350-1 Module Identification and Ordering Data
The FM350-1 is delivered in three hardware revisions. All are mechanically and electrically compatible at the backplane bus, but the firmware revision affects which function blocks are accepted by the FM350-1 configuration package.
| MLFB / Order Number | Hardware Revision | Firmware | Status |
|---|---|---|---|
| 6ES7350-1AH01-0AE0 | 1 | ≤ V1.0 | Discontinued, replacement: -1AH03 |
| 6ES7350-1AH02-0AE0 | 2 | V2.0 | Discontinued, replacement: -1AH03 |
| 6ES7350-1AH03-0AE0 | 3 | V3.0 | Current production |
The module is installed into a free slot in an S7-300 rack (centralised) or in an ET 200M station behind an IM153. The configuration package "Configuration software for FM 350-1 and FM 450-1 counter modules" (Siemens KB article 28291262) must be installed on the same STEP 7 V5.x PC that hosts the hardware configuration. Without this package the FM350-1 is not selectable in HW Config and the parameterisation dialog is missing.
Technical Specifications and Operating Modes
The electrical and functional envelope of the module is the boundary condition that constrains every workaround. Values below are extracted from the FM 350-1 operating instructions (A5E03648639-01).
| Parameter | Value | Notes |
|---|---|---|
| Count inputs | A, B, N (24 V or RS422) | Single-ended 24 V (default) or 5 V differential (RS422) |
| Maximum count frequency | 500 kHz (24 V) / 1 MHz (RS422) | Quadrature x4 evaluation selectable |
| Count width | 32 bit (DINT) | Range −2 147 483 648 … +2 147 483 647 |
| Number of channels | 1 | One encoder / one counter per module |
| Digital outputs | 2 (DQ0, DQ1) | 24 V / 0.5 A, short-circuit proof, isolated |
| Comparator values | CMP_V1, CMP_V2 | 32-bit, written to PQ area |
| Operating modes | Continuous / Single / Periodic count | Mode set in HW Config parameter dialog |
| Gate control | SW gate, HW gate, cancel/reset | Control word bits 0, 1, 2, 3 |
| Diagnostic | Wire break, sensor supply, RAM/ROM | Signalled in STS bit fields and SF LED |
The two digital outputs are not general-purpose; they are physically bonded to the on-board hardware comparators CMP1 and CMP2. When the on-the-fly count value equals the corresponding comparison value, the assigned output is pulsed for one OB1 cycle (or latched, depending on parameterisation). This output/comparator linkage is the source of both field issues and is the reason the entire comparator subsystem can be replaced by a software implementation.
Process Image Layout: Byte Addresses 272 to 287
The FM350-1 occupies a fixed 16-byte slot in the process image regardless of slot position. The slot is configured in HW Config. The address shown below (272..287) is a real example; in a different project the base address may differ. The offsets within the slot are always the same.
| Address (PI / PQ) | Mnemonic | Length | Direction |
|---|---|---|---|
| 272..273 | STS / CTRL | WORD | Status (PI) / Control (PQ) |
| 274..275 | reserved | WORD | — |
| 276..279 | ACT_CNTV | DINT | PI only (read) |
| 280..283 | ACT_CMP1 | DINT | PI only (read) |
| 284..287 | ACT_CMP2 | DINT | PI only (read) |
The output (PQ) side mirrors the same byte layout:
| Address (PQ) | Mnemonic | Length | Function |
|---|---|---|---|
| 272..273 | CTRL | WORD | Control word (gate, reset, encoder mode) |
| 274..275 | reserved | WORD | — |
| 276..279 | CMP_V1 | DINT | Comparison value 1 (DQ0) |
| 280..283 | CMP_V2 | DINT | Comparison value 2 (DQ1) |
| 284..287 | reserved | WORD | — |
In the source example the user references PID 276. The PID mnemonic denotes a 32-bit double word in the process image starting at byte 276, which is exactly ACT_CNTV (or CMP_V1 when read/written from PQ). Double-word reads/writes on the FM350-1 must be aligned on even byte addresses, and 276 is even, so L PID 276 is valid STL.
D) / long-word (DI) access on even byte addresses. If the FM350-1 base address in your project is odd (for example after a slot swap), the PID access will fault (OB121). Re-align the slot or split the read into two PW accesses and reassemble.Application Problem: Triggering Outputs at Encoder Positions
The original application is a textbook example of position-based event firing:
- Encoder increments a 32-bit counter value ACT_CNTV continuously.
- When ACT_CNTV reaches 2000, DQ0 must fire (e.g. a marking actuator).
- When ACT_CNTV reaches 5000, DQ1 must fire and the counter must reset to 0 so the cycle restarts.
On paper the FM350-1 hardware is the right tool: CMP_V1 = 2000, CMP_V2 = 5000, parameterise the outputs to pulse on equality, and add a software reset of the counter when DQ1 fires. In practice two symptoms emerge:
-
Symptom 1: After writing
L 2000; T PQD 276(CMP_V1) andL 5000; T PQD 280(CMP_V2), the read-back valuesL PID 280(ACT_CMP1) andL PID 284(ACT_CMP2) consistently return 0. - Symptom 2: Resetting the counter by writing the CTRL word (toggling the cancel/reset bit) causes ACT_CNTV to drop to 0, but during the next cycle the comparator never fires again. A manual encoder hand-wheel reveals that several pulses are simply not counted between the reset command and the next comparator match.
Both symptoms trace back to the same root: the hardware comparator is a poor abstraction for time-critical, deterministic I/O. The fix is to take the comparator out of the module and implement it in the CPU where the cycle time and the read-back semantics are well defined.
Root Cause: Why ACT_CMP1 / ACT_CMP2 Return Zero
Section 7 of the FM350-1 operating instructions (A5E03648639-01) describes the ACT_CMP1 / ACT_CMP2 update behaviour precisely. The values are not mirrors of CMP_V1 / CMP_V2 written by the user. They are the comparison values that the on-board FPGA has currently latched into the comparator pipeline. The latching is gated by the following conditions, all of which must be true for ACT_CMPx to update from zero:
- The SW gate must be open (CTRL bit 0 = 1).
- The HW gate must be open (terminal X1, pin 8, see wiring manual).
- The module must be in an operating mode where the comparator is enabled (Continuous / Periodic, not Single mode with comparator disabled).
- The user must have set the "load comparison value" bit (CTRL bit 8) and the module must have acknowledged the transfer (STS bit 8 returns 1) before a new value is written. If the load bit is not pulsed for one OB1 cycle, the transfer never completes.
- The DI/DO diagnostic interrupt enable must be set in HW Config; otherwise the module stops servicing comparator updates after a diagnostic event.
Miss any one of these and ACT_CMP1 / ACT_CMP2 stay at 0 — the read-back is not a debugging aid, it is a status flag. The easiest cure is to never read it.
Root Cause: Pulse Loss on Direct Counter Reset
The CTRL word layout is:
| Bit | Symbol | Function |
|---|---|---|
| 0 | SW_GATE | 1 = open SW gate, 0 = close |
| 1 | HW_GATE | Status only (read via STS) |
| 2 | CANCEL | 1 = cancel, 0 = no cancel |
| 3 | RESET | Set counter and latch registers to 0 |
| 4..7 | — | Reserved |
| 8 | LD_CMP | Transfer CMP_V1 / CMP_V2 to the latch |
To reset the counter the user must set CTRL bit 3 (RESET) for at least one OB1 cycle, then clear it. Internally the FM350-1 latches the reset request, then on the next count-clock edge it clears the up/down counter register. The input pulse counter inside the FPGA, however, is also held in reset while CTRL.3 is high. Encoder edges that arrive during that window are lost. At 500 kHz and a 10 ms OB1 cycle the worst-case loss is 5 000 counts — a real and observable quantity on a 1024-ppr encoder in x4 mode.
Resolving this requires either:
- A reset pulse shorter than 1 / max_count_frequency (here 2 µs), which OB1 cannot guarantee; or
- A logical reset of the count that does not touch the FPGA register at all.
The second approach is the only deterministic one, and is implemented by treating ACT_CNTV as a raw, monotonically increasing read-only register and subtracting a software offset on every scan.
Solution: Software-Based Comparator with Zero Offset
Declare two DINTs in the CPU's working DB:
-
ZeroOffset— the snapshot of ACT_CNTV at the moment the cycle is "logically zero". -
UserCount— the virtual, reset-able count visible to the application.
Then in OB1 every scan:
UserCount = ACT_CNTV − ZeroOffset
and the application comparator is a simple integer comparison on UserCount:
UserCount ≥ 2000 → set DQ0 (any logic output of the CPU, not the FM350-1 output)
UserCount ≥ 5000 → set DQ1 AND set the reset one-shot for the next scan
The "reset" is now a single assignment:
ZeroOffset = ACT_CNTV
executed on the positive edge of the reset request. No pulse is dropped because the FPGA counter is never touched. ACT_CNTV keeps incrementing indefinitely; the application sees a wrap-around-free 0…5000 counter, and the hardware comparators are simply left disabled in HW Config.
Step-by-Step Implementation in STEP 7 STL
Allocate the following symbols in DB100 (or any working DB / M area):
| Symbol | Address | Type | Initial value |
|---|---|---|---|
| ResetTrig | DB100.DBX0.0 | BOOL | FALSE |
| OneShot | DB100.DBX0.1 | BOOL | FALSE |
| DQ0_Out | DB100.DBX0.2 | BOOL | FALSE |
| DQ1_Out | DB100.DBX0.3 | BOOL | FALSE |
| ZeroOffset | DB100.DBD4 | DINT | 0 |
| UserCount | DB100.DBD8 | DINT | 0 |
| SetPoint1 | DB100.DBD12 | DINT | 2000 |
| SetPoint2 | DB100.DBD16 | DINT | 5000 |
OB1 — segment 1, edge-detected zero-offset capture:
A "ResetTrig" // ResetTrig (e.g. from HMI or from DQ1_Out)
FP "OneShot" // edge flag, FB-internal or DB bit
JNB END1
L PID 276 // ACT_CNTV (32-bit, aligned)
T "ZeroOffset" // snapshot — pulse loss impossible
END1: NOP 0
OB1 — segment 2, the running virtual counter (executed every cycle):
L PID 276 // ACT_CNTV (32-bit)
L "ZeroOffset" // 32-bit
-D // double-integer subtraction
T "UserCount" // visible count to the rest of the program
OB1 — segment 3, software comparators and outputs:
L "UserCount"
L "SetPoint1" // 2000
>=D
= "DQ0_Out" // mapped to any CPU DO (e.g. Q4.0)
L "UserCount"
L "SetPoint2" // 5000
>=D
= "DQ1_Out"
A "DQ1_Out"
= "ResetTrig" // self-resetting trigger for segment 1
-I (16-bit integer subtraction). Because ACT_CNTV and ZeroOffset are 32-bit, the correct opcode is -D. Using -I will compile and run but will silently truncate ACT_CNTV to 16 bits, producing wrong counts above 32 767. Always use -D for any 32-bit counter arithmetic on S7-300/400.OB1 — segment 4, optional hysteresis to release the outputs at the end of the cycle. The pattern above keeps DQ0 and DQ1 latched between cycles 2000/5000 and 0. If the application needs edge-pulsed outputs, add a one-cycle pulse on first cross:
L "UserCount"
L 1
<=D
A "DQ0_Out"
S "DQ0_Pulse" // fires for one scan on the way back to 0
AN "DQ0_Out"
R "DQ0_Pulse"
Hardware Configuration, Wiring, and Encoder Selection
In STEP 7 HW Config, after the FM350-1 has been inserted and addressed (the example uses PI 272..287 / PQ 272..287), double-click the module and set the following parameters:
| Tab | Parameter | Recommended setting for this application |
|---|---|---|
| Basic | Operating mode | Continuous counting |
| Basic | Encoder signal | 24 V incremental (A, B, N) or RS422, matching your sensor |
| Basic | Count direction | Up (default) |
| Basic | Gate control | SW gate only (cancel / reset controlled by user program) |
| Outputs | DQ0 / DQ1 function | No function — outputs are driven by the CPU now |
| Outputs | Comparator source | — (n/a when "No function") |
| Diagnostics | Wire break, sensor supply | Enable, route to OB82 |
Wiring on the front connector (X1):
| Pin | Signal | Function |
|---|---|---|
| 1 | 24 V | Encoder supply (24 V out, fused 500 mA) |
| 2 | GND | Encoder supply return |
| 3 | A | Track A (24 V or RS422+) |
| 4 | A\ | Track A inverted (RS422 only) |
| 5 | B | Track B |
| 6 | B\ | Track B inverted (RS422 only) |
| 7 | N | Zero pulse (optional) |
| 8 | HW gate | 24 V input — leave open if SW gate only |
| 9 | DQ0 | Module output 0 (unused in this design) |
| 10 | DQ1 | Module output 1 (unused in this design) |
Diagnostic Status Word and Error Code Matrix
The status word (PIW 272) carries both operational state and diagnostic information. Read it every cycle and route the relevant bits to HMI:
| Bit | Symbol | Meaning | Diagnostic action |
|---|---|---|---|
| 0 | STS_RUN | Counting active (SW + HW gate open) | — |
| 1 | STS_DIR | 0 = up, 1 = down | — |
| 2 | STS_CMP1 | DQ0 comparator match flag (sticky until read) | Diagnostic, not used for control |
| 3 | STS_CMP2 | DQ1 comparator match flag (sticky until read) | Diagnostic, not used for control |
| 4..5 | — | Reserved | — |
| 6 | STS_GATE | Internal gate state (1 = open) | — |
| 7 | STS_OV | Overflow / underflow of the 32-bit counter | Reset ZeroOffset; investigate encoder direction |
| 8 | STS_LD_CMP_ACK | Comparison values latched into the comparator | — |
| 9 | STS_LD_VAL_ACK | Load-value (preset) latched | — |
| 10..12 | — | Reserved | — |
| 13 | STS_WIREBRK | Wire break on A, B, or N | Check encoder and cable; clear after repair |
| 14 | STS_SENS_SUP | Sensor supply fault (short / overload) | Remove load on pin 1, check 24 V |
| 15 | STS_ERROR | Group fault (RAM/ROM/parameter) | Cycle power, re-parameterise if persistent |
Mapping to SFCs: STS_WIREBRK and STS_SENS_SUP are also reported as diagnostic interrupt (OB82). STS_OV is purely status; it must be handled in user logic because the counter is read-only and the FM350-1 cannot be configured to auto-preset on overflow. The standard handling is exactly the ZeroOffset snapshot pattern shown above — increment ZeroOffset by 2 147 483 647 (or the negative equivalent) when STS_OV is observed, so that UserCount continues monotonically across the wrap.
Edge Cases: 32-bit Overflow, Cycle Time, and Reset Storms
1. 32-bit overflow. At 500 kHz the raw 32-bit counter wraps in roughly 4 294 s of uninterrupted counting. For most positioning applications this is irrelevant, but on a continuously rotating spindle running for hours it is real. The recommended handling is to subscribe to STS_OV and add a 2 147 483 647 DINT correction to ZeroOffset in a one-shot. UserCount stays continuous.
2. OB1 cycle time. The software comparator introduces a ±1 OB1 cycle of jitter on the output firing instant. For a 10 ms cycle and a 500 kHz encoder this is 5 000 counts of uncertainty. If the application requires sub-cycle accuracy, route UserCount through a hardware-supported interrupt (OB40) driven by a 100 µs timer, or replace the FM350-1 with the modern ET 200SP TM Count 1x24V (6AT1131-0CA04-0AA0), which exposes a sub-µs comparator output via PWM.
3. Reset storm. The self-reset pattern in segment 3 (DQ1_Out drives ResetTrig) is safe provided the reset one-shot is on the positive edge of ResetTrig only. If the user wires a level-driven reset, the counter is re-initialised every OB1 cycle while DQ1 is true, and UserCount oscillates between 5000 and ZeroOffset. The FP (rising edge) instruction in segment 1 is the entire protection.
4. Initial ZeroOffset on cold start. On the first OB1 scan after a CPU restart, ACT_CNTV may be non-zero (the module retains its count through PLC stop). The user code must initialise ZeroOffset to ACT_CNTV on first scan. Use a startup flag (e.g. OB100 sets ZeroOffset := ACT_CNTV) or check the "FirstScan" bit in OB100.
5. Negative UserCount after a forward-running reset. If the encoder keeps moving forward for 1 cycle between the DQ1 fire and the OB1 cycle that runs segment 1, UserCount is briefly 1…N before the new ZeroOffset takes effect. Most applications tolerate this. If not, latch DQ1_Out for one extra cycle.
Commissioning Checklist and Verification Procedure
- HW Config check. Open the FM350-1 properties dialog; confirm Outputs = "No function" and SW gate enabled. Save and download to the CPU.
-
Process image check. Open a VAT table online, force
PID 276(ACT_CNTV) into a watch view. Hand-turn the encoder and verify the value increments in x1, x2, or x4 as configured. - Status word check. Watch PIW 272. Bit 0 (STS_RUN) should be 1 after the SW gate is opened. Bit 13 (wire break) should be 0 with the encoder wired.
- ZeroOffset path test. Set a breakpoint in segment 1; trigger ResetTrig; confirm ZeroOffset takes the value of ACT_CNTV, and that ACT_CNTV is unchanged after the trigger.
- UserCount test. Turn the encoder slowly. UserCount must track 1, 2, 3 … continuously. Reset at any value; UserCount must jump to the new offset without dropping the next count.
- Comparator test. Drive SetPoint1 to 100; turn the encoder 200 counts. Confirm DQ0_Out is 1 between UserCount = 100 and the next reset. Repeat for SetPoint2 / DQ1_Out.
- Pulse-loss test. At 100 kHz and an OB1 of 10 ms, run the cycle for 1 hour. Compare the number of DQ1 events with a reference counter fed by the same encoder; both should agree to within 1 count per cycle (≈ 1 count / 10 ms at the comparator, none elsewhere).
- OB82 diagnostic test. Unplug the encoder cable. Confirm OB82 fires, STS bit 13 goes high, and the SF LED on the FM350-1 is lit. Reconnect, acknowledge the diagnostic, and verify STS bit 13 clears.
Why does my FM350-1 keep ACT_CMP1 and ACT_CMP2 at zero after I write CMP_V1 and CMP_V2?
ACT_CMPx are not mirrors of CMP_Vx; they are status flags indicating the values the on-board FPGA has latched. They stay at 0 until the SW gate is open, the HW gate is open, the output is enabled in HW Config, and the LD_CMP bit in the control word has been pulsed and acknowledged (STS bit 8 = 1). If you do not need the module's hardware comparator, disable the outputs in HW Config and implement the comparator in the CPU — read-back issues disappear entirely.
How do I reset the FM350-1 counter without losing encoder pulses?
Do not touch the FPGA counter register. Instead, snapshot ACT_CNTV (PID 276) into a DINT called ZeroOffset on the rising edge of your reset request, and compute UserCount = ACT_CNTV − ZeroOffset on every OB1 cycle. The raw counter never resets, no pulses are dropped, and the application sees a virtual counter that can be "reset" instantly to any value.
Can I still use the FM350-1 on-board DQ0 and DQ1 outputs with this software-comparator pattern?
Yes, but it is rarely useful. The DQ0 / DQ1 outputs are bonded to the on-board comparators and only fire on hardware-equal matches. With the software pattern you normally drive general-purpose digital outputs of the CPU (e.g. a SM 322 DO32). Only keep DQ0 / DQ1 in play if you specifically need the sub-µs hardware latency and your application tolerates the ACT_CMPx read-back configuration discipline.
What is the correct STL opcode for subtracting a 32-bit counter value?
Use -D (double-integer subtraction), not -I. ACT_CNTV and ZeroOffset are 32-bit DINT; -I truncates to 16 bits and produces wrong results above 32 767 counts. The full three-instruction pattern is L PID 276; L "ZeroOffset"; -D; T "UserCount".
Does the FM350-1 configuration software need to be installed separately?
Yes. The FM 350-1 / FM 450-1 configuration package is a separate install on top of STEP 7 V5.x. Without it, the FM350-1 does not appear in HW Config and the parameterisation dialog is missing. The package is documented in Siemens KB article 28291262 and is required for any project that uses the module on S7-300 or ET 200M.
How do I handle the FM350-1 32-bit overflow in a continuous-rotation application?
Watch the STS_OV bit (PIW 272, bit 7). When it sets, add 2 147 483 647 (or the negative equivalent) to ZeroOffset in a one-shot. UserCount stays continuous across the wrap. Do not attempt to "reset" the FM350-1 to recover from overflow — that re-introduces the pulse-loss problem.