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();
}
asked Mar 28, 2016 at 21:00

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.

answered Mar 29, 2016 at 0:41
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? Commented 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. Commented Mar 29, 2016 at 7:54
  • See amended answer. Commented 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! Commented Mar 31, 2016 at 12:11

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.