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 Registers: Precise Timing Without delay()

Arduino Timer Registers: Precise Timing Without delay()

March 11, 2026 /Posted byJayesh Jain / 0

Understanding Arduino timer registers is the single most important step you can take to move beyond beginner-level sketches into professional, real-time embedded firmware. The delay() function is convenient for learning, but it blocks your entire program — nothing else can run while it counts milliseconds. Arduino timer registers, by contrast, let you schedule interrupts, generate precise PWM signals, and measure time with microsecond accuracy while the rest of your code runs freely. In this guide, you will learn exactly how the ATmega328P timers work and how to configure them directly for your projects.

Table of Contents

  • Why delay() Is Holding You Back
  • Arduino Timer Overview: Timer0, Timer1, Timer2
  • Key Timer Registers Explained
  • Prescalers and Frequency Calculation
  • CTC Mode: Generate Precise Timed Interrupts
  • Fast PWM Mode: Custom PWM Frequencies
  • Practical Examples
  • Using millis() and micros() Correctly
  • Frequently Asked Questions

Why delay() Is Holding You Back

The delay() function calls _delay_ms() from the AVR libc, which spins the CPU in a tight busy-wait loop. During this time:

  • No other code can execute (your sensors go unread, your buttons go unchecked)
  • Serial communication can drop bytes if the buffer fills up
  • Servo signals and other time-sensitive outputs can glitch
  • You cannot respond to external events in real time

The professional alternative is to use hardware timers with interrupts. The timer counts in the background using dedicated hardware, fires an interrupt when it reaches a target value, and your Interrupt Service Routine (ISR) runs — leaving the loop() free to handle everything else. This is fundamentally how real embedded systems work, from thermostats to motor controllers.

Recommended: Arduino Uno R3 Beginners Kit — The Uno’s ATmega328P is the reference AVR for learning timer registers, with extensive documentation and community support. The perfect platform for mastering these concepts.

Arduino Timer Overview: Timer0, Timer1, Timer2

The ATmega328P (used in the Arduino Uno, Nano, and Mini) has three hardware timers:

Timer0 (8-bit)

Timer0 is an 8-bit timer used by the Arduino core for millis(), micros(), and delay(). It also drives PWM on pins D5 and D6. Avoid reprogramming Timer0 unless you understand that doing so will break millis() and delay(). If you must use it, save and restore its registers or implement your own time tracking.

Timer1 (16-bit)

Timer1 is a 16-bit timer, meaning its counter register (TCNT1) counts from 0 to 65535. This gives far greater resolution for time measurement and PWM generation. It drives PWM on pins D9 and D10. The Servo library also uses Timer1, so if you use that library, Timer1 is already claimed.

Timer2 (8-bit)

Timer2 is an 8-bit timer that drives PWM on pins D3 and D11. Unlike Timer0 and Timer1, Timer2 can operate independently from the main clock using an external 32.768 kHz crystal for real-time clock applications. The Tone library uses Timer2.

Key Timer Registers Explained

Each timer has a set of special function registers (SFRs) that control its operation. Here are the most important ones for Timer1:

TCCR1A and TCCR1B — Timer/Counter Control Registers

These two registers control the timer’s operating mode (CTC, Fast PWM, Phase Correct PWM, etc.) and the clock source / prescaler. TCCR1A holds the Waveform Generation Mode (WGM) bits 0 and 1 plus the Compare Output Mode (COM) bits. TCCR1B holds WGM bits 2 and 3 plus the Clock Select (CS) bits.

TCNT1 — Timer/Counter Register

This is the actual 16-bit counter value. You can read it at any time to get the current count, or write to it to set a starting value. Reading TCNT1 is the basis for microsecond timing without interrupts.

OCR1A and OCR1B — Output Compare Registers

When TCNT1 matches OCR1A or OCR1B, the timer triggers a compare match event. In CTC mode, this resets the counter and fires an interrupt. In PWM modes, it toggles the corresponding output pin.

ICR1 — Input Capture Register

In some modes, ICR1 sets the TOP value (maximum count) instead of OCR1A. This is useful when you want to use both OCR1A and OCR1B for independent PWM outputs while still controlling the period.

TIMSK1 — Timer Interrupt Mask Register

This register enables or disables specific timer interrupts. Set the OCIE1A bit (bit 1) to enable the Output Compare A Match interrupt, which fires when TCNT1 == OCR1A.

Recommended: Arduino Frequency Counter Kit with 16×2 LCD Display — A practical kit that uses Timer1’s input capture mode to measure signal frequencies — an excellent hands-on project to solidify your understanding of Arduino timer registers.

Prescalers and Frequency Calculation

The timer clock is derived from the system clock (16 MHz on most Arduinos) divided by a prescaler. Available prescalers for Timer1 are: 1, 8, 64, 256, and 1024.

The timer tick period is: Tick = Prescaler / F_CPU

For a 1 Hz interrupt using CTC mode with Timer1:

OCR1A = (F_CPU / Prescaler) - 1

With Prescaler = 1024:
OCR1A = (16,000,000 / 1024) - 1 = 15,624

The timer counts from 0 to 15,624 (15,625 ticks total at 15,625 Hz), firing the interrupt exactly once per second. The 16-bit Timer1 can hold values up to 65,535, making it perfect for low-frequency interrupts.

For Timer0 and Timer2 (8-bit, max value 255), you cannot achieve 1 Hz directly — you would need to count interrupts in software and act every Nth interrupt.

CTC Mode: Generate Precise Timed Interrupts

CTC (Clear Timer on Compare Match) is the simplest mode for generating regular timed interrupts. Configure it like this:

void setup() {
  cli(); // Disable global interrupts during configuration

  // Reset Timer1 control registers
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;

  // Set Compare Match Register for 1 Hz
  OCR1A = 15624; // = (16*10^6) / (1024*1) - 1

  // CTC mode: WGM12 = 1
  TCCR1B |= (1 << WGM12);

  // Prescaler 1024: CS12=1, CS10=1
  TCCR1B |= (1 << CS12) | (1 << CS10);

  // Enable compare interrupt
  TIMSK1 |= (1 << OCIE1A);

  sei(); // Re-enable interrupts
}

ISR(TIMER1_COMPA_vect) {
  // This runs exactly once per second
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}

void loop() {
  // Main loop is completely free!
}

This is a transformative shift in how you think about Arduino programs. The LED blinks at exactly 0.5 Hz (1 toggle per second) and your loop() can do anything else simultaneously — read sensors, handle serial, update a display — with no timing interference.

Fast PWM Mode: Custom PWM Frequencies

The analogWrite() function generates PWM at a fixed frequency determined by the Arduino core (490 Hz on most pins, 980 Hz on D5 and D6). For applications like motor drivers, audio, or LED drivers, you often need a different frequency.

Here is how to set Timer1 for Fast PWM at a custom frequency using ICR1 as TOP:

// Fast PWM on D9 (OC1A) at 25 kHz, 50% duty cycle
void setup() {
  pinMode(9, OUTPUT);

  // WGM13=1, WGM12=1, WGM11=1 → Fast PWM with ICR1 as TOP
  TCCR1A = (1 << COM1A1) | (1 << WGM11);
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10); // No prescaler

  ICR1 = 639;  // TOP = (F_CPU / frequency) - 1 = (16000000/25000) - 1 = 639
  OCR1A = 319; // 50% duty cycle = ICR1 / 2
}

void loop() {
  // Adjust OCR1A from 0 to ICR1 to change duty cycle
}

At 25 kHz, this PWM frequency is above the audible range — ideal for PC fan speed control (4-pin fans use 25 kHz PWM) and motor drivers where you want silent operation without annoying whine.

Recommended: Multifunction Shield For Arduino Uno / Leonardo — Includes LEDs, buttons, a buzzer, and a 4-digit display, providing an excellent test platform for experimenting with timer-controlled outputs like PWM dimming and tone generation.

Practical Examples

Example 1: Non-Blocking Multi-Rate Task Scheduler

Use Timer2 at 1 kHz to build a simple software task scheduler, calling different tasks at different rates:

volatile uint16_t ticks = 0;

ISR(TIMER2_COMPA_vect) {
  ticks++;
}

void setup() {
  // Timer2 CTC, 1 kHz
  TCCR2A = (1 << WGM21);
  TCCR2B = (1 << CS22); // Prescaler 64
  OCR2A  = 249;          // (16000000 / 64 / 1000) - 1
  TIMSK2 = (1 << OCIE2A);
  sei();
}

void loop() {
  if (ticks % 10 == 0)  task_10ms();  // 100 Hz task
  if (ticks % 100 == 0) task_100ms(); // 10 Hz task
  if (ticks % 1000 == 0) task_1s();   // 1 Hz task
}

Example 2: Microsecond Pulse Width Measurement

Use Timer1 at no prescaler (1 tick = 62.5 ns) to measure pulse widths with sub-microsecond resolution:

uint16_t pulseStart, pulseWidth;

void setup() {
  TCCR1A = 0;
  TCCR1B = (1 << CS10); // No prescaler
  Serial.begin(115200);
  attachInterrupt(0, isr, CHANGE); // D2
}

void isr() {
  if (digitalRead(2)) {
    pulseStart = TCNT1;
  } else {
    pulseWidth = TCNT1 - pulseStart;
    // Convert to microseconds: pulseWidth * 0.0625
  }
}

Using millis() and micros() Correctly

Even without direct register manipulation, you can achieve non-blocking timing using millis() — which reads a counter updated by Timer0’s ISR. The key pattern is storing a timestamp and comparing elapsed time:

unsigned long lastRun = 0;
const unsigned long INTERVAL = 500;

void loop() {
  if (millis() - lastRun >= INTERVAL) {
    lastRun = millis();
    // Do something every 500 ms
  }
  // Everything below runs at full speed
}

Note: millis() has a resolution of approximately 1 ms (limited by Timer0’s ISR rate). micros() has a resolution of 4 µs on a 16 MHz Arduino. Neither is suitable for sub-microsecond timing — for that, read TCNT1 directly with prescaler 1 as shown above.

Recommended: Arduino Mega 2560 R3 Board — The Mega has six hardware timers (Timer0 through Timer5), giving you multiple independent CTC/PWM channels for complex multi-task projects without any resource conflicts.

Frequently Asked Questions

Will changing Timer1 settings break the Servo library?

Yes. The Arduino Servo library uses Timer1 on the Uno/Nano. If you reconfigure Timer1’s registers, you must not include the Servo library in the same sketch — or use an alternative servo library that targets a different timer (such as ServoTimer2 on the Mega’s Timer2 or the Mega’s additional timers).

Does reprogramming Timer0 break millis() and delay()?

Yes. Timer0 drives millis(), micros(), and delay(). Changing its prescaler or mode will make these functions return incorrect values or stop working entirely. If you need Timer0 for something else, replace time tracking with Timer1-based code or avoid delay() and millis() entirely.

How do I generate a precise 38 kHz IR carrier for IR remote control?

Set Timer2 in Fast PWM mode with a prescaler of 1 and OCR2A = (16,000,000 / 38,000) – 1 ≈ 420. Use COM2A0=1 to toggle OC2A (pin D11) on each compare match, producing a 38 kHz square wave. The IRremote library handles this for you automatically, but understanding the underlying mechanism helps with debugging.

Can I use timer interrupts on the Arduino Mega for more simultaneous tasks?

Yes. The ATmega2560 has six 16-bit and 8-bit timers, so you can have multiple completely independent timed tasks. Timer3, Timer4, and Timer5 are all 16-bit and are not used by the Arduino core, making them freely available for your own CTC or PWM configurations.

What happens if my ISR takes too long to execute?

If your ISR execution time exceeds the timer period, the next timer interrupt will be queued but the ISR must finish first. This causes the next interrupt to fire immediately after the previous ISR returns, effectively doubling up on interrupts and causing timing drift. Keep ISRs as short as possible — set a flag and do the work in loop() instead of computing inside the ISR.

Mastering Arduino timer registers will permanently change how you write embedded code. Your programs become more responsive, your timing becomes accurate, and your projects can handle multiple tasks that would be impossible with delay(). Explore Arduino boards and accessories on zbotic.in to find the right platform for your timing-critical applications.

Tags: arduino timer registers, arduino timing, avr timers, delay alternative, pwm arduino, timer interrupt
Share Post
  • Facebook
  • Linkedin
  • Whatsapp
Arduino Sensor Shield: Connect...
blog arduino sensor shield connect multiple sensors without mess 594683
blog arduino mkr wifi 1010 vs uno when to upgrade 594688
Arduino MKR WiFi 1010 vs Uno: ...

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