Timer Basics

The ATMega328 has 3 timers that can used in various ways including keeping time, triggering interrupts, PWM etc. Arduino uses up TIMER0 for functions such as millis, micros etc.

TIMER1 and TIMER2 are available. Below is a contrived example of building a custom timer based on TIMER2. The code is designed to run on a processor clocked at 16MHz and prints out some text at 1Hz. A prescaler of 1024 is combined with a counter to 251. This gives a counter up to: 16e6/(1024*251) = 62.25. Counting up to 62 gives a time very close to 1 sec.

See the Basic AVR page for details on the registers used.

volatile uint32_t counter = 0;

// Interrupt based on Compare Match A vector
ISR(TIMER2_COMPA_vect) {
  counter++;
}

long ms = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("Ready...");

  // Reset, then setup for CTC
  TCCR2A = 0;
  TCCR2A |= _BV(WGM21);

  // Reset, then setup with prescaler: 1024
  TCCR2B = 0;
  TCCR2B |= (_BV(CS22) | _BV(CS21) | _BV(CS20));

  // Comparator A value that triggers interrupt
  OCR2A = 251;

  // Setup interrupt on Comparator Match A
  TIMSK2 |= _BV(OCIE2A);

  ms = millis();
}

uint16_t secs = 0;

void loop() {

  boolean print_time = false;

  // It's essential to suspend interrupts while manipulating counter
  uint8_t oldSREG = SREG;
  cli();
  if (counter>=(62)) {
    print_time = true;
    counter = 0;
  }
  SREG = oldSREG;
  if (print_time) {
    Serial.print(secs++);
    Serial.print("  :  ");
    Serial.println(millis()-ms,DEC);
    ms = millis();
  }
}

Leave a Reply