Skip to main content
2 of 2
Added more explanations.
Nick Gammon
  • 38.9k
  • 13
  • 70
  • 126

However on an oscilloscope I see the pin toggle each 1ms.

That's what you expect. You set up the timer to fire every 1 ms. Since the ISR is called some time after that (and that is what toggles the pin) you will see the pin toggle every 1 ms.

I've reworked your example a bit to make it clearer, and to toggle the pin when we start the timer, and again when the ISR fires:

uint32_t timestamp;

void setup() 
  {
  pinMode(9, OUTPUT);
  // Timer2 for counting 
  TCCR2A = bit (WGM21); // CTC mode, clear timer on compare match
  OCR2A = 99;           // ticks, before ISR is called

  // counting time[s] = (OCR+1)/(F_cpu/prescalar)
  // 99 = 400us | 124 = 500us
  timestamp = micros();
  }  // end of seupt

void loop() 
{
  uint32_t now = micros();
  if (now - timestamp >= 1000) 
    {
    timestamp = now;
    TIFR2 = bit (OCF2A);   // clear any pending interrupt
    TCNT2 = 0;             // make sure counter is zero
    TIMSK2 = bit (OCIE2A); // enable timer compare interrupt
    TCCR2B = bit (CS22);   // start timer: Prescalar 64
    PINB = bit (1);        // toggle pin 9
    }  // end of if
}  // end of loop

ISR(TIMER2_COMPA_vect) 
{
  PINB = bit (1); // toggle pin 9
  TCCR2B = 0;     // stop timer
}  // end of TIMER2_COMPA_vect

You can see from the scope output that the timer is started every 1 ms (as expected) because Cursor 1 is at -1 ms. The width of the pulse is 400 µs, as expected.

Timer 2 scope output


In my example I do not have to set so many settings each time I want to start the timer.

I took out the cli and sei so I saved you two lines. Don't forget to count them!

If you leave the timer running, it may well have reached the counter again (even though not generating an interrupt). Thus this line is important (even more so for you):

TIFR2 = bit (OCF2A);   // clear any pending interrupt

Also, if you leave the timer running, you may have a race condition. Say the timer is just about to tick over and generate the interrupt. You clear the flag (as shown above), but one machine cycle later the timer reaches the counter and sets it again, just before you get a chance to zero it. With the timer running, there could be combinations of when you enable interrupts, zero the flag, and clear the pending interrupt flag, that could occasionally work against you. Having the timer stopped is much safer.

Now if you are worried about the number of lines you need to start it (I wouldn't be) then you can move this line into setup:

TIMSK2 = bit (OCIE2A); // enable timer compare interrupt

Since you stop the timer in the ISR it doesn't matter if the interrupt is still enabled. Now you just have three lines to start the timer:

TIFR2 = bit (OCF2A);  // clear any pending interrupt
TCNT2 = 0;            // make sure timer is zeroed
TCCR2B = bit (CS22);  // start timer: Prescalar 64

I think we've agreed that clearing the compare flag is important, so this isn't any lengthier.


looking at your divisioning of the time axis, and from code, it is clear, that there is a 600us break, not 1ms!

Yes, there's a 600 µs break, because you start the timer every 1 ms, and it runs for 400 µs, thus there will be a 600 µs break.


I seek to call an ISR routine after a fixed time of 400us.

That's exactly what my posted code does. The pin goes high at the time you desire, in loop, and it goes low 400 µs later. That's what you wanted: a 400 µs interval.

Nick Gammon
  • 38.9k
  • 13
  • 70
  • 126