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()ormillis()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
| Register | Purpose | Example Usage |
|---|---|---|
| TCCRnA | Control timer mode and output | TCCR1A = 0; |
| TCCRnB | Set prescaler and mode bits | TCCR1B |= (1 << CS12) | (1 << CS10); |
| TCNTn | Current timer count | TCNT1 = 0; |
| OCRnA | Compare match value A | OCR1A = 15624; |
| TIMSKn | Timer interrupt mask | TIMSK1 |= (1 << OCIE1A); |
| sei() | Enable global interrupts | sei(); |
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.