AVR Interrupt Configuration

318 views Asked by At

The problem is that the timing behavior is totally wrong (factor 30).

I have an ATmega 644PA which is running with 8 Mhz (CKDIV8 fuse is not set).
I wanted an interrupt every 1 ms. I use Timer1 (16 bit timer) in the compare mode.
The prescaler was choiced by the timing range which i need.
Here 1 ms to ~2s. (limited through the 16 bit timer reg.)

Calculation

CPU: 8.000.000 Hz
Prescaler: 256

8.000.000 Hz / 256 = 31250 Cycles / s (Hz)

1000 ms = 31250 Cycles
1 ms = 30.25 Cycles (31.25 - 1, timer reg. start by 0)

From here i can calculate it up, for example:
100 ms = 3025 Cycles
2s = 60500 Cycles

Code

Timer Init

    cli();                  // disable global interrupts
    TCCR1A = (1 << WGM01);  // CTC ON
    TCCR1B = 0x04;          // Prescaler 256
    OCR1A  = 30;            // set compare reg.
    TIMSK1 = (1 << OCIE1A); // set interrupt mask
    TCNT1  = 0x00;          // set counter reg. to zero
    sei();                  // enable global interrupts

ISR:

ISR(TIMER1_COMPA_vect)
{
  // start own code
  ...
  // end own code
  TCNT1 = 0x00; // reset counter reg. to zero after match (same which should CTC do)
}

Now i have for example a volatile variable which will be incremented in the ISR function.
I'm polling the variable and wait so long till it reach 60500, which is equal to 2s.
After it i just set an led on.

I measured it by phone and it takes 1 minute till the led goes on.
It takes to long with the factor 30 => 60s divided by 2s are 30.
I also tried to use an external program to calculate it, it came to the same results. (0x1E = 30)
Screenshot

Someone an idea where my mistake is ?

1

There are 1 answers

1
UncleO On BEST ANSWER

2 seconds is 60500 cycles. But your ISR is incrementing the volatile variable every 1 millisecond. Naturally, it will take 60.5 seconds for the variable to reach 60500.

As a side note, it is better to change the compare value, rather than reset TCNT, to avoid timing drift. That is, let the counter run freely, and use OCR1A += 31; to hit the next interrupt. Resetting TCNT as you do will ignore the time inside the ISR.