Skip to content

Commit c82e9aa

Browse files
Let attachInterrupt clear pending interrupts
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.
1 parent 09b4bfa commit c82e9aa

File tree

2 files changed

+63
-16
lines changed

2 files changed

+63
-16
lines changed

hardware/arduino/avr/cores/arduino/WInterrupts.c

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,70 +51,89 @@ void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) {
5151
// even present on the 32U4 this is the only way to distinguish between them.
5252
case 0:
5353
EICRA = (EICRA & ~((1<<ISC00) | (1<<ISC01))) | (mode << ISC00);
54+
EIFR |= (1 << INTF0);
5455
break;
5556
case 1:
5657
EICRA = (EICRA & ~((1<<ISC10) | (1<<ISC11))) | (mode << ISC10);
58+
EIFR |= (1 << INTF1);
5759
break;
5860
case 2:
5961
EICRA = (EICRA & ~((1<<ISC20) | (1<<ISC21))) | (mode << ISC20);
62+
EIFR |= (1 << INTF2);
6063
break;
6164
case 3:
6265
EICRA = (EICRA & ~((1<<ISC30) | (1<<ISC31))) | (mode << ISC30);
66+
EIFR |= (1 << INTF3);
6367
break;
6468
case 4:
6569
EICRB = (EICRB & ~((1<<ISC60) | (1<<ISC61))) | (mode << ISC60);
70+
EIFR |= (1 << INTF6);
6671
break;
6772
#elif defined(EICRA) && defined(EICRB)
6873
case 2:
6974
EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
75+
EIFR |= (1 << INTF0);
7076
break;
7177
case 3:
7278
EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
79+
EIFR |= (1 << INTF1);
7380
break;
7481
case 4:
7582
EICRA = (EICRA & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);
83+
EIFR |= (1 << INTF2);
7684
break;
7785
case 5:
7886
EICRA = (EICRA & ~((1 << ISC30) | (1 << ISC31))) | (mode << ISC30);
87+
EIFR |= (1 << INTF4);
7988
break;
8089
case 0:
8190
EICRB = (EICRB & ~((1 << ISC40) | (1 << ISC41))) | (mode << ISC40);
91+
EIFR |= (1 << INTF4);
8292
break;
8393
case 1:
8494
EICRB = (EICRB & ~((1 << ISC50) | (1 << ISC51))) | (mode << ISC50);
95+
EIFR |= (1 << INTF5);
8596
break;
8697
case 6:
8798
EICRB = (EICRB & ~((1 << ISC60) | (1 << ISC61))) | (mode << ISC60);
99+
EIFR |= (1 << INTF6);
88100
break;
89101
case 7:
90102
EICRB = (EICRB & ~((1 << ISC70) | (1 << ISC71))) | (mode << ISC70);
103+
EIFR |= (1 << INTF7);
91104
break;
92105
#else
93106
case 0:
94-
#if defined(EICRA) && defined(ISC00)
107+
#if defined(EICRA) && defined(ISC00) && defined(EIFR)
95108
EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
96-
#elif defined(MCUCR) && defined(ISC00)
109+
EIFR |= (1 << INTF0);
110+
#elif defined(MCUCR) && defined(ISC00) && defined(GIFR)
97111
MCUCR = (MCUCR & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
112+
GIFR |= (1 << INTF0);
98113
#else
99114
#error attachInterrupt not finished for this CPU (case 0)
100115
#endif
101116
break;
102117

103118
case 1:
104-
#if defined(EICRA) && defined(ISC10) && defined(ISC11)
119+
#if defined(EICRA) && defined(ISC10) && defined(ISC11) && defined(EIFR)
105120
EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
106-
#elif defined(MCUCR) && defined(ISC10) && defined(ISC11)
121+
EIFR |= (1 << INTF1);
122+
#elif defined(MCUCR) && defined(ISC10) && defined(ISC11) && defined(GIFR)
107123
MCUCR = (MCUCR & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
124+
GIFR |= (1 << INTF1);
108125
#else
109126
#warning attachInterrupt may need some more work for this cpu (case 1)
110127
#endif
111128
break;
112129

113130
case 2:
114-
#if defined(EICRA) && defined(ISC20) && defined(ISC21)
131+
#if defined(EICRA) && defined(ISC20) && defined(ISC21) && defined(EIFR)
115132
EICRA = (EICRA & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);
116-
#elif defined(MCUCR) && defined(ISC20) && defined(ISC21)
133+
EIFR |= (1 << INTF2);
134+
#elif defined(MCUCR) && defined(ISC20) && defined(ISC21) && defined(GIFR)
117135
MCUCR = (MCUCR & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);
136+
GIFR |= (1 << INTF2);
118137
#endif
119138
break;
120139
#endif

hardware/arduino/sam/cores/arduino/WInterrupts.c

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,6 @@ void attachInterrupt(uint32_t pin, void (*callback)(void), uint32_t mode)
7878
for (t = mask; t>1; t>>=1, pos++)
7979
;
8080

81-
// Set callback function
82-
if (pio == PIOA)
83-
callbacksPioA[pos] = callback;
84-
if (pio == PIOB)
85-
callbacksPioB[pos] = callback;
86-
if (pio == PIOC)
87-
callbacksPioC[pos] = callback;
88-
if (pio == PIOD)
89-
callbacksPioD[pos] = callback;
90-
9181
// Configure the interrupt mode
9282
if (mode == CHANGE) {
9383
// Disable additional interrupt mode (detects both rising and falling edges)
@@ -115,6 +105,44 @@ void attachInterrupt(uint32_t pin, void (*callback)(void), uint32_t mode)
115105
}
116106
}
117107

108+
// Set callback function and call handler to clear existing
109+
// flags. On the SAM core, the only way to clear pending
110+
// interrupt flags is to read the Interrupt Status Register.
111+
// However, this always clears _all_ flags at once. To not lose
112+
// any interrupts, we call the handler here which reads the
113+
// status register and calls handlers for any pending
114+
// interrupts (the interrupt we are attaching here isn't enabled
115+
// yet and should be skipped). This does mean that if
116+
// attachInterrupt is called with interrupts globally disabled,
117+
// interrupt handlers are called nonetheless. This seems to be
118+
// the lesser of three evils:
119+
// - Not clearing the status register can cause a bogus
120+
// interrupt shortly after calling attachInterrupt (this
121+
// happened in earlier Arduino versions).
122+
// - Reading the status register but not running interrupts can
123+
// cause interrupts to be missed when attachInterrupts is
124+
// called with interrupts disabled (and depending on timing
125+
// probably also with them enabled).
126+
// - Running the handlers (as now) causes interrupt handlers to
127+
// run while calling attachInterrupt with interrupts
128+
// disabled.
129+
if (pio == PIOA) {
130+
callbacksPioA[pos] = callback;
131+
PIOA_Handler();
132+
}
133+
if (pio == PIOB) {
134+
callbacksPioB[pos] = callback;
135+
PIOD_Handler();
136+
}
137+
if (pio == PIOC) {
138+
callbacksPioC[pos] = callback;
139+
PIOD_Handler();
140+
}
141+
if (pio == PIOD) {
142+
callbacksPioD[pos] = callback;
143+
PIOD_Handler();
144+
}
145+
118146
enableInterrupt(pin);
119147
}
120148

0 commit comments

Comments
 (0)