Resolving S7-300 BCD Conversion Error from Profibus Drive Data

David Krause16 min read
ProfibusSiemensTroubleshooting
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

Problem Statement and Field Symptoms

On a SIMATIC S7-300 station built around a CPU 313C-2DP, an automation cell receives one 16-bit Process Data word (PZD) from a variable-frequency drive (VFD) over Profibus DP. The high byte holds a PID controller output scaled 0–100 % and the low byte holds drive status bits (run, fault, cam, etc.). The PZD lands in DB100.DBW60 and the engineer splits it into two markers with two MOVE boxes:

  • MOVE DB100.DBB60 → MW140
  • MOVE DB100.DBB61 → MW142

While the machine is running the SF (System Fault) LED on the CPU is steady red. When the machine stops, the SF LED clears. STEP 7's diagnostic buffer records a BCD conversion error at intervals that match the drive's cycle time. The MOVE boxes are not BCD instructions, so the source of the error is not where the symptom looks.

This article walks through diagnosis of the BCD conversion error on the S7-300 CPU 313C-2DP family, the memory-layout pitfall that makes the symptom look like a MOVE problem, and a field-proven implementation for splitting the Profibus PZD word safely into two bytes without triggering the CPU's BCD check.

Applicable Hardware and Software

Confirm the part numbers and firmware before applying this procedure. The 313C-2DP catalog number stamped on the front of the module determines which manual and which firmware behavior applies:

Catalog Number Description Firmware
6ES7 313-6CF03-0AB0 CPU 313C-2DP, 64 KB work memory, integrated Profibus DP master V2.6.x
6ES7 313-6CG04-0AB0 CPU 313C-2DP successor, 64 KB work memory V3.3.x
6ES7 313-5BG04-0AB0 CPU 313C (no Profibus) – reference only V3.3.x
6ES7 313-6BG04-0AB0 CPU 313C-2 PtP – reference only V3.3.x

CPU 313C-2DP specifications that matter for this fix:

  • Bit execution: 0.07 µs (CF03), 0.06 µs (CG04)
  • Integrated Profibus DP master/slave: 9.6 kbps to 12 Mbps
  • Digital I/O: 16 DI / 16 DO integrated
  • Analog I/O: 4 AI / 2 AO integrated (11-bit + sign)
  • Integrated counters: 3 channels up to 30 kHz
  • Programming: STEP 7 V5.5 + SPx or STEP 7 (TIA Portal) V13+

Reference: SIMATIC S7-300 CPU 31xC and 31x: Manual.

Understanding the BCD Conversion Error

Binary Coded Decimal encodes each decimal digit in its own 4-bit nibble. The S7-300 CPU's BCD conversion family (BTI, ITB, BTD, DTB, BCD_I, I_BCD) expects every nibble in the source operand to be in the legal range 0000b to 1001b (decimal 0 to 9). When the CPU encounters any nibble in the illegal range 1010b to 1111b (decimal 10 to 15), the conversion result is undefined, the BR (binary result) bit is cleared, and the CPU raises a programming error.

Per the SIMATIC S7-300/400 standard function reference, the programming error is reported as:

  • Fault code in the diagnostic buffer: 2521 (decimal) / 0x09D9 (hex) for BCD conversion error
  • Fault class: programming error
  • OB invoked: OB121 (Programming Error OB) if present
  • SF LED: lit while the error is unacknowledged

Reference: STEP 7 V5.5 System and Standard Functions Reference Manual, section on programming-error OBs.

Critical: A BCD conversion error does not mean the offending instruction is a BCD instruction you can see. The CPU reports the BCD instruction that just executed and failed. The MOVE two networks up is innocent; the CPU reports the instruction that read the illegal nibble, not the instruction that copied the value into a marker.

Why the Symptom Tracks Machine Run

The status byte from the drive contains bits that represent "running," "fault present," "at setpoint," etc. During machine stop the drive is in standby; most of those bits are 0 and the byte carries a low value such as 0x00, 0x01, or 0x02. None of those low values contain nibbles 0xA–0xF, so a BCD conversion on a stopped byte quietly succeeds.

During machine run, the drive sets bits that produce values such as 0x4F (running, at setpoint, fault latch cleared), 0xB7 (run enable plus warning), or 0xF0 (full status word). The 0xF in any of these nibbles triggers the BCD conversion error the moment another part of the program passes the byte through a BCD instruction. The SF LED lights in step with the run state because the data only becomes illegal when the drive is active.

Where the BCD Conversion Actually Lives

The MOVE boxes from the field report (DB100.DBB60 → MW140, DB100.DBB61 → MW142) are innocent. MOVE on S7-300 is a pure byte copy and never raises a BCD error. The fault is in one of the following locations:

  1. Hidden BCD conversion block: A legacy FB written against an older drive mapping may still issue a BTI on the data block word before splitting. Search the project for BTI, BCD_I, ITB, I_BCD, BTD, DTB.
  2. S5 timer / counter preset: S5 timers (SP, SE, SD, SS, SF) and S5 counters (S_CD, S_CU, S_CUD) load their preset values in BCD. Loading a non-BCD marker word into a preset triggers the same fault.
  3. HMI scaling block: A scaling FB that publishes status to the panel via a BCD conversion that was incorrectly retained from a S5-era codebase.
  4. Cross-block pointer arithmetic: A POKE or area-crossing move on the marker word that effectively transfers the byte into a region later used by a BCD instruction.

Reading the Diagnostic Buffer on the 313C-2DP

Use STEP 7 to query the CPU's diagnostic buffer, which is the authoritative record of faults:

  1. Open STEP 7 and go online (PLC > Connect to Target System > Accessible Nodes).
  2. Select the 313C-2DP from the discovered nodes.
  3. Open PLC > Module Information > Diagnostic Buffer.
  4. Read the most recent event of class Programming error. Look for "BCD conversion error" in the event text. Note the block number, block type (FB/FC/OB), and relative instruction offset shown in the event detail.
  5. Click the Open Block button to navigate STEP 7 directly to the failing network.
  6. Save the diagnostic buffer for the maintenance log: PLC > Module Information > Save As.

Reference: SIMATIC S7-300 Programming with STEP 7 V5.5, chapter on diagnostics and fault handling.

OB121 Local Variables for BCD Conversion Errors

OB121 receives fault information in its local temporary variables. Reading these variables in a logging block turns OB121 into a passive fault-trap. The variables of interest for a BCD conversion fault are:

Local Variable Type Meaning on BCD Error
OB121_EV_CLASS BYTE Event class: B#16#35 (incoming fault)
OB121_SW_FLT BYTE Fault identifier (BCD conversion = 0x21 / 33)
OB121_PRIORITY OB_PRIORITY Priority of OB121 in the running priority class
OB121_OB_NUMBR OB_NUMBER 121
OB121_BLK_TYPE BYTE Block type where fault occurred: 0x08 = OB, 0x0A = DB, 0x0C = FC, 0x0E = FB
OB121_BLK_NUM WORD Number of the failing block
OB121_PG_OFFSET WORD Relative instruction offset of the failing instruction
OB121_FLT_REG DWORD Contains the illegal BCD value read by the failing instruction

Sample OB121 stub for logging the failing block to a watch DB:

FUNCTION_BLOCK FB_OB121_Logger
// Diagnostic stub - drop into OB121 to capture BCD fault context.

VAR_TEMP
  info AT : STRUCT
    EV_CLASS  : BYTE;
    SW_FLT    : BYTE;
    PRIORITY  : BYTE;
    OB_NUM    : BYTE;
    RESERVED1 : BYTE;
    RESERVED2 : BYTE;
    BLK_TYPE  : BYTE;
    RESERVED3 : BYTE;
    BLK_NUM   : WORD;
    PG_OFFSET : WORD;
    FLT_REG   : DWORD;
  END_STRUCT;
END_VAR

BEGIN
  // info pointer is bound by STEP 7 to OB121's local stack
  DB_BCDFaultLog.SW_FLT     := info.SW_FLT;
  DB_BCDFaultLog.BLK_TYPE   := info.BLK_TYPE;
  DB_BCDFaultLog.BLK_NUM    := info.BLK_NUM;
  DB_BCDFaultLog.PG_OFFSET  := info.PG_OFFSET;
  DB_BCDFaultLog.FLT_REG    := info.FLT_REG;
END_FUNCTION_BLOCK

Cross-Referencing Every BCD Instruction in the Project

With OB121 in place, run the following audit against the offline project:

  1. Open the S7 program and select the Blocks container.
  2. Right-click and choose Reference Data > Display. Generate the cross-reference list.
  3. Filter by symbol or operand pattern DB100.DBW60, DB100.DBB60, DB100.DBB61, MW140, MW142, and any of the legacy marker region around them.
  4. Open every block in the resulting list and search inside the source for BTI, BCD_I, ITB, I_BCD, BTD, DTB. STEP 7's cross-reference does not flag BCD conversions as data-flow violations; only a manual scan will.
  5. Search for S5 timer / counter operations on the same markers: SP, SE, SD, SS, SF, S_CU, S_CD, S_CUD.
  6. For each occurrence, decide: (a) remove the BCD conversion, (b) replace it with type-correct integer conversion, or (c) add range validation before the conversion.

Memory Layout: Words, Bytes, and the Marker Overlap Trap

S7-300 marker memory is bit-addressable. Markers MB0 through MB2047 form the working area, and words are simply pairs of adjacent bytes. MW140 occupies MB140 (low byte) and MB141 (high byte). MW142 occupies MB142 and MB143. The bytes do not overlap because the marker word indices are even/odd, but if you reuse MW141 anywhere in the program, you corrupt the high byte of MW140.

The MOVE from DB100.DBB60 to MW140 writes only MB140. The MOVE from DB100.DBB61 to MW142 writes only MB142. The high bytes MB141 and MB143 retain whatever value they had before. If the program reads MW140 as a 16-bit word expecting a clean low byte, it can be confused by stale data in MB141.

If you need the marker word to be clean (low byte only, high byte zero), explicitly clear the high byte before the move, or use the STL mask pattern shown in the next section.

Step-by-Step: Splitting DB100.DBW60 into Two Marker Words

The procedure below uses an existing FC (here named FC100) and produces clean marker words without BCD conversion:

  1. Open the S7 program in STEP 7 and locate the FC that handles the Profibus data from the drive. If no such FC exists, create FC100.
  2. Insert the STL network below at the top of FC100 so it runs each OB1 cycle.
  3. Compile the FC and download the modified block to the 313C-2DP.
  4. Place the CPU in RUN with the drive enabled.
  5. Open a VAT (Variable Table) and add MW200, MW140, MW142. Force MW200 from the VAT to known values and confirm the masks work as expected.
  6. Drive a known pattern through DB100.DBW60 from the drive (or the simulation) and verify MW140 / MW142 track.

STL Implementation

// FC100 - Split DB100.DBW60 into two marker words
//   MW140 = low byte  (drive status flags)
//   MW142 = high byte (PID output 0..100 %)

      L     DB100.DBW60        // Load full PZD word
      T     MW200              // Save full word for masking

      L     MW200              // Reload full word
      L     W#16#00FF          // Mask = 0x00FF keeps low byte only
      UW                        // AND word
      T     MW140              // Status flags

      L     MW200              // Reload full word
      L     W#16#FF00          // Mask = 0xFF00 keeps high byte only
      UW                        // AND word
      SRW   8                  // Shift right 8 bits to align
      T     MW142              // PID output 0..100

      NOP   0

LAD / FBD Implementation

In LAD or FBD, two MOVE boxes suffice when the destination marker word is otherwise unused:

[ MOVE ]       DB100.DBB60    -->    MB140
[ MOVE ]       DB100.DBB61    -->    MB142

If the high bytes MB141 and MB143 are reused elsewhere, follow each MOVE with a word MOVE that copies the byte into a clean word:

[ MOVE ]       DB100.DBB60    -->    MB140
[ MOVE ]       0              -->    MB141
[ MOVE ]       DB100.DBB61    -->    MB142
[ MOVE ]       0              -->    MB143

SCL Implementation

// SCL fragment - type-safe split
VAR_TEMP
  pzd_word  : WORD;
  status_b  : BYTE;
  pid_b     : BYTE;
END_VAR

pzd_word := "DB_Profibus".DBW60;
status_b := pzd_word AND W#16#00FF;            // low byte
pid_b    := WORD_TO_BYTE(SHR(IN := pzd_word AND W#16#FF00, N := 8));

// Publish to markers (MOVE equivalent)
MB140 := status_b;
MB142 := pid_b;

Handling Drive Mapping You Cannot Change

In the field case the drive's parameter mapping is locked (drive engineer off-site). The byte split must accommodate the existing mapping without modifying the drive. The pure masking approach above does exactly that: it does not touch the Profibus PZD structure or the data block layout. Only the marker consumption pattern changes.

If the receiving program needs the PID output as a 0–100 % normalized integer rather than a byte, the second stage converts the high byte to a 0–27648 standard PROFIdrive scaling. Avoid BTI in this stage; use direct integer arithmetic:

// PID output in 0..100 percent from high byte, no BCD
L     MW142              // PID output 0..100
L     100                // divisor (engineering unit max)
*I                        // INT * INT, no BCD
T     MW300              // result in engineering units

Removing the BCD Conversion Without Losing Function

Once the failing block and instruction are identified, three replacement strategies work in practice:

Strategy 1: Delete the conversion

If the original programmer's intent was only to display the status byte, no BCD conversion was ever needed. Status words from drives are bit patterns and should be parsed bitwise:

// Bit-parse the status byte
A     MB140.0     // drive running
=     M 100.0
A     MB140.1     // drive faulted
=     M 100.1
A     MB140.2     // at setpoint
=     M 100.2
// ... continue per drive manual

Strategy 2: Replace with INT conversion when needed for HMI

For HMI display of the PID output byte, use a direct integer move. The PID output byte is a small integer (0–100) and never triggers any BCD check on its own:

L     MB142              // PID output byte
T     MW310              // integer-ready for HMI

Strategy 3: Range-validation wrapper

If a BCD conversion is genuinely required (for example, driving an S5-format timer preset), wrap the source value with a range check that limits it to 0–999 before the BCD instruction:

// Range-limit before BCD conversion
L     MW142
L     999                // ceiling
>I                        // clip high: keep below 999
SPB   HIGH
TAK
L     0
>I                        // clip low: never negative
SPM   LOW
SPA   CONVERT
HIGH: L     999
SPA   CONVERT
LOW:  L     0
CONVERT:
BTI                       // now safe; nibbles all <= 9
T     MW320

Verifying the Fix on the Live CPU

  1. Download the modified blocks. Confirm STEP 7 reports no compile errors and the online / offline compare matches.
  2. Reset the CPU (MRES) if a programming error response is latched. Note: MRES clears work memory, not the diagnostic buffer; the diagnostic buffer remains until manually cleared via STEP 7.
  3. Place the CPU in RUN with the drive enabled.
  4. Open the diagnostic buffer. Confirm no new BCD conversion entries appear over a 30-minute observation window covering at least one machine cycle.
  5. Open a VAT and watch MW140, MW142, DB100.DBW60. Trigger known drive events (run, fault, reset) and confirm the marker values track.
  6. Cycle power the 313C-2DP and confirm the SF LED remains off after restart.
  7. If OB121 was added for diagnostics, leave it in place but switch the diagnostic logging DB to a write-only structure to minimize scan time.
Caution: Some 313C-2DP firmware revisions latch the SF LED until the CPU is restarted even after the underlying fault is cleared. Always perform a power cycle as the final verification.

Troubleshooting Matrix

Symptom Likely Cause Fix
SF LED lit, drive running, BCD error in buffer BTI/BTD/ITB on the status byte or word Locate offending block via OB121 / diagnostic buffer, replace or guard the BCD conversion
SF LED only while machine runs Status byte carries illegal BCD nibbles only in run state Mask the byte before any conversion; never BCD-convert status words
SF LED clear after STOP CPU clears pending programming error on STOP Add OB121 to keep diagnostic continuity across STOP/RUN transitions
SF LED latched after fix Firmware-revision-dependent latch behavior on CF03 / CG04 Power-cycle the 313C-2DP
MOVE of DBB to MW leaves stale high byte MOVE only touches the low byte of the destination word Use the STL mask pattern or move zero into the high byte
S5 timer preset with BCD conversion error Preset loaded with a non-BCD value Convert integer preset to BCD before the timer call, or use IEC timers (SFB3 / SFB4 / SFB5)
Marker overlap causes cross-block corruption MW / MB allocation not reviewed after split Generate cross-reference for the marker region and audit each usage
SF LED on, diagnostic buffer empty Diagnostic buffer was manually cleared or CPU is in restart Wait for next cycle and re-read; check PLC > Clear/Reset > Diagnostic Buffer

Standards Reference

BCD-conversion behavior is standardized in IEC 61131-3 for PLC programming languages. Siemens STEP 7 implements the BCD-related conversion functions consistent with this standard:

STEP 7 Name IEC 61131-3 Equivalent Behavior on Illegal Nibble
BTI BCD_TO_INT Programming error, BR = 0
ITB INT_TO_BCD Programming error if INT > 9999
BTD BCD_TO_DINT Programming error, BR = 0
DTB DINT_TO_BCD Programming error if DINT > 99999999

Reference: IEC 61131-3, Programmable controllers – Part 3: Programming languages, section on conversion functions.

Related Field Notes

  • The same fault pattern applies to the CPU 314C-2DP (6ES7 314-6CG03-0AB0 / 6ES7 314-6CH04-0AB0) and CPU 315-2DP / 315-2PN/DP family. The OB121 mechanism and BCD conversion behavior are common to the S7-300 line.
  • For SINAMICS G120 / S120 drives over Profibus DP using the PROFIdrive profile, the status word (ZSW1) is mapped at PZD1 and is a bit pattern, never BCD. The PROFIdrive standard forbids BCD interpretation of PZD words.
  • For drives that publish both control and feedback in a single packed word (as in this field case), confirm the byte order with the drive engineer before changing the masking pattern. Big-endian vs little-endian interpretations swap which byte is high and which is low.

FAQ

Why does the SF LED only appear when the machine is running?

The drive only writes illegal BCD nibbles (0xA–0xF) into the status byte while running. When stopped, the byte carries 0x00, 0x01, or 0x02, all of which are legal BCD. A BCD conversion on the running data triggers the fault; on the stopped data, it does not. This is why the SF LED tracks the machine run state directly.

Is the MOVE instruction actually at fault?

No. MOVE on the S7-300 is a pure byte copy and never raises a BCD conversion error. The diagnostic buffer points to a BCD instruction (BTI, ITB, BTD, DTB, BCD_I, I_BCD) in a different block. Search the project for these instructions against the data block region DB100.DBW60 and the marker region MW140 / MW142.

How do I find the failing block without a programmer?

Read the CPU's diagnostic buffer in STEP 7 (PLC > Module Information > Diagnostic Buffer). Each programming error entry includes the block number, block type, and relative instruction offset of the failing statement. Click "Open Block" to jump STEP 7 to the offending network. If OB121 is present, its local variables OB121_BLK_NUM and OB121_PG_OFFSET give the same data.

Should I add OB121 to the program or remove it?

For diagnosis, add OB121 (or a stub FB inserted into OB121) so the CPU does not transition to STOP on the first fault. Once the failing BCD conversion is removed, leave OB121 in place for future fault visibility, but switch the body to a lightweight log so it does not extend the OB1 scan.

What is the right CPU part number for the 313C-2DP used here?

The Profibus master variant is 6ES7 313-6CF03-0AB0 (firmware V2.6) or 6ES7 313-6CG04-0AB0 (firmware V3.3). Both expose integrated Profibus DP and respond identically to BCD conversion errors. Confirm the part number on the front of the module before applying any firmware-specific guidance.

Can S5 timers cause the same BCD error from a marker preset?

Yes. S5 timers and counters load their preset values in BCD. Loading a non-BCD marker word into the preset of an SP, SE, SD, SS, SF, S_CU, S_CD, or S_CUD operation triggers the same OB121 / BCD conversion error as a direct BTI instruction. Convert the preset to BCD before the call, or use the IEC equivalents SFB3 (TP), SFB4 (TON), and SFB5 (TOF), which accept integer presets directly.

Back to blog