Can you trigger an external interrupt event by setting a flag with software?

Hi.

Regarding the EIFR - External Interrupt Flag Register ATMEGA 2560 datasheet Chpt. 15.2.4 Pg. 115.

The datasheet says a logic change on a pin will cause its corresponding INTFn bit to be set (one). It also says the flag can be cleared by writing a logical one to it.

Isn't setting the bit (one) and writing a logical 1 to it the same thing? If not, what do I write to it to set the flag to affect an interrupt?

What I want to do is set the bit by code to trigger an interrupt event. But I can't see any way of doing it.
Writing a (zero) to the bit doesn't change the bit state so I can't trigger an interrupt that way. The only thing I can think of is to set the pinMode(INT4PIN, OUTPUT) and digitalWrite(INT4PIN, LOW), to affect a logic change on the pin. This is annoying because I have to put a resistor in series to protect the pin and this may affect the circuit triggering the interrupt. This is not an elegant solution.

Is there a way to trigger an external interrupt using software only?

P.S. I have read "Why are (many) interrupt flags cleared by writing a logical 1?" from the frequently asked questions section of the Atmel AVR libc reference manual. I love how they say "The solution is simple". Not to me it's not!

ref: Smart | Connected | Secure | Microchip Technology

"The solution is simple: writing a logical 1 to it requires only a single OUT instruction, and it is clear that only this single interrupt request bit will be cleared. There is no need to perform a read-modify-write cycle (like, an SBI instruction), since all bits in these control registers are interrupt bits, and writing a logical 0 to the remaining bits (as it is done by the simple OUT instruction) will not alter them, so there is no risk of any race condition that might accidentally clear another interrupt request bit. So instead of writing
TIFR |= _BV(TOV0); /* wrong! */
simply use
TIFR = _BV(TOV0);"

Everyone seems preoccupied with clearing the flag. Nothing on how to set the bit to cause an interrupt event however.

"Why would I wan't to?" you ask. After all, external interrupts are for being triggered externally. Well... I have a D.P momentary push button switch that both switches on power to the MEGA and also triggers an interrupt on pin2/INT4. The MEGA can be switched on by various means, so when the AVR fires up the first thing it wants to know is if it has been switched on by the "interrupt switch". If so it wants to execute the ISR. This same switch may be used to fire an interrupt at any time while the MEGA is active (the very purpose of interrupts).

The MEGA is not switched on the first time the button is pressed. Pressing the button puts power to the MEGA via a mosfet and also stores a charge in a capacitor which puts a temporary voltage on pin2/INT4 for a few seconds. The MEGA powers up and immediately checks pin2. If pin2 is High it knows the button was pressed and wants to execute the ISR. The interrupt is configured "rising edge". Because of the capacitor the falling edge doesn't occur until a few seconds after the button press. I configure it as RISING because I want the ISR to execute immediately. No-one wants to press a button and wait two seconds for a response, it just doesn't inspire confidence.

But, the first time the button is pressed the MEGA is off. It can't detect the rising edge, only the falling edge when the capacitor bleeds down. "Why not configure it FALLING initially and change it to RISING later?" you ask. After power up I don't want to hang around waiting for a possible interrupt from a falling edge. I want to know if pin2 is HIGH straight away and act on it. The most elegant solution would be to flag the EIFR in software to fire the interrupt, but I can't figure out how when only the AVR can affect an interrupt by setting the bit (one). When I set the bit (one) the bloody flag is cleared!

Please tell me I'm a complete NOOB, that I clearly don't know what I'm talking about, and that YES you can set the register from software to fire an interrupt.

Cheers All.

Isn't setting the bit (one) and writing a logical 1 to it the same thing? If not, what do I write to it to set the flag to affect an interrupt?

No. This particular flag is cleared by writing 1 to it, and unaffected by writing 0 to it.

"Why would I wan't to?" you ask.

Indeed.

According to the datasheet, section 14:

Observe that, if enabled, the interrupts will trigger even if the INT7:0 or PCINT23:0 pins are configured as outputs. This feature provides a way of generating a software interrupt.

I configure it as RISING because I want the ISR to execute immediately.

I think this is flawed. A powered-off processor can hardly notice a RISING level change. Powering on takes time, by which time it will not notice the rising signal.

I don't see why, upon booting, you can't just check this pin yourself. What is the obsession with calling an ISR?

void myISR ()
  {
  doStuff ();
  }

void doStuff ()
  {
  // handle button press
  }

void setup ()
  {

  // we got the power-on "interrupt"
  if (digitalRead (whatever) == HIGH)
    doStuff ();

 ...

Thanks for your reply Nick. You mustn’t sleep!

You picked up on the very point I was trying to explain; that the AVR can’t detect the rising edge because it is in the process of powering up. That’s why the capacitor is there to leave a “footprint” of the button press, if you like.

I didn’t read chapter 14. Thanks for explaining that you can’t affect an external interrupt by setting the EIFR flag via software.

I have this obsession with executing the ISR because the ISR calls other routines which are shared with other conditionals in the loop. It gets messy.

Well, I put in the 220ohm resistor and affected the interrupt by setting pinMode(2, OUTPUT). Works a trick. I share my code with you for your amusement. One last question. See where I use EIFR = bit (INTF4); is this correct or should I be using bitSet? It works anyway. Cheers.

void setup()
{
EICRB |= (1 << ISC41); // Trigger INT4 on falling edge.
if (digitalRead(manualSwitchPin)==LOW) EICRB |= (1 << ISC40); // if switch wasn’t pressed set
EIMSK |= (1 << INT4); // trigger on rising edge
sei();

if (digitalRead(manualSwitchPin)==HIGH)
{
pinMode(manualSwitchPin, OUTPUT);
while(digitalRead(manualSwitchPin)==HIGH); // INT4 triggers here on falling edge
pinMode(manualSwitchPin, INPUT); // returns here from ISR
debounceInterrupt(); // MSK deactivated prior to leaving last routine shared by ISR:
cli(); // - if (ISRcalled==true) EIMSK &= ~(1 << INT4);
EICRB |= (1 << ISC40); // set rising edge
EIFR = bit (INTF4);
EIMSK |= (1 << INT4);
sei();
ISRcalled=false;
}
} // end of setup()

ISR(INT4_vect)
{
ISRcalled=true;
manuallyActivate();
}

Oh, and I saw your picture, standing by the archerII. I am a L.A.M.E. and remember fixing the dimmable lighting console in one of those during my apprenticeship. Took me a whole day to get it in and out from behind the dash. And when I re-installed it I discovered I had put the transistor in backwards. Click the knob on and it went full bright, then dimmed as you turned it further. I left it like that, the owner didn’t seem to mind but wouldn’t pay the bill. Life is funny, fond memories of the bug smashers. Of course its all turbine work now.

Cheers again and happy aviating.

    EIFR = bit (INTF4); 
    EIFR = 1 << INTF4;   
    bitSet (EIFR, INTF4);

All those lines do the same thing. Probably bitSet is the more readable.

Oh, and I saw your picture, standing by the archerII. I am a L.A.M.E. and remember fixing the dimmable lighting console in one of those during my apprenticeship.

That particular aircraft had just had its 100 hour service, and I foolishly didn’t notice that the compass was jammed. It always pointed North or something. I set the DG* to what it said, took off, and then half an hour later “corrected” the DG to the compass (which was wrong) and eventually got myself quite confused.

It reminds me of the days when I worked as a computer operator and the engineers would come and service the mainframe every few weeks. Usually immediately afterwards something would go wrong.

  • Directional Gyro

Yeah, it gets spooky when you don't know where you are.

Thanks again for the help.

Cheers.

If you want to run the ISR at boot time, why not call it as a subroutine? The inverse housekeeping (if there is any) is already at the end of the ISR, followed by the RETI instruction, which only pulls the return address off the stack and loads it into the PC.

Edit: I haven't looked up the full info on interrupt processing, it will be today's project for me. The datasheet must not be the right document or I haven't found the right section.

It's something that is possible on any microprocessor that I've ever seen.