Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow enabling/disabling external interrupts and clear pending interrupts on attach #2159

Open
wants to merge 5 commits into
base: ide-1.5.x
Choose a base branch
from

Conversation

matthijskooijman
Copy link
Collaborator

@matthijskooijman matthijskooijman commented Jul 2, 2014

This is a fix for #510arduino/ArduinoCore-avr#244, plus the addition of a new enableInterrupt and disableInterrupt API to cater for libraries that relied on the broken behaviour of attachInterrupt.

See also this discussion: https://groups.google.com/a/arduino.cc/d/topic/developers/I9iJYIcbPLs/discussion

This code is still unfinished:

  • Only quickly compile tested
  • Not implemented on SAM yet

@matthijskooijman
Copy link
Collaborator Author

I updated the code based on feedback from @PaulStoffregen: enableInterrupt now now never clears pending interrupts, that code is moved into attachInterrupt (which always clears).

@matthijskooijman matthijskooijman added Type: Bug feature request A request to make an enhancement (not a bug fix) Component: Compilation Related to compilation of Arduino sketches Component: Core Related to the code for the standard Arduino API Version: 1.5.x Waiting for feedback More information must be provided before we can proceed and removed Component: Compilation Related to compilation of Arduino sketches labels Sep 10, 2014
On SAM:
 - all pins in a port share a single interrupt pin
 - the interrupt status flag for a pin is set even when it is disabled
 - The actual ISR does not look at the interrupt enable flags, it just
   runs the callback for every pin whos interrupt status flag is set.

This meant that if:
 - an interrupt was attached and later detached,
 - then the interrupt condition occured, setting the flag and
 - then another (still attached) interrupt on a different pin in the
   same port occured,
then the detached interrupt callback would also be called.

By masking off the Interrupts Status Register using the Interrupt Mask
Register, only actually enabled interrupt handlers are called.

The incorrect behaviour has been verified using the following test sketch:

	/*
	  To test:
	  - Upload this sketch to the Arduino Due
	  - Open the serial monitor at 115200
	  - Connect a jumper wire to ground and touch the other end to pins 25 and 26, alternating between them
	*/

	void int25() {
		Serial.println("INT25");
	}

	void int26() {
		Serial.println("INT26");
	}

	void setup() {
		Serial.begin(115200);
		pinMode(25, INPUT_PULLUP);
		pinMode(26, INPUT_PULLUP);
		attachInterrupt(25, int25, CHANGE);
		attachInterrupt(26, int26, CHANGE);
		detachInterrupt(26);
	}
	void loop() { }
These allow temporarily enabling and disabling a single interrupt. This
is currently pretty much the same as detaching and re-attaching the
interrupt, but this will change when attachInterrupt is changed to clear
any pending interrupts next.

This commit introduces duplicate code, between attach/detach and
enable/disable, which will be removed next (doing this in one commit
makes the diff harder to review).
…bleInterrupt

This removes duplicate code and shrinks the resulting binary slightly.
Prevously, when attachInterrupt was called but the (previously
configured or default) interrupt condition has already occured while the
interrupt was (still) detached, the interrupt triggers immediately once
after attaching, even though the condition didn't occur then.

This fixes this problem by clearing any pending interrupts after
configuring the interrupt level, but before enabling it.

Note that some libraries actually depend on this broken behaviour. Paul
Stoffregen wrote:

> As a matter of principle, I agree, this is a bug.  In practice, this
> behavior really is needed by libraries that use attachInterrupt(),
> but need to temporarily prevent the interrupt from occurring while
> they do lengthy operations which shouldn't globally disable
> interrupts.  When they reenable the interrupt, the desired behavior
> of course it to immediately respond to any pending interrupt that
> was detected during that disabled time.

However, now that the enableInterupt and disabledInterrupt functions are
available, these libraries can use those instead of relying on this bug
in attachInterrupt.

On the SAM core, the only way to clear pending
interrupt flags is to read the Interrupt Status Register.
However, this always clears _all_ flags at once. To not lose
any interrupts, after reading the status register to clear it, interrupt
handlers for any pending interrupts are called.

This does mean that if attachInterrupt is called with interrupts
globally disabled, interrupt handlers are called nonetheless. This seems
to be the lesser of three evils:
 - Not clearing the status register can cause a bogus
   interrupt shortly after calling attachInterrupt (this
   happened in earlier Arduino versions).
 - Reading the status register but not running interrupts can
   cause interrupts to be missed when attachInterrupts is
   called with interrupts disabled (and depending on timing
   probably also with them enabled).
 - Running the handlers (as now) causes interrupt handlers to
   run while calling attachInterrupt with interrupts
   disabled.

This fixes #510.
@matthijskooijman
Copy link
Collaborator Author

I just updated this PR to also support SAM and split a commit in two. For clearing an interrupt on SAM, there is a caveat I also pointed out on the mailling list, see the last commit for details.

I haven't runtime-tested the code on Due due to lack of a Due (I'll probably get one soon, though). Any testing on Due is welcomed.

I haven't runtime-tested the code on AVR after the PR update. The code for AVR shouldn't have changed, but it should probably be tested again nonetheless. I started on this, but then found the bootloader on my Uno was broken en somehow my ICSP programmer was also broken and gave up for today...

@matthijskooijman matthijskooijman removed the Waiting for feedback More information must be provided before we can proceed label Oct 27, 2014
@matthijskooijman
Copy link
Collaborator Author

I just tested this on my Uno, works as expected: Interrupts occuring while detached are ignored, interrupts while disabled are remembered and fired when enabling. For reference, here is the test sketch I used (25 and 26 are legacy from my Due test sketch):



void int25() {
  Serial.println("INT25");
}

void int26() {
  Serial.println("INT26");
}

void setup() {
  Serial.begin(115200);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  attachInterrupt(0, int25, CHANGE);
  attachInterrupt(1, int26, CHANGE);
  detachInterrupt(0);
  Serial.println("INIT");
  while(Serial.read() == -1);
  attachInterrupt(0, int25, CHANGE);
    Serial.println("ATTACH");
  while(Serial.read() == -1);
  disableInterrupt(0);
      Serial.println("DISABLE");
  while(Serial.read() == -1);
  enableInterrupt(0);
        Serial.println("ENABLE");
} 

void loop() {
}

@NicoHood
Copy link
Contributor

NicoHood commented Sep 2, 2015

Good to know that this exists. Can you build it please?

@matthijskooijman
Copy link
Collaborator Author

@ArduinoBot build this please

@@ -154,26 +234,33 @@ void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) {

void detachInterrupt(uint8_t interruptNum) {
if(interruptNum < EXTERNAL_NUM_INTERRUPTS) {
disableInterrupt(interruptNum);
intFunc[interruptNum] = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be nothing now
https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/WInterrupts.c#L273

And the PR needs to be built against the master, otherwise its useless. May you update the PR or better create a new against the master?

@CLAassistant
Copy link

CLAassistant commented Apr 9, 2021

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: Core Related to the code for the standard Arduino API feature request A request to make an enhancement (not a bug fix) Type: Bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants