1
\$\begingroup\$

I'm trying to use an ATTiny84's INT0 pin to detect a falling edge event on a 1 kHz signal.

What is the correct way to temporarily halt this interrupt INT0, and then resuming it at a later time, without skewing its response time?

I'm having difficulty halting the interrupt, performing a time-consuming operation, and then resuming the interrupt. Afterwards the interrupt's response seems to have drifted very badly from its original position.

I've been using cli() to stop it, and sei() to restart it, but this seems not to work reliably. Is there a better way?

If I do not perform any halt and restart of the interrupt, then the ISR performs solidly and in lock-step with the 1 kHz signal flawlessly. If I do halt, pause for a while and resume the interrupt it seems to "remember" that it needs to call the ISR at an unpredictable moment and loses sync with the 1 kHz signal and never regains sync.

\$\endgroup\$
4
  • \$\begingroup\$ It seems that GIFR = 0xff; fixes the problem. Is this register still active even after cli(); is called? \$\endgroup\$ May 16 at 21:27
  • 4
    \$\begingroup\$ When you "pause" the interrupt, you still need to clear it from the registers, otherwise when you re-enable the interrupt(s) it'll trigger immediately. \$\endgroup\$
    – Aaron
    May 16 at 21:34
  • 1
    \$\begingroup\$ "Afterwards the interrupt's response seems to have drifted very badly from its original position." This has perhaps nothing to do with the interrupting itself, but how long you keep the flag disabled as well as how you re-enable the interrupt. A common mistake is to update timers inside the ISR from the value of a free running timer, instead of updating it based on the previous value inside it's individual timer register. If you do it from a free running timer, the timer's period and real-time characteristics will drift away, since you don't compensate for interrupt latency. \$\endgroup\$
    – Lundin
    May 17 at 6:18
  • 1
    \$\begingroup\$ Anyway, I think you need to post the code for setting up the interrupt, the ISR and the disable/enable code in the background program. Sure someone can answer and tell you what the actual correct flag instead of using cli/sei is named, but the manual ought to do that as well. \$\endgroup\$
    – Lundin
    May 17 at 6:20

1 Answer 1

0
\$\begingroup\$

As is typical, I managed to stumble on a solution about an hour after I posted the question!

Here's a severely truncated code example. This is not a complete program so please don't expect it to do anything useful.

(Thank you @Lundin for some useful further improvements to my naive code...)

//Code sample for an Atmel ATTiny84A (DIP package) microcontroller

void Init_Interrupt_Handler()
{
  //the "INT0" interrupt is used... 

  MCUCR |= 0b00000010; //generate interrupts on falling edge of physical pin 5 (PB2)
  GIMSK |= 0b01000000; //enable INT0 interrupt
}

volatile unsigned char triggered = 0;
ISR(INT0_vect)
{
  //this ISR is called when a falling edge is detected on physical pin 5 (PB2)

  triggered = 1; //a signal to tell the while(1) loop in main to do something
}

void SlowFunction()
{
  //something that takes a long time...
}

int main(void)
{
  Init_Interrupt_Handler();

  sei(); //enable global interrupts

  while(1)
  { 
    if(triggered)
    {
      //turn off the INT0 interrupt until we've finished reading the data
      GIMSK &= ~0b01000000;

      triggered = 0; //this must be cleared when the interrupt is NOT enabled

      SlowFunction();

      //purge interrupt flag so that we don't get a spurious interrupt immediately
      GIFR |= (1 << INTF0);

      //reenable the INT0 interrupt in readiness for the next round
      GIMSK |= 0b01000000;
    }
  }
}

So the solution is to write a 1 into the INTFO bit of the GIFR register immediately before re-enabling the INT0 ISR.

Indeed, now I can have SlowFunction() take as long as it needs, and afterwards I can resume responding to the falling edges seamlessly and with no timing jitter of any kind.

Thanks to those that replied in the comments above.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ triggered = 0; you have a race condition bug here. Even if you don't need to worry about the ISR writing to that variable as main is reading it (which could also be an issue), you still have the problem of an interrupt hitting between that line and the cli call. \$\endgroup\$
    – Lundin
    May 19 at 12:49
  • 1
    \$\begingroup\$ Apart from that, the method you use here that toggles the global interrupt mask with cli/sei is almost always bad practice. You should indeed look for the option to enable/disable the specific interrupt instead. And in general you shouldn't disable interrupts for long periods of time either. \$\endgroup\$
    – Lundin
    May 19 at 12:50
  • \$\begingroup\$ @Lundin, thank you for these comments. I will alter the code in the answer. \$\endgroup\$ May 20 at 17:53

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.