Omron C200H Timer Value Transfer with ROL/ROR Shift Registers

James Nishida15 min read
OmronOther TopicTutorial / 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

Omron C200H Timer Value Transfer with ROL/ROR Shift Registers

Implementing a rotating timer stack on an Omron C200H using ROL and ROR on the HR area creates a subtle field problem: the bits rotate cleanly through the word, but the timer Present Values (PV) do not follow them. The HR bit rotation only propagates the on/off pattern; the T100–T199 timer PVs remain anchored to their fixed TC-area addresses. To make the running timer value "follow the bit," the application must explicitly move the PV between timer words on the same scan that the bit transition fires. This article documents the three field-proven solutions—XFER(70) block transfer, MOVD(83) digit/word move, and XCHG(73) exchange gated by DIFU(13)—and explains the scan-cycle failure mode that makes XCHG fail without an edge trigger.

The Problem: Bits Move, Timer Values Stay

A typical application uses two sensors to advance and retreat a "running timer" through a multi-slot stack. Sensor 1 pulses ROL on HR00, shifting a "1" one position to the left; Sensor 2 pulses ROR, shifting a "0" back from the right. If HR0000 is wired as the start input of T100, T101, T102, … through HR0009, then the bit rotation alone will light the timers on and off in sequence—but the elapsed count held inside each timer's PV stays attached to its fixed address. The timer that just started has 0.0 s; the timer that is one step ahead has its full PV, and so on. There is no built-in mechanism on the C200H for the PV to track the bit.

To obtain the desired behavior—where the running PV travels with the bit—you must move the PV from the timer's source address to the timer's destination address on every shift. Because the C200H executes each rung once per scan in program order, this move must be conditioned on the same edge that triggers ROL/ROR. Otherwise the move re-fires every scan, the PV oscillates between two addresses, and nothing accumulates.

C200H Memory Architecture and Timer Addressing

The C200H address map relevant to this task spans four areas:

Area Range Function
HR (Holding Relay) HR00–HR99 Bit-stored I/O; retains state across power cycles
TC (Timer/Counter) T000–T199 / C000–C199 Each entry is a 16-bit PV word plus a 1-bit completion flag
DM (Data Memory) DM0000–DM1999 16-bit words for numeric data, BCD or BIN
IR (Internal Relay) IR000–IR246 Working bits, scan-only retention
LR (Link Relay) LR00–LR63 Reserved for PLC Link

The TC area is special: each timer number is simultaneously a bit (the completion flag) and a 16-bit word (the present value). By default the C200H stores PVs as four-digit BCD, so the legal range is 0000–9999 (i.e., 0.0 s to 999.9 s for a 0.1 s timer). The timer word can be set in BIN mode by setting bit 15 of the timer control word, but the field default is BCD.

When you ROL or ROR a 16-bit HR word, only the bit pattern rotates; no TC-area word is touched. Conversely, when you use XFER/MOVD/XCHG on TC words, you are manipulating 16-bit PVs as numeric data, independent of whether the corresponding completion bit is on or off. The two halves of a TC entry can be in inconsistent states during a transfer; that is why the source timer must typically be off (completion flag = 0) when its PV is read or written.

Why Timer PVs Do Not Auto-Follow HR Bits

The C200H has no auto-relocation mechanism linking a timer's PV to the bit position that started it. This is by design: timers are address-bound resources, not tag-bound variables. Once T100 is T100, the PV is stored at the TC address allocated to T100; no hardware or firmware logic will copy that PV to T101 just because a bit in HR00 shifted from HR0000 to HR0001.

To make the PV travel, the application must explicitly perform a block or word move in ladder logic. The scan-time contract is: when the bit that started timer N turns off (or the bit that should now drive timer N+1 turns on), the PV must be moved from timer N to timer N+1 in the same scan. Doing it on the next scan introduces a 1-scan error equal to one scan period, which for a 20 ms scan equals 20 ms of "lost" count. For most industrial applications this is negligible, but for high-speed sequencing it matters.

Solution 1: XFER(70) Block Transfer

XFER(70) is the cleanest solution for a multi-slot rotating stack. It copies N consecutive words from a source block to a destination block, where N is a 16-bit count specified in the first operand.

Format:

FUN  XFER(70)
     N    Control word: number of words to transfer (BCD 0001–FFFF)
     S    Source: first word of source block
     D    Destination: first word of destination block

Example rung for a left-rotation on a single timer PV:

|  HR0000  --|DIFU(13) 20000|--|  ROL(560)  |
|                                |  HR00 HR00 |
|  HR0000  --|DIFU(13) 20000|--|  XFER(70)  |
|                                |  #0001     |
|                                |  T100      |
|                                |  T101      |

Operation on the rising edge of HR0000:

  1. Sensor 1 sets HR0000; DIFU(13) generates a one-scan pulse on IR20000.
  2. ROL(560) rotates HR00 left by one bit, moving the "1" from HR0000 to HR0001.
  3. XFER(70) on the same pulse copies T100's PV (the running count) into T101.
  4. T100 is now off (its input is no longer driven) and ready to start a fresh count on the next Sensor 1 pulse.

C200H execution time: 305 µs for a 1-word transfer (per the C200H Operation Manual, function-number table). For an N-word transfer, multiply accordingly and add to scan time.

XFER(70) is the recommended solution because it scales to multi-slot stacks trivially—set N to the stack size and the source/destination pointers shift by one word each rotation. The instructions and the operand words are easy to read in a handheld programmer or printed ladder listing.

Solution 2: MOVD(83) Move with Digit Control

MOVD(83) (Move Digit) can move a single 4-bit digit or a full 16-bit word from a source to a destination, with the option to mask or reposition digits. For full PV transfer the typical control word is #0030: move four digits from source to destination unmodified.

FUN  MOVD(83)
     S    Source word (e.g., T100 PV)
     C    Control word: #0030 = move all four digits to destination
     D    Destination word (e.g., T101 PV)

C200H execution time: 195 µs for a word-to-word move. Slightly faster than XFER(70) for a single word, but the operator must understand the control-word encoding to read the rung.

Use MOVD(83) when you need digit-level granularity—e.g., transferring only the hundreds and thousands digit while leaving units and tens at zero. For pure timer-PV transfer, XFER(70) is generally easier to read and audit.

Solution 3: XCHG(73) Exchange with DIFU(13) Edge Trigger

XCHG(73) swaps the contents of two words. To use it for a "follow-the-bit" PV transfer, the rung must be edge-triggered with DIFU(13) or DIFD(14); otherwise the exchange re-fires every scan and the PVs oscillate.

|  HR0000  --|DIFU(13) 20000|--|  XCHG(73)  |
|                                |  T100      |
|                                |  T101      |

C200H execution time: approximately 190 µs per execution—the fastest of the three options for a single PV. The trade-off is that XCHG swaps both directions at once; if the application needs to preserve the source value (rather than discard it), you must precede XCHG with a stash to DM or another TC word.

For a ROR (right-rotation) direction, the same XCHG works in reverse: rising edge of HR0001 (the bit moving right) triggers the swap, returning the PV to its previous slot.

Why XCHG(73) Fails Without DIFU(13): Scan Cycle Analysis

This is the most common field failure mode. A technician places XCHG(73) directly in the rung that follows the HR bit's input condition, expecting a one-time swap. In ladder logic, a rung executes once per scan for as long as its input condition is true. If the input sensor stays on for multiple scans, the rung fires on every scan.

Walkthrough of the failure:

  1. Scan 1: HR0000 turns on. ROL shifts the "1" to HR0001. XCHG swaps T100 ↔ T101. T101 now has the running count; T100 is 0.
  2. Scan 2: HR0000 is still on. ROL does nothing new (the bit is now at HR0001). XCHG re-fires: T100 and T101 swap back. T100 again has the count.
  3. Scan N: Same oscillation. The PVs ping-pong between T100 and T101; the count never accumulates in either.

The fix is a one-shot input edge detector. DIFU(13) reads its input bit, detects a 0→1 transition, and energizes its output bit for exactly one scan. On the next scan, the output goes off regardless of the input. Place DIFU(13) immediately upstream of the XCHG rung and the rung will fire exactly once per rising edge of the trigger condition.

|  Sensor1  --|DIFU(13) 20000|--|  XCHG(73)  |
|                                |  T100      |
|                                |  T101      |

The same logic applies to MOVD(83) and XFER(70) when the rung is driven by a maintained (not momentary) input. Edge-triggering is cheap insurance and improves scan-time determinism by preventing repeated execution on the same input cycle.

Multi-Slot Stack Implementation

For a 10-position timer stack that rotates within HR00 (bits HR0000–HR0009), the cleanest implementation is a single XFER(70) with N = 10:

|  Sensor1  --|  ROL(560) HR00 HR00  |   ; rotate bit left within HR00
|  Sensor2  --|  ROR(560) HR00 HR00  |   ; rotate bit right within HR00
|
|  Sensor1  --|DIFU(13) 20000|--|  XFER(70)  |
|                                |  #0010     |
|                                |  T100      |
|                                |  T101      |

On each Sensor 1 pulse:

  1. ROL shifts the bit one position left through HR00.
  2. DIFU(13) generates a one-scan pulse.
  3. XFER(70) copies 10 words from T100–T109 to T101–T110.
  4. T110 receives the previously-elapsed PV at position 9 (the front of the stack).
  5. T110's previous content is overwritten—this is the "exit" slot of the FIFO.
  6. T100 is reset to 0 and is ready for a new entry.

For Sensor 2 (right-rotation, removing an entry from the back of the stack):

|  Sensor2  --|DIFU(13) 20001|--|  XFER(70)  |
|                                |  #0010     |
|                                |  T101      |
|                                |  T100      |

Note the reversed source/destination: T101→T100. The PV shifts one position back; T109 is overwritten with 0 (the back of the stack clears).

Scan-time impact: 10-word XFER at 305 µs/word ≈ 3.05 ms added to the scan. On a C200H with a 20 ms nominal scan, this is a 15% increase. If scan budget is tight, switch to MOVD(83) in a loop, or reduce the stack size.

Instruction Comparison: Performance and Use Cases

Instruction Function C200H Execution Time Best Use Case
XFER(70) Block move N words 305 µs (1 word) Multi-timer PV stacks; readable ladder
MOVD(83) Move with digit control 195 µs (word) Single PV with digit-level masking
XCHG(73) Swap two words ~190 µs Two-slot exchange; lowest latency
MOV(21) Single word copy ~80 µs Non-timer data only; not for live PV

Warning: Direct MOV(21) from one active timer word to another can read the PV in a way that resets the source timer on certain C200H firmware revisions (notably early C200H-CPU11 units). Use XFER(70) or XCHG(73) for live PV transfer between TC words. Reserve MOV(21) for non-TC sources/destinations.

Verification and Commissioning Procedure

Follow this sequence to confirm a correct implementation:

  1. Connect a C200H handheld programmer (or a PC running CX-Programmer via Sysmac Gateway) to the CPU RS-232C port.
  2. Enter Monitor mode and force HR0000 ON. Confirm via the bit display that the "1" appears at HR0001 on the next scan.
  3. Open the TC area display and monitor T100, T101, and T102 in succession.
  4. Set T100 as a 0.1 s free-running timer (TIM T100 #0010).
  5. Force HR0000 ON once per second. Verify that T101's PV increases by 0.1 s on each pulse, and T100 resets to 0000 between pulses.
  6. For the ROR direction, repeat with HR0001 as the trigger and reverse the XFER source/destination.
  7. Confirm no oscillation: if the PV toggles between two addresses, you forgot the DIFU(13) edge trigger.
  8. Plot the PV trend over 10 cycles to confirm monotonic accumulation in the receiving timer.

State machine reference for the verification:

Idle T100 PV=0 T100 Timing PV increments DIFU(13) Pulse XFER(70) fires T101 Holds PV T100 reset Sensor1 one-shot next cycle

Common Pitfalls and Field Failures

Symptom Cause Fix
PV oscillates between T100 and T101; no accumulation XCHG/MOVD re-firing every scan Add DIFU(13) ahead of the move rung
PV always reads 0000 in destination timer Source timer still timing during transfer; PV not yet latched Use XCHG (atomic swap) or sequence the move one scan after the bit shifts
Scan time exceeds 50 ms with 10-timer stack 305 µs × 10 words = 3 ms; other logic overhead Reduce stack size or switch to MOVD(83) per word
BCD corruption: PV reads as hex 0x1234 Timer set in BIN mode but XFER/MOVD default to BCD context Verify TC control word; confirm BIN/BCD consistency across source and destination
Power cycle loses all running timers TC PVs do not retain across power loss Mirror PVs to DM on every shift; reload from DM on startup
One timer lags by one scan Move rung placed after ROL rung; second scan delay Reorder rungs so move fires same scan as ROL, or use XCHG (atomic)

Cross-Platform Compatibility

The same instructions exist across the Omron C-series and CJ/CS families, with a few addressing and operand differences:

Platform XFER(70) MOVD(83) XCHG(73) TC Area Notes
C200H Yes Yes Yes T000–T199 Original; base instructions, BCD default
C200HS Yes Yes Yes T000–T511 Expanded TC area; same instruction set
C200HE/HG/HX Yes Yes Yes T000–T1023 Larger TC area; otherwise identical
CQM1 / CPM1 / CPM2 Yes (XFER/@XFER) Yes Yes TIM/CNT numbering differs Address format: TIM 000 vs T000
SRM1 Yes Yes Yes Compact subset Reduced TC area; verify ladder fits
CJ1 / CS1 Yes (XFER(70)) Yes (MOVD(83)) Yes (XCHG(73)) CIO-based TC area Function numbers preserved; operand areas expanded (WR, EM banks)
CP1 series Yes Yes Yes TIM/TCNT area Verify with CX-Programmer instruction reference

On CJ1/CS1, XFER(70) and XCHG(73) are present in the base instruction set and the function numbers are unchanged, but the operand areas are much richer (CIO, WR, DM, EM banks, HR). This means you can rotate timer PVs through any word area without worrying about the TC-only restriction. The XFER scan-time penalty on CJ1 is significantly lower (sub-µs per word), so the 10-timer stack adds microseconds rather than milliseconds.

For migration from C200H to CJ1/CS1, ladder logic using XFER(70), MOVD(83), and XCHG(73) ports directly; only the I/O and address prefixes (HR → HR or H, IR → CIO) need updating. The handheld programmer is replaced by CX-Programmer, but the rung structure is identical.

Alternative Architectures

If the application does not strictly require the running PV to follow the bit, two simpler architectures are available:

Cascaded timer completion flags. Wire the TC bit of T100 to the start input of T101, T101's TC bit to T102, and so on. The PVs remain anchored to their fixed addresses; only the on/off state propagates. This is sufficient for sequencing applications where only the "next timer is ready" signal matters, not the elapsed count of a specific step.

DM-mirrored PVs. Store the running count in a DM word and shift the DM block with XFER(70). This avoids TC-area write restrictions entirely and gives the application a numeric history of every step's elapsed time, which can be useful for diagnostics. Trade-off: the DM block is just a number, not a running timer; you cannot use it to drive subsequent logic with TIM-style on-delay behavior.

For most rotating-stack applications on the C200H, the XFER(70) + TC area approach described above is the cleanest combination of physical timer behavior and address shiftability.

References in the C200H Documentation

For authoritative instruction timing and operand restrictions, consult the Omron C200H product family page and the C200H Operation Manual (W339). The Omron Industrial Automation portal hosts the C200H instruction-set reference, firmware revision notes, and CX-Programmer migration guides. Specific instruction pages for XFER, MOVD, XCHG, DIFU, and DIFD are indexed under the C-series instruction reference in the Omron IA knowledge base.

Why does my XCHG(73) keep swapping back and forth?

Because XCHG executes every scan for as long as the rung is true. Wrap the XCHG rung input with DIFU(13) so the swap fires only on the rising edge of the trigger, matching the scan where ROL/ROR shifts the bit. Without DIFU(13), a maintained sensor input causes the rung to fire repeatedly and the PVs oscillate.

Can I use MOV(21) to copy a timer PV directly?

MOV(21) reads the source PV and writes the destination in one instruction cycle, but on early C200H firmware revisions (notably CPU11) this can corrupt an actively timing source. Use XFER(70) or XCHG(73) for live timer PV transfer between TC words; reserve MOV(21) for non-TC sources/destinations.

Does the TC area retain PVs across power-off like HR?

No. HR retains bit state across power cycles, but TC PVs reset to 0000 on power-up. If the application must survive a power loss with timers mid-state, mirror the PVs into DM on every shift and reload from DM on the first scan after startup.

What is the fastest instruction for a single timer PV transfer?

XCHG(73) at approximately 190 µs is the lowest-latency swap on the C200H. MOVD(83) is 195 µs for a word copy, and XFER(70) is 305 µs. For a single PV, XCHG with DIFU(13) is the fastest; for multi-PV stacks, XFER(70) with N equal to the stack size is more efficient and more readable.

How do I handle a 10-timer stack that shifts in unison?

Use XFER(70) with N = 10 (#0010) to copy the 10-word block T100–T109 to T101–T110 on each shift pulse. Allow approximately 3.05 ms of additional scan time at C200H execution speeds (305 µs × 10). For the reverse direction (ROR), swap source and destination in the XFER operand fields so the block shifts back.

What is the difference between DIFU(13) and DIFD(14) for triggering the move?

DIFU(13) detects a 0→1 (rising) transition and energizes its output for one scan; DIFD(14) detects a 1→0 (falling) transition. Use DIFU(13) when the bit transition that starts the rotation is a rising edge (e.g., sensor input going high). Use DIFD(14) when the trigger is the trailing edge of a maintained input or the falling edge of a HR bit that has been rotated out of position.

Back to blog