PLC up down counter: scan timing and direction logic

5 min read · Published November 1, 2026

Call the timer and counter once per scan, generate a fresh rising edge for each count, and preserve one direction state between scans. Reverse direction at the endpoints while the pulse is low so the change cannot create an extra count.

A PLC up down counter needs a new rising edge for every count change and a direction state that changes at both endpoints. A loop inside one scan does not create timed events. It only repeats instructions before the task finishes.

For a repeatable cycle, call the timer and counter once per task scan, route each pulse to one count input, and change direction while the pulse is low. That last detail matters: switching direction during a high pulse can create an unintended rising edge on the opposite input.

Why a counter moves only once

A typical CTUD function block has separate count-up and count-down inputs. Each responds to a FALSE-to-TRUE transition. Keeping an input TRUE does not request another count on every call.

Think of the input sequence as:

  • FALSE, then TRUE: one rising edge and one count event.
  • TRUE, then TRUE: no new rising edge.
  • TRUE, then FALSE: the input returns low, ready for a later edge.
  • FALSE, then TRUE again: another count event.

The block retains its current value and edge-detection state between calls. This is why the same instance should normally receive one complete, deliberate call per scan for this pattern.

Check the controller's counter documentation before adapting the logic. Input names, numeric types, reset and load priority, simultaneous-input behavior, and overflow or underflow handling can differ. Do not assume that every block stops counting at zero or at its preset.

Why a loop does not create elapsed time

A WHILE loop normally completes inside the current task execution. Ten iterations are ten passes through the statements, not ten timer periods.

If a timer is called before that loop, its output is not automatically refreshed by each loop iteration. Repeated counter calls can therefore receive the same high input without a new edge. Changing the input arguments between calls may also produce unintended transitions, depending on the block's implementation.

A separate variable assigned zero at the start of every scan cannot preserve counting progress across scans. Increasing it inside the loop measures loop iterations, not elapsed time or timer events.

For time-spaced counting, let the PLC task provide repeated execution. Preserve the timer instance, counter instance, and direction variable between scans. A loop that waits for a future task event can also prevent the current task from completing, potentially causing a watchdog response.

How a self-resetting timer creates pulses

One common illustrative timer expression is:

T1(IN := Run AND NOT T1.Q, PT := T#1s);

Before this call executes, the input expression uses the timer output left by its previous call. While Run is TRUE and that output is FALSE, the timer accumulates time. When the preset is reached, the call sets Q TRUE.

On the next call, the previous TRUE output makes IN FALSE. A conventional non-retentive on-delay timer then resets, returning Q to FALSE. A later call starts the next timing interval.

With one unconditional call per task scan, this normally creates a high output for one task interval. The example preset of one second is a timing request, not a promise of an exact one-second count period. Task scheduling, timer resolution, and the calls needed to reset and restart the timer affect the interval.

Use the runtime trace and the manufacturer's timer description to establish the actual timing. If the count must represent accurately elapsed time, use an appropriate documented timebase rather than treating this pulse generator as a precision clock.

Keep direction changes in the low phase

Use one Boolean direction state instead of two independent flags. For example, CountUp = TRUE selects counting up; FALSE selects counting down.

The intended transitions are simple:

Current direction Endpoint reached Next direction
Up Current value at or above the upper limit Down
Down Current value at or below zero Up
Either Neither relevant endpoint reached No change

Apply those transitions while the timer output is FALSE. This gives both count inputs a low phase before the next pulse reaches the selected input.

Without that separation, a subtle extra count is possible. Suppose an upward pulse reaches the upper limit. If direction changes while that pulse remains TRUE, the down input can change from FALSE to TRUE without a new timer pulse. The counter sees an edge created by the direction logic rather than by the timing source.

The preset also does not normally reverse direction by itself. An upper-limit output reports a comparison condition. Your application must decide what happens next.

A scan-based pattern for repeated cycling

The following Structured Text illustrates the call order. It assumes a conventional CTUD interface with R for reset and LD for load. Adapt the names and behavior to the controller's documented block definition before use.

You need persistent-between-scans instances of T1 and C1, a Boolean CountUp initially TRUE, and a positive UpperLimit compatible with the counter's value type. Here, Run and ResetRequest are application inputs.

T1(
    IN := Run AND NOT ResetRequest AND NOT T1.Q,
    PT := T#1s
);

IF ResetRequest THEN
    CountUp := TRUE;
ELSIF NOT T1.Q THEN
    IF CountUp AND (C1.CV >= UpperLimit) THEN
        CountUp := FALSE;
    ELSIF (NOT CountUp) AND (C1.CV <= 0) THEN
        CountUp := TRUE;
    END_IF;
END_IF;

C1(
    CU := Run AND NOT ResetRequest AND T1.Q AND CountUp,
    CD := Run AND NOT ResetRequest AND T1.Q AND NOT CountUp,
    R := ResetRequest,
    LD := FALSE,
    PV := UpperLimit
);

The direction comparisons read the counter value from its previous call. If a count reaches an endpoint during the final call, direction changes on a later scan when the timer output is low. The next timer pulse then drives the opposite input.

Each call supplies the intended control inputs explicitly. The pattern does not rely on omitted arguments retaining a convenient value.

Preserved between scans does not necessarily mean retained through a power cycle. Define startup initialization and the documented reset sequence separately. Validate UpperLimit before enabling the cycle, and keep it unchanged during a cycle unless you have specified how a configuration change should behave.

In this example, stopping Run resets the timing interval but does not request a counter reset. Restarting begins a fresh timing interval from the stored count and direction. Confirm that this is the behavior your application needs. This counting pattern is not a machine safety function.

What to monitor during commissioning

Use a controller trace or task-rate capture where available. A watch window can refresh too slowly to show a pulse that lasts only one task interval.

Monitor these signals together:

  • Run and ResetRequest, to establish which commands are active.
  • T1.IN, T1.Q, and elapsed time, where available, to inspect pulse generation.
  • The actual CU and CD expressions, to inspect the edges reaching the counter.
  • C1.CV, to identify the count change associated with each edge.
  • CountUp, to confirm direction changes during a low pulse phase.

For an illustrative upper limit of three, the count should progress from zero through one, two, and three, then through two, one, and zero on successive pulses. It should reverse again without requiring a new start command.

Check reset, stop and restart during both directions. Confirm that no pulse activates both count inputs, and that every intended pulse changes the value once. Perform commissioning under the site's approved maintenance procedure.

If the count changes only once, inspect whether the selected input ever returns FALSE. If it cycles once and stops, inspect both direction transitions. If it changes unexpectedly at an endpoint, check for a direction change while the pulse remains high.

For a separate operating view, distinguish a factory status display from an operator HMI. A status screen can present the count and direction, but controller tracing is the appropriate tool for checking scan-level edges.

Questions

Why does a PLC up down counter count only once inside a loop?

A WHILE loop repeats statements within the current task execution. It does not wait for another timer interval or automatically create fresh input edges. A typical CTUD counter responds to FALSE-to-TRUE transitions, so repeated calls with the same high input do not produce repeated timed counts.

When should the counter change direction?

Change direction after the current value reaches the selected endpoint, while the timing pulse is low. This allows both count inputs to return FALSE before the next pulse. Changing direction during a high pulse can create an unintended rising edge on the opposite input.

Does stopping the timer reset the counter value?

Not necessarily. In the example pattern, stopping resets the timer but leaves the counter value unchanged. Resetting the count requires the counter's documented reset or load behavior. Define stop, restart, reset and power-up behavior separately rather than assuming they have the same effect.

Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.

Keep reading

Build your signAsk for a quote