2
\$\begingroup\$

I am trying to use one of the hardware timers on the ATmega 328/p (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??

\$\endgroup\$
2
  • \$\begingroup\$ Did you forget to set bit 7 in SREG? That's the global interrupt enable bit. \$\endgroup\$
    – Envidia
    Mar 1, 2017 at 23:45
  • \$\begingroup\$ @Envidia, I already got an answer here: arduino.stackexchange.com/a/35014/401 , but it turns out that I forgot to clear FOC1x before re-enabling the interrupt, since the interrupt flag gets set on compare match even if the corresponding interrupt is disabled. \$\endgroup\$ Mar 3, 2017 at 21:46

1 Answer 1

0
\$\begingroup\$

I didn't read the codes that closely but it shouldn't this complex. Assuming the desired duration is reasonable large vs. Isr overhead, all you need is to use the signal to trigger an Isr (a timer cannot be set up as an external interrupt) and in that Isr ttriigger a series of delayed pin actions.

The situation gets tricky if the desired duration is very short.

\$\endgroup\$
4
  • \$\begingroup\$ The code needs to be this complex because the pulse length needs to be quite short (only 10 µs), very precise (such that extra time spent waiting for uninterruptible code to finish is unacceptable), and I intend to have a whole lot of other code running on the arduino at the same time - this is not the only thing this arduino will be doing, and I need every cycle I can get for its other tasks. \$\endgroup\$ Feb 23, 2017 at 16:40
  • \$\begingroup\$ If that's the case think about ways to use the pwm module to generate a single pulse. You can set up the module periods and duty cycle in advance but otherwise has it disabled. \$\endgroup\$
    – dannyf
    Feb 23, 2017 at 16:57
  • \$\begingroup\$ I'm writing a library here that I intend to use in a large number of projects, and want it to (a) work perfectly, (b) be as fast as possible, and (c) conflict as little as possible with other code. Using an extra library to do this is not conducive to any of those three goals. \$\endgroup\$ Feb 23, 2017 at 17:23
  • \$\begingroup\$ In any case, I already got a real answer here: arduino.stackexchange.com/a/35014/401. Turns out I forgot to clear the interrupt flag before re-enabling the compare match interrupt. \$\endgroup\$ Feb 23, 2017 at 17:24

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.