Zbotic Logo Zbotic Logo
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

  • Shop
  • About Us
  • Contact Us
  • Reseller
  • Blogs
020 69134444
1800 209 0998
[email protected]
Help Desk
Facebook Twitter Instagram Linkedin YouTube
Zbotic Logo Zbotic Logo
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

All departments
  • 3D Print Service
  • 3D Printer
  • Batteries & Chargers
  • Development Boards
  • Drone Parts
  • EBike parts
  • Sensor Modules
  • Electronic Components
  • Electronic Modules
  • IoT and Wireless
  • Mechanical Parts and Workbench Tools
  • Motors & Drivers & Pumps & Actuators
  • DIY and Robot Kits
  • Show more
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
Return to previous page
Home Arduino & Microcontrollers

Arduino Timer and Counter: Precision Timing Without Delay

Arduino Timer and Counter: Precision Timing Without Delay

April 1, 2026 /Posted by / 0

Table of Contents

  • Why You Should Avoid delay() in Serious Projects
  • The millis() Technique: Non-Blocking Timing
  • Hardware Timers: Timer0, Timer1, and Timer2 Explained
  • CTC Mode: Precise Frequency Generation
  • Input Capture: Measuring External Signal Timing
  • Counter Mode: Counting External Pulses
  • Timer Libraries: TimerOne and MsTimer2
  • Frequently Asked Questions
  • Conclusion

Why You Should Avoid delay() in Serious Projects

The delay() function is the first timing tool every Arduino beginner learns, but it is also the first thing experienced developers stop using. When you call delay(1000), the processor does absolutely nothing for one full second — it cannot read sensors, respond to buttons, update displays, or process serial data. For a blinking LED, this is fine. For anything more complex, it is a serious limitation.

Understanding arduino timer counter alternatives to delay() is essential for building responsive, multi-tasking Arduino projects. The ATMega328P has three hardware timers and a millis() function that allow you to manage timing without blocking your code. This tutorial covers all of these techniques, from simple millis()-based scheduling to advanced hardware timer configuration.

Consider a project that reads a temperature sensor every 2 seconds, updates an LCD every 500 ms, and checks a button for presses. With delay(), these three tasks cannot run independently — each delay blocks the others. With millis() or hardware timers, all three tasks run concurrently, each on its own schedule, and the button press is never missed.

🛒 Recommended: Arduino Uno R3 Development Board — Three hardware timers make the Uno ideal for learning timer-based programming.

The millis() Technique: Non-Blocking Timing

The millis() function returns the number of milliseconds since the Arduino was powered on or reset. By recording timestamps and comparing them, you can create non-blocking timing that allows other code to run between events.

// Multi-tasking with millis() - three independent tasks
unsigned long lastSensorRead = 0;
unsigned long lastLCDUpdate = 0;
unsigned long lastLEDToggle = 0;
bool ledState = false;

void setup() {
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  unsigned long now = millis();

  // Task 1: Read sensor every 2 seconds
  if (now - lastSensorRead >= 2000) {
    lastSensorRead = now;
    int sensorValue = analogRead(A0);
    Serial.print("Sensor: "); Serial.println(sensorValue);
  }

  // Task 2: Update display every 500ms
  if (now - lastLCDUpdate >= 500) {
    lastLCDUpdate = now;
    // updateLCD(); // Your display update function
  }

  // Task 3: Blink LED every 250ms
  if (now - lastLEDToggle >= 250) {
    lastLEDToggle = now;
    ledState = !ledState;
    digitalWrite(LED_BUILTIN, ledState);
  }

  // Button check runs every loop iteration - no delay blocking it
  if (digitalRead(2) == LOW) {
    Serial.println("Button pressed!");
  }
}

Key Pattern: Store the “last time” each task ran, and check if enough time has passed since then. This pattern is sometimes called the “Blink Without Delay” technique, named after the Arduino example sketch.

millis() Overflow: The millis() counter is an unsigned 32-bit integer that overflows (wraps to zero) after approximately 49.7 days. The subtraction pattern (now - lastTime >= interval) handles this overflow correctly because unsigned arithmetic wraps around naturally. Do not use (now >= lastTime + interval) — this fails at overflow.

micros(): For microsecond-level timing, use micros() instead. It overflows after approximately 70 minutes. The resolution is 4 microseconds on 16 MHz boards (Timer0 has a prescaler of 64).

Hardware Timers: Timer0, Timer1, and Timer2 Explained

The ATMega328P has three hardware timers that operate independently from the CPU. Each timer is essentially a counter register that increments at a configurable rate and can trigger actions when it reaches certain values.

Timer Bits Arduino Usage PWM Pins
Timer0 8-bit millis(), delay(), micros() 5, 6
Timer1 16-bit Servo library 9, 10
Timer2 8-bit tone() 3, 11

Timer0 is used by the Arduino core for millis(), delay(), and micros(). Modifying Timer0 will break these functions. Only reconfigure Timer0 if you are prepared to handle timing yourself.

Timer1 is 16-bit, meaning it counts from 0 to 65535. This is the most versatile timer and the safest to reconfigure for custom purposes. The Servo library uses Timer1, so if you use Servo and a custom Timer1 configuration simultaneously, they will conflict.

Timer2 is 8-bit like Timer0 but is used by the tone() function. If you do not need tone(), Timer2 is available for custom use. Its prescaler options differ from Timer0 and Timer1.

Prescalers: Each timer’s input clock can be divided by a prescaler to slow down the counting rate. Available prescalers:

  • Timer0 and Timer1: 1, 8, 64, 256, 1024
  • Timer2: 1, 8, 32, 64, 128, 256, 1024

At 16 MHz with no prescaler, Timer1 overflows every 4.096 ms (65536 / 16,000,000). With prescaler 256, it overflows every 1.048 seconds — a much more useful interval for timing applications.

CTC Mode: Precise Frequency Generation

CTC (Clear Timer on Compare Match) mode is the most common timer configuration for generating precise frequencies. The timer counts up from 0 and resets to 0 when it matches the OCR (Output Compare Register) value. This creates a repeating cycle with a predictable period.

// Generate a precise 1 kHz square wave on pin 9
void setup() {
  pinMode(9, OUTPUT);

  noInterrupts();
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;

  // CTC mode, toggle OC1A (pin 9) on compare match
  TCCR1A |= (1 << COM1A0);  // Toggle pin 9 on match
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS11);    // Prescaler 8

  // For 1 kHz: 16MHz / (2 * 8 * (OCR1A+1)) = 1000 Hz
  // OCR1A = (16000000 / (2 * 8 * 1000)) - 1 = 999
  OCR1A = 999;

  interrupts();
}

void loop() {
  // Pin 9 outputs 1 kHz square wave automatically
  // Main loop is free for other tasks
}

The formula for CTC mode with output toggle is: frequency = F_CPU / (2 * prescaler * (OCR + 1)). The factor of 2 accounts for the fact that a complete square wave cycle requires two compare matches (one for HIGH, one for LOW).

Practical Applications:

  • Generating carrier frequencies for IR remote control (38 kHz)
  • Creating clock signals for external chips
  • Generating audio tones with precise pitch control
  • Driving stepper motors at exact step rates

Input Capture: Measuring External Signal Timing

Timer1’s Input Capture Unit (ICU) records the exact timer value when an edge occurs on pin 8 (ICP1). This provides microsecond-precision measurements of pulse widths, frequencies, and time intervals between events.

// Measure pulse width on pin 8 using Input Capture
volatile unsigned long risingEdge = 0;
volatile unsigned long fallingEdge = 0;
volatile bool newMeasurement = false;

void setup() {
  Serial.begin(9600);
  pinMode(8, INPUT);

  noInterrupts();
  TCCR1A = 0;
  TCCR1B = 0;

  // Normal mode, prescaler 8 (2 MHz clock, 0.5us resolution)
  TCCR1B |= (1 << CS11);

  // Input Capture on rising edge, enable noise canceller
  TCCR1B |= (1 << ICES1) | (1 << ICNC1);

  // Enable Input Capture interrupt
  TIMSK1 |= (1 << ICIE1);
  interrupts();
}

ISR(TIMER1_CAPT_vect) {
  if (TCCR1B & (1 << ICES1)) {
    // Rising edge captured
    risingEdge = ICR1;
    TCCR1B &= ~(1 << ICES1); // Switch to falling edge
  } else {
    // Falling edge captured
    fallingEdge = ICR1;
    TCCR1B |= (1 <= risingEdge) {
      pulseWidth = fallingEdge - risingEdge;
    } else {
      pulseWidth = (65536 - risingEdge) + fallingEdge;
    }
    interrupts();

    float microseconds = pulseWidth * 0.5; // 0.5us per tick at prescaler 8
    Serial.print("Pulse width: ");
    Serial.print(microseconds);
    Serial.println(" us");
  }
}

Input Capture is the most accurate way to measure external signal timing on the Arduino. Unlike pulseIn() which blocks the CPU during measurement, Input Capture runs entirely in hardware and interrupts only when a measurement is ready.

Counter Mode: Counting External Pulses

Timer1 can be configured to count external pulses on pin T1 (digital pin 5) instead of the internal clock. This is useful for frequency counters, event counters, and tachometers.

// Count external pulses on pin 5 (T1)
void setup() {
  Serial.begin(9600);
  pinMode(5, INPUT);

  noInterrupts();
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;

  // External clock source on T1 pin, rising edge
  TCCR1B |= (1 << CS12) | (1 << CS11) | (1 << CS10);

  interrupts();
}

void loop() {
  noInterrupts();
  TCNT1 = 0; // Reset counter
  interrupts();

  delay(1000); // Count for exactly 1 second

  noInterrupts();
  unsigned int count = TCNT1;
  interrupts();

  Serial.print("Frequency: ");
  Serial.print(count);
  Serial.println(" Hz");
}

This simple frequency counter can measure signals up to approximately 8 MHz (the maximum external clock frequency for the ATMega328P). For signals above this, use a prescaler divider circuit before the T1 pin.

🛒 Recommended: Original Arduino Uno Rev3 A000066 — The official Arduino Uno with high-quality crystal oscillator for accurate timer operation.

Timer Libraries: TimerOne and MsTimer2

If you prefer not to manipulate registers directly, timer libraries provide a simpler interface:

TimerOne Library:

#include <TimerOne.h>

volatile bool toggle = false;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Timer1.initialize(500000); // 500ms period (in microseconds)
  Timer1.attachInterrupt(timerISR);
}

void timerISR() {
  toggle = !toggle;
  digitalWrite(LED_BUILTIN, toggle);
}

void loop() {
  // Main loop is free
  // LED blinks at 1 Hz via Timer1 interrupt
}

TimerOne Functions:

  • Timer1.initialize(microseconds) — Set the timer period
  • Timer1.attachInterrupt(callback) — Attach an ISR
  • Timer1.setPwmDuty(pin, duty) — Set PWM duty cycle (10-bit resolution)
  • Timer1.pwm(pin, duty) — Start PWM on pin 9 or 10

MsTimer2 Library: Uses Timer2 instead of Timer1, leaving Timer1 free for other purposes (like the Servo library):

#include <MsTimer2.h>

void setup() {
  MsTimer2::set(100, flashLED); // 100ms interval
  MsTimer2::start();
}

void flashLED() {
  static bool state = false;
  state = !state;
  digitalWrite(LED_BUILTIN, state);
}

void loop() {
  // Your main code here
}

Frequently Asked Questions

Can I use Timer0 for my own purposes?

You can, but millis(), delay(), and micros() will stop working correctly. If your project does not use these functions, reconfiguring Timer0 is safe. Otherwise, use Timer1 or Timer2.

Why does my servo jitter when I use Timer1?

The Arduino Servo library uses Timer1 for generating servo control pulses. If you reconfigure Timer1 for a custom purpose, the Servo library loses control. Use the ServoTimer2 library (which uses Timer2) or the TimerOne library’s PWM functions for servo control instead.

What is the maximum frequency I can generate with a timer?

In CTC mode with no prescaler, the maximum output frequency is F_CPU / 2 = 8 MHz. However, at this frequency, the CPU has zero cycles for other tasks. Practical maximum for a useful output is around 1 MHz, leaving enough CPU time for your application.

How accurate are Arduino timers?

Timer accuracy depends on the clock source. Standard Arduino boards use a ceramic resonator with approximately 0.5% accuracy. The Original Arduino boards (like the Rev3 from Arduino.cc) use a crystal oscillator with 0.005% accuracy. For applications requiring high timing precision, use the original boards or add an external crystal.

Conclusion

Moving beyond delay() to millis()-based scheduling and hardware timers is a critical skill for building responsive Arduino systems. The millis() technique handles most multi-tasking needs with simple code. Hardware timers provide microsecond-precision frequency generation, pulse measurement, and event counting that runs independently of your main loop. Whether you prefer register-level configuration or library-based approaches, mastering Arduino timers unlocks a new level of capability for your projects.

🛒 Recommended: Arduino Uno R3 Beginners Kit — Everything you need to start building timer-based projects. Browse all Arduino boards at Zbotic.in
Tags: Arduino, Counter, timer
Share Post
  • Facebook
  • Linkedin
  • Whatsapp
Weather Balloon: High-Altitude...
blog weather balloon high altitude data collection 613350
blog smart fence vibration detection for property boundary 613356
Smart Fence: Vibration Detecti...

Related posts

Svg%3E
Read more

Arduino Batch Programming: Flash Multiple Boards Quickly

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Based Radar System with Ultrasonic Sensor

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Automatic Plant Monitor: Sunlight, Moisture, Temperature

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Lie Detector: GSR Sensor Polygraph Project

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Metal Detector: Build a Treasure Finder

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading

Add comment Cancel reply

Your email address will not be published. Required fields are marked

Facebook Twitter Instagram Pinterest Linkedin Youtube

Get the latest deals and more.

Download on Google Play Download on the App Store

Call us: 020 69134444 / 1800 209 0998

Monday - Saturday 09:30 AM - 06:00 PM
For Technical Supports Email: [email protected]
For Sales / Enquiries Email: [email protected]

  • My Account

    • Cart

    • Wishlist

    • Checkout

    • My Orders

    • Track Order

    • My Account

  • Information

    • FAQs

    • Blogs

    • Career

    • About Us

    • Contact Us

    • Payment Options

  • Policies

    • Privacy Policy

    • Terms & Conditions

    • GST Input Tax Credit

    • Shipping Return Policy

    • E-Waste Collection Points

    • Our Sitemap

© Zbotic.in is registered trademark of Moxie Supply Pvt Ltd – All Rights Reserved
Login
Use Phone Number
Use Email Address
Not a member yet? Register Now
Reset Password
Use Phone Number
Use Email Address
Register
Already a member? Login Now