Interrupt runs after flag cleared

Hi, I'm doing a project with a rotary encoder and I'm experimenting a little with interrupts in order to make it run better. I trying to make an interrupt-timer reenable the "encoder turn"-interrupt after a certain time has passed.

- I'm using an Arduino Mega for my project.

I have tested the disabling of the encoder-interrupt (works).
I have tested the timer interrupt (works).

The issue is that my encoder-interrupt runs after being enabled again, if I turn the rotary encoder while its interrupt is disabled. I figured the interrupt flag for the encoder-interrupt can still be 'set' even though I disabled the interrupt. The encoder-interrupt is an external interrupt on the Mega's 18th pin (RISING Edge). I disable the interrupt by clearing the bit in the EIMSK-byte.

bitClear(EIMSK, INT3); //Disable Encoder

Just before the timer-interrupt enables the encoder again, I clear what I think is the right flag -so it won't trigger the encoder-interrupt again when it gets reenabled.

ISR (TIMER4_COMPA_vect)
{
  bitClear(EIFR, 4); //Resets INTF4 (External Interrupt Flag 4)
  bitSet(EIMSK, INT3);  //Enable Encoder
}

But the interrupt still triggers. Have I misunderstood something here, or am I simply just clearing a wrong flag?


Here's the whole code - sorry if the syntax Isn't 100% right

//Encoder
#define ENCODER_A 18 //Encoder's A output
#define ENCODER_B 46 //Encoder's B output

volatile int encoderValue;
volatile bool encoderTurned;  //True if encoder position was changed

void setup()
{
  // PinModes
  pinMode(ENCODER_A, INPUT);
  pinMode(ENCODER_B, INPUT);

  //EncoderTimerINTSetup (4seconds)
  cli();
  TCCR4A = 0; //clear Timer/Counter Control Register 4A
  TCCR4B = 0; //clear Timer/Counter Control Register 4B
  TCCR4B |= B00001101; //CTC mode with 1024 prescaler
  TCNT4 = 0; //clear counter
  OCR4A = 62499;//(4s with 1024 prescaler)
  TIMSK4 |= B00000010; //Enable timer compare interrupt
  sei();

  //ExternalINTSetup
  attachInterrupt(digitalPinToInterrupt(ENCODER_A), EncoderTurnISR, RISING);

  Serial.begin(9600);

  function();
}

void loop() 
{
  // put your main code here, to run repeatedly:
}

void function()
{
  while (1)
  {
    if (encoderTurned)
    {
      encoderTurned = false;
      Serial.println("Now");
    }
  }
}

void EncoderTurnISR()
{
  encoderTurned = true;
  if (digitalRead(ENCODER_A) != digitalRead(ENCODER_B)) //Clockwise turn
  {
    encoderValue++;
  }
  else //Clockwise
  {
    encoderValue--;
  }

  //Disable Encoder... until timer activates it again
  bitClear(EIMSK, INT3);
}

ISR (TIMER4_COMPA_vect)
{
  bitClear(EIFR, 4); //Resets INTF4 (External Interrupt Flag 4) ... Issue? Clears before enable, but EncoderTurnISR() still runs for some reason
  bitSet(EIMSK, INT3);  //Enable Encoder
}
  • The Serial monitor writes "Now" twice if the encoder is: turned, disabled, turned, enabled.

Hope someone can help :slight_smile:

Check the datasheet for the 2560: To clear a flag in the EIFR you need to write a 1 to the bit position you want to clear:

.
.
.
When an edge or logic change on the INT7:0 pin triggers an interrupt request, INTF7:0 becomes set (one). If the Ibit in SREG and the corresponding interrupt enable bit, INT7:0 in EIMSK, are set (one), the MCU will jump to the interrupt vector. The flag is cleared when the interrupt routine is executed. Alternatively, the flag can be cleared by writing a logical one to it.
.
.
.

Are pullup or pulldown resistors required? Are the inputs floating somewhere between 0V and Vcc, causing phantom reads?

JCA34F:
Are pullup or pulldown resistors required? Are the inputs floating somewhere between 0V and Vcc, causing phantom reads?

No that can't be the issue, no inputs are floating.

Blackfin:
Check the datasheet for the 2560: To clear a flag in the EIFR you need to write a 1 to the bit position you want to clear:

"When an edge or logic change on the INT7:0 pin triggers an interrupt request, INTF7:0 becomes set (one).
If the Ibit in SREG and the corresponding interrupt enable bit, INT7:0 in EIMSK, are set (one),
the MCU will jump to the interrupt vector. The flag is cleared when the interrupt routine is executed.
Alternatively, the flag can be cleared by writing a logical one to it."

**That's what confuses me... **

When an edge or logic change on the INT7:0 pin triggers an interrupt request, INTF7:0 becomes set (one)

  • So the edge or logic change on the INT7:0 sets EIFR bit to 1 (interrupt requested).

To clear a flag in the EIFR you need to write a 1 to the bit position you want to clear.

  • So I want to clear this flag, in order to not make the interrupt happen. "Clear" means setting the bit to 0 right?
    ... But i need to write 1 to the bit?
    Is clearing a flag and clearing a bit not the same thing?
    Does that mean the interrupt will occur when the EIFR bit are set to 0?

edit:


OH... I just realized. It's the INTF register that triggers the external interrupts, and not the EIFR register... So EIFR register is only used to clear the INTF bits -right? Why not just set the INTF bits directly?

  • Anyway I will test this now and reply back. Thanks for the help!

jackmaro:
So I want to clear this flag, in order to not make the interrupt happen.

On the Mega the EIMSK register is the one that controls whether interrupts are active or not. And the PCMSKx registers for the pin-change interrupts

It is wise to clear the relevant EIFR bit immediately before re-enabling the interrupt in case it has already been set by an event that occurred while interrupts were disabled.

...R

Also check your pin assignments against those specified for external interrupts for your Mega:

#define ENCODER_A 18 //Encoder's A output
#define ENCODER_B 46 //Encoder's B output

Pin 18 appears to be INT.5 from here (but beware of the confusing formatting) attachInterrupt() - Arduino Reference

Pin 18 appears to be INT.5 from here (but beware of the confusing formatting) attachInterrupt() - Arduino Reference

Hmm, you appear to be right. But I'm a little confused now. On Arduino's page: https://www.arduino.cc/en/Hacking/PinMapping2560 (ATmega2560 PinMapping)
pin 18 seems to be INT3 ???

Have I misunderstood something?

On the Mega the EIMSK register is the one that controls whether interrupts are active or not. And the PCMSKx registers for the pin-change interrupts

This is what I do, but then because the rising-edge happens while disabled I need to do this:

It is wise to clear the relevant EIFR bit immediately before re-enabling the interrupt in case it has already been set by an event that occurred while interrupts were disabled.

I'm not sure I clear INTF correctly. Because the Encoder-interrupt still occurs after the timer-ISR has run.

This is the current code:

//Encoder
#define ENCODER_A 18 //Encoder's A output
#define ENCODER_B 46 //Encoder's B output

volatile bool encoderTurned;  //True if encoder position was changed

void setup()
{
  // PinModes
  pinMode(ENCODER_A, INPUT);
  pinMode(ENCODER_B, INPUT);

  //EncoderTimerINTSetup (4seconds)
  cli();
  TCCR4A = 0; //clear Timer/Counter Control Register 4A
  TCCR4B = 0; //clear Timer/Counter Control Register 4B
  TCCR4B |= B00001101; //CTC mode with 1024 prescaler
  TCNT4 = 0; //clear counter
  OCR4A = 62499;//(4s with 1024 prescaler)
  TIMSK4 |= B00000010; //Enable timer compare interrupt
  sei();

  //ExternalINTSetups
  attachInterrupt(digitalPinToInterrupt(ENCODER_A), EncoderTurnISR, RISING);

  //Inits
  Serial.begin(9600);
}

void loop()
{
  if (encoderTurned)
  {
    encoderTurned = false;
    Serial.println("Encoder Turned");
  }
}

void EncoderTurnISR()
{
  encoderTurned = true;
  bitClear(EIMSK, INT3); //Disable Encoder... until timer activates it again
  TCCR4B |= B00001101; //Enable Counter
}

ISR (TIMER4_COMPA_vect)
{
  TCCR4B |= B00000000; //disable counter
  TCNT4 = 0; //clear counter
  bitSet(EIFR, 4); //Clear INTF4 (External Interrupt Flag 4)
  bitSet(EIMSK, INT3);  //Énable Encoder
}

jackmaro:
Hmm, you appear to be right. But I'm a little confused now. On Arduino's page: Arduino - PinMapping2560 (ATmega2560 PinMapping)
pin 18 seems to be INT3 ???

Have I misunderstood something?

It is difficult to imagine that both are are correct for the current Mega 2560 version.

Here is another one. Look at the picture in the first post:

Why are you clearing the flag for INT4 and enabling INT3?

Why not do this (assuming INTF4 is the correct number)

bitSet(EIFR, INTF4);

in place of

bitSet(EIFR, 4);

...R

Why are you clearing the flag for INT4 and enabling INT3?

Oh god I'm a fool. Think I confused myself with timer 4....

I changed the bit to INT3 instead and now it works!

And in terms of the INT number, it must be 3 -since disabling and enabling bit INT3 in EIMSK does indeed enable and disable the interrupt on pin 18. I really have no clue why the attachInterrupt() page says INT.5

I'm glad it is fixed.

Anyway, this goes some way to explaining this apparent interrupt/pin conflict: interupt PINOUT arduino MEGA is wrong · Issue #5840 · arduino/Arduino · GitHub

If you look here, indeed the documentation suggestions in the above link have indeed been incorporated. Here is what the table in attachInterrupt() - Arduino Reference should look like (at least for the Mega 2560)