Why timers are so slow in Mega2560??

Hi all,

I wrote a tiny code for Mega2560 to measure ISR cycle frequency (a simple DO-toggle routine). If I set a timer interrupt with "no prescaling" option (CS00) I measure a ~260 kHz timer-cycle. If I move this code into the loop() section I measure here a ~510 kHz loop-cycle frequency.. Now I'm a little bit confused.. As I know, "no prescaling" means that timers run at osc frequency (in this case 16 MHz). The question is why I get 260 kHz with "full speed" settings???

  • George Cs. -

Your code must be really tiny - I can't even see it.

AWOL:
Your code must be really tiny - I can’t even see it.

:slight_smile:

Here it is:

volatile uint16_t counter = 0;
volatile bool toggle0 = true;

void setup(){
DDRC |= (1<<PC1); // set pin 36 output

cli();

TCCR0A = 0;
TCCR0B = 0;
TCNT0 = 0;

OCR0A = 1;

TCCR0A |= (1 << WGM01); // turn on CTC mode
TCCR0B |= (1 << CS00); // set CS00 bit for no prescaler
TIMSK0 |= (1 << OCIE0A); // enable timer comp interrupt

sei();
}

ISR(TIMER0_COMPA_vect){

// downsampling, since I have a 200 kHz scope for measuring
++counter;
if (counter>1000) {
counter = 0;

// toggle pin 36 state
toggle0 = !toggle0;
if (toggle0)
{PORTC |= (1<<PC1);} else
{PORTC &= ~(1<<PC1);}
}
}

void loop(){
}

If I set a timer interrupt with “no prescaling” option (CS00) I measure a ~260 kHz timer-cycle. If I move this code into the loop() section I measure here a ~510 kHz loop-cycle frequency…

You have an interrupt source that generates “interrupt events” at a rate far faster than the execution speed of the ISR code itself, so you are essentially measuring how many times per second the ISR can execute. That will involve looking at the instructions that actually make up the ISR, and counting the cycles. A loop has less overhead than an interrupt, and so will always be faster than ISR code for the same function. “Twice as fast” doesn’t seem unreasonable…

ISR(TIMER0_COMPA_vect){
                                                /* 6 cycles ISR response  */
 14c:   1f 92           push    r1              /* 2-cyle instr: 8 */
 14e:   0f 92           push    r0              /* 10 */
 150:   0f b6           in      r0, 0x3f        /* 11 */
 152:   0f 92           push    r0              /* 13 */
 154:   11 24           eor     r1, r1          /* 14 */
 156:   2f 93           push    r18             /* 16 */
 158:   8f 93           push    r24             /* 18 */
 15a:   9f 93           push    r25             /* 20 */

 // downsampling, since I have a 200 kHz scope for measuring
 ++counter;
 15c:   80 91 02 02     lds     r24, 0x0202     /* 22 */
 160:   90 91 03 02     lds     r25, 0x0203     /* 24 */
 164:   01 96           adiw    r24, 0x01       /* 26 */
 166:   90 93 03 02     sts     0x0203, r25     /* 28 */
 16a:   80 93 02 02     sts     0x0202, r24     /* 30 */
 if (counter>1000) {
 16e:   80 91 02 02     lds     r24, 0x0202     /* 32 */
 172:   90 91 03 02     lds     r25, 0x0203     /* 34 */
 176:   89 3e           cpi     r24, 0xE9       /* 35 */
 178:   93 40           sbci    r25, 0x03       /* 36 */
 17a:   88 f0           brcs    .+34            /* 37 */
   counter = 0;
 17c:   10 92 03 02     sts     0x0203, r1      /* usually skipped... */
 180:   10 92 02 02     sts     0x0202, r1

   // toggle pin 36 state
   toggle0 = !toggle0;
 184:   80 91 00 02     lds     r24, 0x0200
 188:   91 e0           ldi     r25, 0x01
 18a:   89 27           eor     r24, r25
 18c:   80 93 00 02     sts     0x0200, r24
   if (toggle0)
 190:   80 91 00 02     lds     r24, 0x0200
 194:   88 23           and     r24, r24
 196:   11 f0           breq    .+4
   {PORTC |= (1<<PC1);} else
 198:   41 9a           sbi     0x08, 1
 19a:   01 c0           rjmp    .+2
    {PORTC &= ~(1<<PC1);}
 19c:   41 98           cbi     0x08, 1
 }
}
 19e:   9f 91           pop     r25             /* 39 */
 1a0:   8f 91           pop     r24             /* 41 */
 1a2:   2f 91           pop     r18             /* 43 */
 1a4:   0f 90           pop     r0              /* 45 */
 1a6:   0f be           out     0x3f, r0        /* 46 */
 1a8:   0f 90           pop     r0              /* 48 */
 1aa:   1f 90           pop     r1              /* 50 */
 1ac:   18 95           reti                    /* 54 */

/* At least one non-ISR instruction executes:      55 */

So, about 55 cycles for the ISR routine. 16MHz/55 = 290kz - just about what you measured.

Is this some class assignment? There was a nearly identical question (with nearly identical code posted) over in the Arduino Due section (by a different user.)

westfw: Is this some class assignment?

No, it's just a hobby development. (I didn't know about the mentioned question in the Due section.) Actually I want to make a signal generator to generate various waveforms up to 2 kHz. I would use 64-byte lookup tables for this task so I need an accurate 128 kHz (or faster) loop for this. I'm afraid the SPI communication (DAC) and other parts of the code will not fit in 128 kHz since, based on your calculations, I have 71 free clock cycles in the ISR to realize this task..

Anyhow, thanks for your answer, it was very helpful.