Problem Overview: S7-300 Enters STOP from a BCD Conversion Fault
An S7-300 CPU can be forced into the STOP operating state by a programming error raised inside a code block. One of the most common triggers is an invalid input to a BCD (Binary-Coded Decimal) conversion instruction such as BTI (BCD-to-Integer, 16-bit) or BTD (BCD-to-Double-Integer, 32-bit). When the operand word or double word fed into the conversion contains a nibble outside the legal range 0..9 (i.e. A..F hexadecimal) the CPU raises a programming error and, unless an error OB is loaded and active, transitions to STOP.
This article documents the recovery path for the exact symptom reported in the field: an S7-300 CPU halts after an unhandled error inside FC32, where a BCD conversion is being executed. The value being processed originates from a counter (C0) that has been moved into a data block word (DB1.DBW0) and then handed to a function whose BCD conversion is failing. The result is a CPU in STOP, a logged event in the diagnostic buffer, and a process that has lost control until the operator intervenes.
RUN-P, or modifying process I/O, confirm that the controlled equipment is in a safe state. Field devices driven by this CPU may be de-energized, frozen, or hold last value depending on the CPU's Output tab configuration in STEP 7 HW Config.Background: How BCD Conversion Is Implemented on the S7-300
BCD is a numeric encoding in which each decimal digit occupies exactly four bits (a nibble). The S7-300 / S7-400 family implements BCD conversion through four dedicated instructions in the STL/Statement List and LAD/Ladder editors:
| Mnemonic | Operation | Source width | Result width | Range |
|---|---|---|---|---|
BTI |
BCD to Integer | 16 bits (WORD) | 16 bits (INT) | -999..+999 |
ITB |
Integer to BCD | 16 bits (INT) | 16 bits (WORD) | -999..+999 |
BTD |
BCD to Double Integer | 32 bits (DWORD) | 32 bits (DINT) | -9999999..+9999999 |
DTB |
Double Integer to BCD | 32 bits (DINT) | 32 bits (DWORD) | -9999999..+9999999 |
The sign of a BCD number is encoded in the most-significant nibble: 0000 = positive, 1111 = negative. The remaining 15 bits (BTI/ITB) or 31 bits (BTD/DTB) hold three (or seven) packed decimal digits. A conversion fails whenever any data nibble has a value of 10102 through 11112 (i.e. A..F), which has no decimal interpretation.
When the conversion instruction detects an illegal nibble, the CPU writes a programming-error event into the diagnostic buffer and calls OB121 (programming error). If OB121 is not present in the CPU's block container, the default reaction is to switch the CPU to STOP.
Why FC32 Stops the CPU: Trace of the Failing Data Path
The fault described in the field is rooted in a data flow that crosses two boundaries: counter-to-DB and DB-to-FC. A typical implementation looks like this in STEP 7:
- A counter
C0accumulates pulses from an input (often an up/down count from an incremental encoder or a stepper position register). - The current count value is transferred into a shared data word such as
DB1.DBW0using a ladder rung such as:L C0
T DB1.DBW0 - A function
FC32readsDB1.DBW0and feeds it into aBTIorBTDinstruction, expecting the data to be BCD-encoded because downstream it drives a seven-segment display or a BCD-only output module.
Two distinct root causes must be ruled out at this point:
-
Counter value not BCD.
C0holds a binary (INT) count, not a BCD value. If the FC was designed on the assumption that the value was already BCD-encoded, the conversion is meaningless and any non-decimal nibble in the binary representation will trip the error. -
Counter rolled out of BCD range. Even if the source is genuine BCD, a 16-bit BCD count is limited to
+999forBTIor-9999999..+9999999forBTD. A count of1000in BCD requires a sign-bit pattern (upper nibble) that is no longer legal as a count field, and the conversion will fault.
Reading the Diagnostic Buffer for the Exact Cause
Before editing the program, capture the diagnostic buffer of the S7-300 CPU. In STEP 7 (SIMATIC Manager) or TIA Portal (when the project has been migrated), open the online view of the CPU and select PLC > Diagnostics/Setting > Diagnostic Buffer (TIA: Online & diagnostics > Diagnostics buffer).
The most recent events are listed at the top. A BCD conversion fault produces a programming error with the following characteristic fields:
| Field | Typical content for a BCD fault |
|---|---|
| Event ID |
0x2570 (Programming error) or 0x35xx family entries |
| OB |
OB 1 requested at time of fault (priority class 1) |
| Block | FC32 |
| Reaction | CPU goes to STOP (no OB121 loaded) |
| Error location | Address in the block plus a relative byte offset identifying the failing instruction |
Record the faulted block, the relative address inside the block, and the priority class. With the address, you can navigate the source listing of FC32 in the offline project (or, if only the online project is available, in the editor after an upload to PG) and locate the specific BTI/BTD/ITB/DTB instruction that raised the error.
0x2522 (area length error) or 0x2534 (DB not loaded), the cause is a memory access problem, not a BCD conversion problem. Treat the symptom as a different fault class.Understanding the Error OB Hierarchy on S7-300
The S7-300 supports a fixed set of error and interrupt OBs. According to the official Siemens documentation for the system diagnostics of S7-300 / S7-400, the CPU calls a specific OB when it detects each class of error. If the corresponding OB is not present, the CPU enters STOP.
| Error class | Trigger | OB called | Default if OB missing |
|---|---|---|---|
| Programming error (e.g. invalid BCD) | Bad operand, illegal nibble in BCD conversion, divide by zero | OB 121 | CPU → STOP |
| I/O access error (1st cycle) | Faulty module access during startup | OB 121 (or OB 85) | CPU → STOP |
| I/O access error (run time) | Faulty module access during cyclic execution | OB 122 | CPU → STOP |
| Insert/remove module | RSE event | OB 83 | Depends on CPU "STOP on RSE" setting; can also stop |
| Priority class error | OB not loaded, OB execution fault | OB 85 | CPU → STOP |
The behavior of OB 85 is particularly relevant: per the official Siemens reference, with all other error events of OB 85, the CPU changes to STOP. This means that fixing one error class by loading OB 85 is not sufficient; the operator must load the specific OB that matches the error (in this case, OB 121) to suppress the STOP reaction. Always load the most specific OB that covers the actual fault.
Loading OB 121 by itself does not repair the underlying logic; it only prevents the CPU from stopping. The block must be instrumented to set a fault flag, increment an error counter, and force a safe fallback value. See the Preventive Measures section below for an implementation pattern.
Step-by-Step Recovery Procedure
The recovery sequence below assumes that the CPU is currently in STOP and that the project archive is available on the programming device.
- Place the machine in a safe state. Disable drives, isolate hazardous motion, and follow the site lock-out/tag-out procedure. The CPU may force outputs to a defined state on STOP, but field confirmation is required.
- Read the diagnostic buffer. Open the online connection to the CPU and dump the diagnostic buffer to a text file. Identify the block, the relative address of the failing instruction, and the event ID.
- Upload the project from the CPU. Use PLC > Upload Station to PG in STEP 7 (or the equivalent in TIA Portal) to bring the live blocks onto the PG. This guarantees that the offline copy you will modify is identical to the running program.
-
Inspect
FC32. Locate theBTI/BTD/ITB/DTBinstruction at the address reported in the buffer. Trace the input operand back to its source. Document whether the input is a counter value, an analog input scaled into a word, or a value moved from a DB. -
Confirm the assumption mismatch. If the input to the BCD instruction is a binary integer (typical for
C0), decide whether the application really needs a BCD conversion. If the destination is a numeric display or a BCD-only output module, you must convert the binary value to BCD first withITBorDTBafter applying range limits. -
Apply range guarding. Clamp the source value to the legal BCD range before the conversion. For a 16-bit BCD count, the legal range is
0..999(or-999..+999for signed). Anything outside must be saturated, faulted, or routed to a different code path. -
Replace the bad conversion. Edit the rung to perform the correct conversion. If the upstream source is a binary counter and the downstream is a numerical display, use
L C0, apply range limits withL 999and<I/>Ichecks, and only then callDTBon a double-word that was previously loaded with the count. -
Add an
OB 121handler (recommended). Even with the logic corrected, load a minimalOB 121that increments a fault counter and sets a retentive flag. This converts future similar errors from a process-stopping fault into a logged event that operators can review. -
Compile and download. Compile
FC32andOB 121. In HW Config, ensure the CPU is inSTOPbefore downloading. After the download, the CPU will remain inSTOPuntil you switch it back toRUN-P. -
Restart and verify. Switch the CPU to
RUN-P. Watch the diagnostic buffer for any new entries and monitorDB1.DBW0with a watch table while exercising the counter input.
Replacing the Faulty Rung: Worked Example
Below is a STL snippet that demonstrates a corrected version of a typical fault path. The original code (assumed) is:
// Original (faulty): assumes C0 is already BCD
L C0
T DB1.DBW0
CALL FC32 // BTI on DB1.DBW0 -> fault when C0 > 999
The corrected code guards the range, performs an explicit binary-to-BCD conversion only when the value is in range, and signals a fault otherwise:
// Corrected
L C0
T DB1.DBW0 // binary count kept in DB1.DBW0
L 999
>I // C0 > 999 ?r>= M10.0 // out-of-range flag
SPB OUT1
L DB1.DBW0
ITB // C0 in range, convert INT to BCD
T DB2.DBW0 // BCD value for display / output module
SPA CONT
OUT1: L 0
T DB2.DBW0 // safe default
S M10.0 // latch out-of-range for HMI alarm
CONT: NOP 0
Key principles in this rewrite:
- Separate the binary value (
DB1.DBW0) from the BCD-encoded value (DB2.DBW0). Mixing them in a single DBW is a frequent source of confusion during commissioning. - Guard the range before the conversion.
BTIandBTDwill fault on values outside the BCD-legal range;ITBandDTBfault when the source integer is out of the representable BCD range. - Always provide a safe default value. A value of
0is rarely a process-safe default; for displays, blank the value (L 0, T DB2.DBW0is acceptable because BCD0displays as000); for control outputs, hold the last good value and set an alarm.
Alternative Cause: Mixed Word Widths
A second, frequently missed cause of BCD conversion errors is a width mismatch. A 16-bit BCD variable (BTI/ITB) holds three digits. A 32-bit BCD variable (BTD/DTB) holds seven digits. If a value is loaded with L DB1.DBW0 and then passed to a BTD, the upper word of the 32-bit accumulator is whatever was in ACCU 1 from the previous operation. If the previous operation left a non-zero high word with non-decimal nibbles, the conversion faults.
Always clear the high word explicitly when widening:
L 0
T DB1.DBW2 // clear high word of the 32-bit slot
L DB1.DBW0
T DB1.DBW2 // actually load low word
L DB1.DBW2
BTD // now safe to convert
Or, equivalently, use the symbol-absolute addressing form L DB1.DBD0 (double-word at byte offset 0) so the CPU implicitly zero-extends the upper word for unsigned interpretation. Be aware that L DBD does not sign-extend; for signed values use ITD first, then DTB.
Verification: Proving the Fix Before Returning to Production
After the corrected program is loaded, perform the following checks before handing the line back to operations.
| Check | Method | Pass criteria |
|---|---|---|
CPU stays in RUN during normal operation |
Watch the mode selector for at least 30 minutes of representative process activity | Mode remains RUN; diagnostic buffer is empty of new programming errors |
| Counter rolls past 999 do not fault | Force C0 to 1000, 1500, 9999 in the watch table, then cycle the CPU to RUN
|
Display holds at the saturated value; M10.0 (out-of-range) is set; no STOP
|
| Counter rolls negative do not fault | If the counter is configured as up/down, force to -1 and observe | Value displayed as expected; sign bit handled correctly |
| Outputs respond correctly | Trigger a known step in the analog output described in the original incident and verify the output moves | Output follows expected up/down trajectory; no hold-last-value |
| OB 121 fault counter is zero | Monitor the fault counter from OB 121 via watch table |
Counter does not increment for a full production shift |
Preventive Measures: Building BCD-Safe Code From the Start
The cheapest BCD error to fix is the one that never happens. Apply these rules across the project, not only in FC32:
- Use one DB for binary, another for BCD. Never mix BCD and binary encodings in the same data word, even at different offsets of a DB. Engineers reading the code six months later will not remember the convention.
-
Validate ranges at the source. Any integer that may be converted to BCD must be range-checked immediately after it is written. Use
<I,>I,<D,>Dfor the comparison family and branch to a saturation / fault handler. -
Load a minimal
OB 121in every project. A 12-lineOB 121that increments a fault counter and stores the block type and number from its local data is enough to convert a stop-the-line fault into a logged event that is visible on the HMI. -
Document the encoding in the symbol table. Append
_BINor_BCDto every symbol that holds a BCD-encoded value. This catches accidental cross-references during code review. - Commission the worst case. During FAT/SAT, force the counter to its maximum and minimum values and verify the conversion path. This is the only way to confirm that the range guards work under the actual scan pattern, not just the test cases the developer thought of.
Related Faults That Look Like a BCD Error
Before closing the ticket, confirm that the diagnostic buffer points to a programming error and not to one of these lookalikes:
| Symptom | Likely real cause | How to confirm |
|---|---|---|
CPU stops on first scan with a programming error in FC32
|
DB1 not loaded or too short; DB1.DBW0 is not present |
Diagnostic buffer event 0x2534 (DB not loaded) or 0x2522 (area length error) |
| CPU stops intermittently, always in the same scan | Counter value is exactly out of range at one specific process step | Add a watch table entry for DB1.DBW0; correlate with the process step |
| CPU stops after a specific I/O module replacement | RSE event in OB 83 with "STOP on RSE" enabled in HW Config |
Diagnostic buffer shows 0x3954 or 0x3858 insert/remove events |
CPU stops but no FC32 in the project |
Different block; FC number drift between offline and online | Compare offline block list to online block list with PLC > Compare |
FAQ
What BCD conversion instruction in the S7-300 raises a programming error?
The BTI, BTD, ITB, and DTB instructions raise a programming error when the source operand contains nibbles outside the decimal range 0..9 or when the value is outside the representable BCD range (e.g. > 999 for BTI or > 9999999 for BTD).
Why does an S7-300 CPU go to STOP on a BCD error in FC32?
The CPU calls OB 121 (programming error) when a BCD conversion fails. If the project does not contain OB 121, the default reaction is to transition the CPU to STOP. Loading an OB 121 handler prevents the stop but does not fix the underlying data problem.
How do I find which instruction in FC32 caused the STOP?
Open the online diagnostic buffer of the CPU and read the programming-error event. The buffer records the block (FC32), the relative address inside the block, and the priority class of the OB that was active when the fault occurred. Combine that with a source view of FC32 in STEP 7 to identify the exact BTI/BTD/ITB/DTB instruction.
Can a counter value (C0) be used directly as input to a BCD conversion?
No. A counter holds a binary integer, not a BCD value. To present a count on a BCD-only display or output module, you must range-check the binary count to 0..999 (or -999..+999 for signed) and then apply ITB or DTB to encode it as BCD. Feeding the raw binary count into BTI or BTD will fault as soon as any nibble is non-decimal.
Do I need OB 121, OB 122, and OB 85 in the same project?
For robust operation, yes. OB 121 handles programming errors (including BCD conversion faults), OB 122 handles I/O access errors during run time, and OB 85 handles priority-class errors. The official Siemens documentation for S7-300/S7-400 error OBs notes that with most OB 85 error events the CPU changes to STOP if the OB is absent, so loading each relevant OB is a low-cost safety net.