0
0
AutocadHow-ToIntermediate · 4 min read

How to Use Timer Registers Directly in Arduino for Precise Control

To use timer registers directly in Arduino, you write to specific hardware registers like TCNT1 for the timer counter or TCCR1A for control settings. This lets you configure timers precisely for tasks like PWM or interrupts without relying on Arduino functions.
📐

Syntax

Arduino timers are controlled by special registers. For example, TCCR1A and TCCR1B set timer modes and prescalers, while TCNT1 holds the current timer count. You write values directly to these registers to configure the timer.

  • TCCRnA: Timer/Counter Control Register A (mode and output settings)
  • TCCRnB: Timer/Counter Control Register B (prescaler and mode bits)
  • TCNTn: Timer/Counter Register (current timer value)
  • OCRnA, OCRnB: Output Compare Registers (for PWM or interrupts)

Replace n with the timer number (e.g., 0, 1, 2).

arduino
TCCR1A = 0;       // Clear control register A
TCCR1B = 0;       // Clear control register B
TCNT1 = 0;        // Reset timer count
TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
TCCR1B |= (1 << CS10); // Start timer with no prescaling
OCR1A = 15624;    // Set compare match value for 1Hz (assuming 16MHz clock)
TIMSK1 |= (1 << OCIE1A); // Enable timer compare interrupt
💻

Example

This example sets up Timer1 to generate an interrupt every second. It toggles the built-in LED on pin 13 each time the interrupt fires, showing how to use timer registers directly for precise timing.

arduino
#include <avr/interrupt.h>

void setup() {
  pinMode(13, OUTPUT);       // Set LED pin as output
  TCCR1A = 0;               // Clear control register A
  TCCR1B = 0;               // Clear control register B
  TCNT1 = 0;                // Reset timer count

  OCR1A = 15624;            // Compare match value for 1Hz (16MHz clock, prescaler 1024)
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS12) | (1 << CS10); // Prescaler 1024
  TIMSK1 |= (1 << OCIE1A);  // Enable Timer1 compare interrupt

  sei();                    // Enable global interrupts
}

ISR(TIMER1_COMPA_vect) {
  digitalWrite(13, !digitalRead(13)); // Toggle LED
}

void loop() {
  // Main loop does nothing, LED toggled by interrupt
}
Output
The built-in LED on pin 13 toggles ON and OFF every second.
⚠️

Common Pitfalls

When using timer registers directly, common mistakes include:

  • Not disabling interrupts before changing registers, causing unpredictable behavior.
  • Forgetting to enable global interrupts with sei().
  • Using wrong prescaler values or compare match values, leading to incorrect timing.
  • Conflicts with Arduino functions like delay() or millis() that use timers internally.

Always check which timers Arduino uses by default to avoid conflicts.

arduino
/* Wrong: Not enabling interrupts */
TCCR1B |= (1 << WGM12);
TCCR1B |= (1 << CS12) | (1 << CS10);
// Missing sei(); so interrupt never fires

/* Right: Enable interrupts */
sei();
📊

Quick Reference

RegisterPurposeExample Usage
TCCRnAControl timer mode and outputTCCR1A = 0;
TCCRnBSet prescaler and mode bitsTCCR1B |= (1 << CS12) | (1 << CS10);
TCNTnCurrent timer countTCNT1 = 0;
OCRnACompare match value AOCR1A = 15624;
TIMSKnTimer interrupt maskTIMSK1 |= (1 << OCIE1A);
sei()Enable global interruptssei();

Key Takeaways

Directly writing to timer registers gives precise control over Arduino timers.
Always configure timer mode, prescaler, and compare values correctly for your timing needs.
Enable global interrupts with sei() to allow timer interrupts to work.
Avoid conflicts by knowing which timers Arduino functions use by default.
Test timer code carefully to ensure correct timing and behavior.