2

I seek to call an ISR routine after a fixed time of 400us. This action needs to be done every now and then (in the range of 0.8-3.0ms). For that matter, I thought I'd set timer2 to CTC mode, use OCR2A to set some TOP value for 400us and let the timer call the ISR routine after 400us. Within the ISR I would then stop the timer from triggering interrupts, until I need to count to 400us again, where I would activate the timer interrupt again.

This works, as expected, except for the fact, that after activating the interrupt for OCR2A again, it instantly triggers. The code below, produces a 1ms alternating signal, instead of 1ms+400us.

My question is, am I on a good way with this, or should I use the overflow ISR and preload the timer? Or any other method? The problem is, that after the 1ms waiting time in the main loop, I'd expect the counter to count from 0 to 100, meaning 400us. However on an oscilloscope I see the pin toggle each 1ms.

uint32_t timestamp;

void setup() {
  Serial.begin(115200);
  pinMode(9, OUTPUT);
  cli();         // disable global interrupts
  // Timer2 for counting (to release the pin)
  TCCR2A |= (1 << WGM21); // CTC mode, clear timer on compare match
  TCCR2B |= (1 << CS22); // Prescalar 64

  OCR2A = 99; // ticks, before ISR is called
  TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt
  TCNT2 = 0x00; // set the timer to 0
  // counting time[s] = (OCR+1)/(F_cpu/prescalar)
  // 99 = 400us | 124 = 500us
  sei(); // enable global interrups
  timestamp = micros();
}

void loop() {
  uint32_t now = micros();
  if (now - timestamp > 1000) {
    timestamp = now;
    cli();
    TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt
    sei();
    TCNT2 = 0x00;
  }
}

ISR(TIMER2_COMPA_vect) {
  // Serial.print(F("Here\n"));
  PORTB ^= 2; // set pin 9 high again
  // TCNT2 = 0x00; // reset timer for testing
  // deactivate the counter
   cli();
   TIMSK2 = 0x00; // unlink the ISR from timer
   sei();
}

1 Answer 1

2

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.

4
  • As far as I can tell, the only additional thing is TIFR2 = bit (OCF2A);. In your code, you stop the timer alltogether. Isn't it cleaner the way I did, where I just disconnect the timer from the interrupt? In my example I do not have to set so many settings each time I want to start the timer. Or is it even desirable to use the overflow timer instead,... would that result in less hastle when starting the timer?
    – mike
    Mar 29, 2016 at 6:48
  • btw.: looking at your divisioning of the time axis, and from code, it is clear, that there is a 600us break, not 1ms! Since both timers (timer0 running micros() and timer2) run parallel, it is clear, that the break is 1ms-400us = 600us.
    – mike
    Mar 29, 2016 at 7:54
  • See amended answer.
    – Nick Gammon
    Mar 29, 2016 at 20:45
  • Thanks for the additional explanation. I was originally worried about the flag, that was set on compare match, but now I totally agree, that stopping the timer is what I was looking for!
    – mike
    Mar 31, 2016 at 12:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.