2

I am trying to use one of the hardware timers on the arduino uno to generate a short pulse some number of microseconds after receiving a pulse on an input.

Currently my code looks like this:

uint16_t pulse_delay = 12000; //half-microseconds
uint16_t pulse_length = 20;

void setup(){
  pinMode(8, INPUT);
  pinMode(9, OUTPUT);
  TCCR1A = 0;
  TCCR1B = _BV(ICNC1)  //input capture noise cancel
      | _BV(ICES1) //positive edge
      | _BV(CS11); // /8 prescaler
  TIMSK1 = _BV(ICIE1); //enable input capture interrupt
}
void loop(){}
ISR(TIMER1_CAPT_vect){
  TCCR1A = _BV(COM1A0) | _BV(COM1A1); //set OC1A on match
  TIMSK1 |= _BV(OCIE1A); //enable match interrupt
  OCR1A = pulse_delay; //pulse begin time

  TCNT1 = TCNT1 - ICR1; //TCNT1 now contains time since input pulse, even if 
                        //the interrupt isn't run immediately
}
ISR(TIMER1_COMPA_vect){
  TIMSK1 &=~ _BV(OCIE1A); //disable match interrupt
  TCCR1A = _BV(COM1A1); //clear OC1A on match

  OCR1A = pulse_delay + pulse_length;
}

This code should theoretically do the task, but it doesn't produce any output at all - to my oscilloscope it looks like the output pin just stays low.

However, if I replace the last line (OCR1A = pulse_delay + pulse_length;) of the compare match interrupt with the following two lines, it outputs a pulse just fine. The issue with this is that it uses significantly more CPU time, and it can only count time from when the interrupt starts so if the interrupt is executed late the pulse will be longer.

  delayMicroseconds(pulse_length);
  TCCR1C = _BV(FOC1A); //manually trigger match event

All that the first version is doing differently is triggering the match event via an 'alarm' set on the timer, rather than waiting to trigger the match manually.

Why does the first version not work, and how can I make it work??

  • Just a quick guess, which I may develop into a complete answer when I have time, a little bit later: do TIFR1 = _BV(OCF1A); right before TIMSK1 |= _BV(OCIE1A);. – Edgar Bonet Feb 23 '17 at 10:49
  • Also just a guess. Instead of OCR1A = pulse_delay + pulse_length I'd just try OCR1A = TCNT1 + pulse_length; – 6v6gt Feb 23 '17 at 11:49
  • @6v6gt I have tried that and it also fails. It also has the problem that it lengthens the pulse by the amount of time spent waiting for interrupts to be re-enabled in case the interrupt is triggered during a noInterrupts() block. – AJMansfield Feb 23 '17 at 16:46
2

Expanding on my comment, after checking experimentally that this was indeed the issue.

Short answer

You should do

TIFR1 = _BV(OCF1A);  // clear interrutp flag

right before

TIMSK1 |= _BV(OCIE1A);  // enable match interrupt

Explanation

Whenever the timer reaches the value stored in one of its “compare match” registers, this triggers a “compare match” event, which has the effect of raising the corresponding interrupt flag (OCF1A in your case). If the associated interrupt is enabled, that fires an interrupt request, and the flag is automatically cleared when the CPU starts executing the interrupt vector.

However, if the interrupt is not enabled, the flag just stays set. If you later enable the interrupt when the flag is already set, the interrupt request is sent immediately. This is not what you want, you want the interrupt to fire only on the next match. For this to happen, you have to clear the interrupt flag prior to enabling the interrupt.

The interrupt flags have a very non-intuitive behavior: you clear the flag by writing a logic one to it. Thus the line:

TIFR1 = _BV(OCF1A);  // clear interrutp flag

For completeness, I would also clear ICF1 before enabling ICIE1, although this seems to not be critical for the working of your program.

Also, as a side note, be aware that the timer can increment while the CPU is in the middle of executing TCNT1 = TCNT1 - ICR1;. If you want the cleanest possible timings, leave the timer free-running and just set OCR1A to the appropriate values: ICR1+pulse_delay, then ICR1+(pulse_delay+pulse_length).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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