How Do I Pack Four TwinCAT 3 SINT Bytes into One DINT?

Stefan Weidner5 min read
BeckhoffOther TopicPLC Programming
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

Four received SINT values occupy separate variables, but the application needs their four octets preserved as one 32-bit DINT. Follow the data from the producer into the input variables before choosing between explicit bit packing and a direct memory copy. The deciding factors are byte order, signed conversion, and whether the source octets are contiguous in memory.

Where does the four-octet data path stop?

The sender produces four octets. An input mapping or communication parser places them in four SINT variables or in ARRAY[0..3] OF SINT. The missing operation is at the application boundary: assigning each source octet to a defined byte position in the 32-bit destination.

Check the raw inputs before changing the packing code. Confirm that all four variables update, that no upstream conversion has changed their bit patterns, and that their order matches the sending specification. A correct packing routine cannot repair swapped or altered input bytes.

Source element Destination position Shift for low-byte-first mapping
asiSource[0] Bits 0-7 0
asiSource[1] Bits 8-15 8
asiSource[2] Bits 16-23 16
asiSource[3] Bits 24-31 24

This table defines an assumption, not a universal byte order. If the sender places the first octet in the most-significant position, reverse the mapping. Read the sender's field definition or inspect a known message to decide.

Which TwinCAT 3 packing method fits the data?

Method Byte order Signed-data risk Best use
Masked SHL and OR Explicit in the code Controlled by conversion and masking Protocol fields, mapped I/O, and portable logic
MEMCPY Follows source and target memory layout Copies bits without numeric conversion Contiguous data whose memory representation is already correct
Multiplication and addition Explicit through powers of 256 Signed promotion can affect the result Arithmetic formulation when operands are normalized first
SINT_TO_DINT alone Not applicable Converts the signed value One-value numeric conversion; it does not pack four octets

Use masked shifts and OR when the octets came from a protocol or device definition. The code documents the byte order and is independent of how an array happens to be represented in memory. Use MEMCPY only when the source is contiguous and its memory order is deliberately identical to the required destination representation.

Why can a direct shift-and-add expression fail?

A SINT is signed. When its high bit is set, conversion to a wider signed type can extend the sign bit into the upper positions. Shifting or adding that widened value can therefore set bits outside the intended eight-bit field. Mask every converted source with 16#000000FF before shifting.

Addition and bitwise OR produce the same packed pattern only when the four fields are clean, eight bits wide, and non-overlapping. OR better expresses the operation: combine independent bit fields. It also makes an omitted mask or overlapping field easier to identify during review.

Use SHL, not ROL. A left shift discards bits that leave the high end. A rotate returns those bits at the low end, which can corrupt a field after intermediate values become wider or already contain high-order bits. A rotation may appear to work for a particular input pattern, but its circular behavior is not part of byte packing.

How should the four SINT values be packed explicitly?

The following Structured Text implements the low-byte-first mapping shown above. Each source is widened, reduced to its original eight-bit pattern, shifted into position, and combined with OR.

diTarget :=
    (SINT_TO_DINT(asiSource[0]) AND 16#000000FF)
    OR SHL((SINT_TO_DINT(asiSource[1]) AND 16#000000FF), 8)
    OR SHL((SINT_TO_DINT(asiSource[2]) AND 16#000000FF), 16)
    OR SHL((SINT_TO_DINT(asiSource[3]) AND 16#000000FF), 24);
  1. Capture the four source octets before packing so they belong to the same message or I/O update.
  2. Determine which source element represents the least-significant byte from the producer's data definition.
  3. Convert each SINT to DINT before applying the shift.
  4. Mask each converted operand with 16#000000FF to remove sign extension.
  5. Apply shifts of 0, 8, 16, and 24 bits according to the required order.
  6. Combine the non-overlapping fields with OR.

If the first source octet belongs in bits 24-31, assign shifts of 24, 16, 8, and 0 respectively. Changing that mapping is a byte-order correction; changing OR to addition is not.

When is MEMCPY the better implementation?

MEMCPY copies the four stored octets directly into the destination. It avoids signed arithmetic because it does not interpret the values. It also preserves the runtime's existing memory order, which is useful only when that order matches the required 32-bit representation.

asiSource : ARRAY[0..3] OF SINT;
diTarget  : DINT;

MEMCPY(
    destAddr := ADR(diTarget),
    srcAddr  := ADR(asiSource),
    n        := SIZEOF(diTarget)
);

Before using this form, verify all of the following:

  1. The four source elements are contiguous in one array.
  2. SIZEOF(diTarget) equals the number of source bytes intended for the copy.
  3. The destination address points to the actual DINT, not to a temporary or unrelated object.
  4. The array's memory order matches the required least-significant-to-most-significant byte order.
  5. No task or communication update can change part of the source array during the copy.

An incorrect address or length can overwrite adjacent memory. Keep the length tied to SIZEOF(diTarget), and do not generalize this call to differently sized targets without checking both object sizes.

How do you verify the packed DINT?

Verify the result as a bit pattern before interpreting it as a signed decimal number. A valid 32-bit pattern can display as a negative DINT when bit 31 is set; that does not by itself indicate failed packing.

  1. Monitor the four source elements and record their hexadecimal octets in array order.
  2. Display diTarget in hexadecimal so signed decimal formatting cannot hide the byte layout.
  3. Compare target bits 0-7, 8-15, 16-23, and 24-31 against the mapping table.
  4. Test values with the high bit set in at least one source SINT; this exposes missing masks and sign extension.
  5. Repeat with unequal octets so reversed order is visible.
  6. If using MEMCPY, compare its result with the explicit shift-and-OR result for the same captured array.

Frequently Asked Questions

Why does SINT_TO_DINT not combine four TwinCAT values?

SINT_TO_DINT converts one signed value to a wider signed value. Packing requires four conversions plus byte-position shifts, or a four-byte MEMCPY.

Why does a SINT set unexpected high bits after shifting?

A negative SINT can be sign-extended during conversion. Apply AND 16#000000FF after widening and before SHL.

Why does ROL work for some byte patterns but fail for others?

ROL wraps discarded high bits into the low end. Packing needs positional left shifts, so use SHL and combine the fields with OR.

Why does MEMCPY produce a reversed DINT?

MEMCPY preserves memory order rather than protocol significance. Reverse the source mapping or use explicit shifts when the producer's byte order differs from the target layout.

How do I confirm the four octets were packed correctly?

Display the sources and diTarget in hexadecimal, then compare each source octet with target bit ranges 0-7, 8-15, 16-23, and 24-31 according to the selected mapping.

Back to blog