Timers, Counters and Data
Timer and counter types, data types, math and compare, scaling, and the number mistakes that bite.
Timers
IEC 61131-3 defines three timers. Each has an input IN, a preset time PT, a done output Q and an elapsed time ET. Brand names and syntax differ, but the behavior is the same.
| Timer | Q turns on… | Q turns off… | Typical use |
|---|---|---|---|
| TON on-delay | PT after IN turns on, if IN stays on | immediately when IN turns off | Start-up delays; ignore short blips; alarm delays |
| TOF off-delay | immediately when IN turns on | PT after IN turns off | Fan run-on; keep a light on after leaving |
| TP pulse | immediately on a rising edge of IN | after exactly PT, whatever IN does | Fixed-length pulse: horn, dosing shot |
| Retentive (RTO, TONR) | when the accumulated time reaches PT | only when reset | Run-hours, total on-time, maintenance timers |
- A TON restarts from zero each time IN goes false. A signal that flickers never reaches the preset; this makes a TON a simple filter.
- Use the done (Q) bit, not the elapsed time, to drive the next step. Comparing ET to a value makes logic that depends on scan timing.
- Timer accuracy is to within one scan: the timer is updated once per scan. A 100 ms timer on a 12 ms scan can be 12 ms late.
- One timer instance per use. Reusing the same timer in two places causes the two to fight over it. Give each function its own timer tag.
- Timer range. Presets are limited by the data type; see the number gotchas below.
Timer units by platform
| Platform | How a 5 s on-delay is entered | Notes |
|---|---|---|
| Allen-Bradley Logix | TON with .PRE = 5000 (milliseconds) | Timer tag has .PRE .ACC .EN .TT .DN; use .DN as the done bit |
| Siemens S7-1200 | TON with PT := T#5s | IEC timer with an instance data block; Q and ET outputs |
| Mitsubishi iQ-F / FX | OUT T0 K50 for a 100 ms timer (50 × 100 ms) | Different timer numbers have different time bases (100 ms, 10 ms, 1 ms); check the device list |
| Omron CP1 | TIM 0000 #0050 (0.1 s units) | TIMH is the 10 ms version; BCD preset on older models |
| IDEC FC6A | TIM T0 50 (typically 0.1 s units) | TIM, TMH (10 ms) and TMS (1 s) variants |
| Unitronics Vision | Timer with a preset in 10 ms units (500) | Check the units of the timer in the software; presets can be a number or a data register |
| CODESYS | TON(IN := x, PT := T#5s) | Standard IEC function block; TIME type |
Units are the most common source of timer mistakes when changing brands: a preset of 50 is 5 s on one platform and 50 ms on another. Always check the time base.
Timer examples
Flasher (blinker)
Two TON timers feeding each other produce a repeating on/off signal: when Flash_On is false, timer 1 counts 0.5 s and turns the output on; when the output is on, timer 2 counts 0.5 s and turns it off. In structured text:
Flash_T1(IN := NOT Flash_Out, PT := T#500ms); (* time off *)
Flash_T2(IN := Flash_Out, PT := T#500ms); (* time on *)
IF Flash_T1.Q THEN Flash_Out := TRUE; END_IF;
IF Flash_T2.Q THEN Flash_Out := FALSE; END_IF;
Many PLCs have a built-in clock bit (for example a 1 Hz or 0.5 s system flag) that does the same in one contact.
Delayed start sequence
To start three conveyors one after another 3 s apart, use a TON per conveyor with the previous conveyor’s run bit as IN: Conv2 starts 3 s after Conv1 runs, Conv3 3 s after Conv2. Stop in reverse order with off-delays, so the material is cleared.
Timeout on a wait
Every “wait until the sensor is on” should have a timeout that raises a fault. A TON that starts when the step starts and is reset when the step ends, with the done bit moving to a fault state, prevents a stuck sensor from freezing the machine.
Run-hours
A retentive timer, or a counter driven by a one-second clock bit, holds total run time. Counting seconds in a DINT holds 2 147 483 647 s, about 68 years, which is plenty; counting in an INT overflows after 9.1 hours.
Counters
CTU counts up on each rising edge of its count input CU; CTD counts down; CTUD does both. Q (done) is on when the count value CV reaches the preset PV. A reset input sets CV to zero. Because only the edge counts, holding the input on adds one count, not many.
- Retentive or not? Production counts you need after a power loss go in retentive memory; counters that are only part of a cycle should reset at start-up.
- Reset on the same event that uses the done bit. Look at the edge of Q so the case-full signal fires once per case.
- Bounce. A noisy sensor produces extra counts: use a filter, a hardware debounce, or a time-based gate.
- Limits. A 16-bit counter holds 32 767 (signed) or 65 535 (unsigned). A running total needs a DINT.
- Rate. A normal input is limited by the scan; use a high-speed counter input for fast signals (see the next section).
Measuring speed with a counter
A pulse counter measures speed if you count over a fixed time window: speed = counts ÷ window. A high-speed counter input does the counting in hardware, so the scan cannot miss pulses.
rpm = (counts ÷ pulses per revolution) × (60 ÷ window in seconds)
Worked example: an encoder on a motor shaft
- A 1 000 pulse-per-revolution encoder gives 500 counts in a window of 100.0 ms.
- Revolutions in the window: 500 ÷ 1 000 = 0.5, so rpm = 0.5 × (60 ÷ 0.1) = 300 rpm.
- The resolution is one count: 1 ÷ 1 000 rev in 0.1 s = 0.6 rpm per count. A longer window gives a finer, but slower, reading.
- A 3 000 rpm shaft makes 50 rev/s, or 50 000 pulses per second with this encoder: far beyond a normal input (about 29 per second), so it needs a high-speed counter.
- In a PLC with a scan, read the counter every 100 ms (a periodic task or a timer), subtract the previous value, and handle the rollover when the counter wraps.
Data types
| Type | Size | Range (or precision) | Platform names |
|---|---|---|---|
| BOOL | 1 bit | 0 or 1 | BOOL; bit; X; M; B |
| SINT / USINT | 8 bits | −128 to 127 / 0 to 255 | SINT, Byte |
| INT / UINT | 16 bits | −32 768 to 32 767 / 0 to 65 535 | Logix INT, S7 Int/UInt, Mitsubishi word, Unitronics MI |
| DINT / UDINT | 32 bits | −2 147 483 648 to 2 147 483 647 / 0 to 4 294 967 295 | Logix DINT (the default), S7 DInt, Unitronics ML |
| REAL | 32 bits | IEEE 754 single: about 7 significant digits | Logix REAL, S7 Real, Unitronics MF |
| LREAL | 64 bits | IEEE 754 double: about 15–16 digits | S7 LReal, CODESYS LREAL |
| WORD / DWORD | 16 / 32 bits | Bit strings for flags and masks | Word, DWord |
| TIME | varies | T#5s, T#1h30m (stored in ms on most platforms) | TIME; Logix timers use DINT milliseconds |
| STRING | varies | Text, with a length limit | STRING; S7 String[254] |
Number gotchas
- Integer overflow. A 16-bit INT wraps or faults past 32 767. Counting seconds in an INT overflows after 32 767 s = 9.1 hours. Multiplying a raw analog value of 27 648 by 100 gives 2 764 800, which does not fit: convert to DINT or REAL first.
- Integer division truncates. 7 ÷ 2 is 3 in integer math. Convert to REAL first when you need the fraction, and multiply before dividing to keep precision.
- Timer range. A 32-bit millisecond count tops out at 2 147 483 647 ms, about 24.9 days. Count minutes or use a clock for longer.
- REAL loses small increments on large totals. A 32-bit REAL has a 24-bit mantissa, so once the total is large enough, adding a small number does nothing.
- Never compare REAL values with “=”. Test whether the difference is within a tolerance, or use greater-than and less-than.
- Signed and unsigned. A raw analog value read as a signed 16-bit integer that is really unsigned (or the reverse) goes negative above 32 767; check the module’s data format.
- Mixed types. Adding an INT to a REAL converts silently on some platforms and faults on others; convert explicitly.
Worked example: a flow totaliser that stops counting
- A flow of 12.5 L/s is integrated every 10 ms scan: each scan adds 12.5 × 0.010 = 0.125 L to a REAL total.
- A 32-bit REAL can represent steps no smaller than 2e−23 at a value near 2e. When the total reaches 221 = 2 097 152 L, the spacing between REAL values is 0.25 L, so adding 0.125 L rounds back to the same number: the total stops increasing.
- At 12.5 L/s that happens after 167 772 s, about 46.6 hours: a long-running plant would see a totaliser that quietly stalls.
- Cure: use LREAL (about 15 digits), or accumulate integer pulses from the flow meter in a DINT and convert to liters only for display, or keep a REAL for the fraction and a DINT for the whole liters.
Math, compare and scaling
| Instruction | Does | Notes |
|---|---|---|
| ADD, SUB, MUL, DIV | Arithmetic on integers or reals | Check overflow and divide-by-zero behavior |
| MOD | Remainder | Cycling a count through a range; every-nth-item logic |
| EQU, NEQ, GRT, GEQ, LES, LEQ | Compare two values (equal, not equal, greater, and so on) | Output is a bit; use with hysteresis for analog control |
| MIN, MAX, LIMIT | Clamp a value into a range | Protect an output from out-of-range setpoints |
| SCALE / SCP / NORM_X and SCALE_X | Linear scaling | Names vary: Logix SCL/SCP, S7 NORM_X + SCALE_X, CODESYS structured text |
| CPT, CALC | Computes an expression | Logix CPT, some Mitsubishi and IDEC math blocks |
Analog values come into the program as raw counts and need scaling to engineering units: a straight-line conversion through two known points.
EU = EUlow + (raw − rawlow) ÷ (rawhigh − rawlow) × (EUhigh − EUlow)
Worked example: a pressure transmitter
- A 4–20 mA transmitter measures 0–10 bar, and the card gives 0 to 27 648 counts. The program reads raw = 20 736.
- Fraction of span: 20 736 ÷ 27 648 = 0.75. Current: 4 + 16 × 0.75 = 16 mA. Pressure: 0.75 × 10 = 7.5 bar.
- In structured text, convert to REAL before dividing:
Pressure_bar := INT_TO_REAL(Raw) / 27648.0 * 10.0;Doing it in integers would truncate the result to a whole number. - Fault detection: raw below about −1 382 or above about 29 030 (4 mA minus 5 % and 20 mA plus 5 %) means a broken wire or an overrange: raise a fault instead of using the number.
Pump control with hysteresis
A comparator that turns a pump on below 40 % and off above 80 % needs two thresholds, so that the pump does not chatter around a single one. The ladder rung combines a “less than 40” test with a “less than 80 and already running” seal-in branch:
Bits, words and conversions
- Masks and flags. A 16-bit word can hold 16 status bits.
Status.0addresses a single bit on many platforms; otherwise use AND with a mask (Status AND 16#0004tests bit 2). - Shift and rotate move bits left or right: they multiply or divide by two, build shift registers for tracking items on a conveyor, and pack data.
- BCD. Binary-coded decimal stores each digit in four bits; Mitsubishi, Omron and thumbwheel switches still use it. Convert with BCD_TO_INT and INT_TO_BCD; displaying a BIN number in a BCD display gives nonsense.
- Word order for 32-bit values. A 32-bit number sent as two 16-bit words can arrive with the words swapped; a REAL read over Modbus often needs a word swap. See Modbus RTU, ASCII and TCP.
- Strings and text use one byte per character, with length limits; build HMI messages with the platform’s string instructions.
- Time and date. Real-time clock and date instructions give timestamps for logs; check time-zone and daylight-saving handling.
Related
- PLC & Logic Control: the overview of this guide.
- Ladder Logic in Practice: patterns that use these timers and counters.
- I/O Wiring and Signals: analog signals in more detail.
- Structured Text and the Other IEC Languages: math and data in Structured Text.
- 4–20 mA / 0–10 V Analog Scaling Tool.
FAQ
Why is my timer not timing?
Its input is not staying on long enough (it resets each time the input drops), the timer is in a routine that is not running, or the preset is in different units from what you expected. Go online and watch the accumulated value.
What is the difference between TON and a retentive timer?
A TON resets when its input goes off. A retentive timer (RTO, TONR) keeps its accumulated time and continues from there next time, until you reset it. Use it for run-hours and total on-time.
Why does my counter not count?
The input is a level, not an edge, that does not change; the counter instruction is in a rung that is not executed every scan; or the pulses are shorter than the scan. For fast pulses use a high-speed counter.
Should I use INT or DINT?
Use DINT unless memory is very tight or the protocol needs 16 bits. It avoids overflow problems, and on many platforms (Logix) it is also the native size and faster.
Why does my math give the wrong answer?
Usually integer truncation or overflow. Convert to REAL (or DINT) before multiplying, and divide last. Check each intermediate result.
References
- IEC 61131-3, Programmable controllers, Part 3: Programming languages (standard function blocks TON, TOF, TP, CTU, CTD, CTUD).
- IEEE 754-2019, Standard for Floating-Point Arithmetic.
- Rockwell Automation, Logix 5000 Controllers General Instructions Reference Manual (timers and counters, data types).
- Siemens, S7-1200 System Manual (IEC timers and counters, data types, NORM_X and SCALE_X).
- Mitsubishi, Omron, IDEC and Unitronics instruction reference manuals for timer time bases.
Last reviewed: October 2026. Instruction names, addresses and data sizes differ by manufacturer: confirm against your controller’s manual, and keep safety functions independent of standard PLC logic.
