Skip to content

Commit 1fb3628

Browse files
committed
Improved ISR response time. Thanks @joverbee
1 parent 67f8b59 commit 1fb3628

File tree

1 file changed

+83
-40
lines changed

1 file changed

+83
-40
lines changed

cores/arduino/WInterrupts.c

Lines changed: 83 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@
2323

2424
#include <string.h>
2525

26-
static voidFuncPtr callbacksInt[EXTERNAL_NUM_INTERRUPTS];
26+
static voidFuncPtr ISRcallback[EXTERNAL_NUM_INTERRUPTS];
27+
static uint32_t ISRlist[EXTERNAL_NUM_INTERRUPTS];
28+
static uint32_t nints; // Stores total number of attached interrupts
2729

2830
/* Configure I/O interrupt sources */
2931
static void __initialize()
3032
{
31-
memset(callbacksInt, 0, sizeof(callbacksInt));
33+
memset(ISRlist, 0, sizeof(ISRlist));
34+
memset(ISRcallback, 0, sizeof(ISRcallback));
35+
nints = 0;
3236

3337
NVIC_DisableIRQ(EIC_IRQn);
3438
NVIC_ClearPendingIRQ(EIC_IRQn);
@@ -86,47 +90,69 @@ void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode)
8690
return;
8791
#endif
8892

89-
// Assign pin to EIC
90-
if (pinPeripheral(pin, PIO_EXTINT) != RET_STATUS_OK)
91-
return;
93+
uint32_t inMask = 1 << in;
9294

9395
// Enable wakeup capability on pin in case being used during sleep (WAKEUP always enabled on SAML and SAMC)
9496
#if (SAMD21 || SAMD11)
95-
EIC->WAKEUP.reg |= (1 << in);
97+
EIC->WAKEUP.reg |= (inMask);
9698
#endif
9799

98-
// Assign callback to interrupt
99-
callbacksInt[in] = callback;
100+
// Assign pin to EIC
101+
if (pinPeripheral(pin, PIO_EXTINT) != RET_STATUS_OK)
102+
return;
100103

101-
// Look for right CONFIG register to be addressed
102-
if (in > EXTERNAL_INT_7) {
103-
config = 1;
104-
} else {
105-
config = 0;
106-
}
104+
// Only store when there is really an ISR to call.
105+
// This allow for calling attachInterrupt(pin, NULL, mode), we set up all needed register
106+
// but won't service the interrupt, this way we also don't need to check it inside the ISR.
107+
if (callback)
108+
{
109+
// Store interrupts to service in order of when they were attached
110+
// to allow for first come first serve handler
111+
uint32_t current = 0;
112+
113+
// Check if we already have this interrupt
114+
for (current=0; current<nints; current++) {
115+
if (ISRlist[current] == inMask) {
116+
break;
117+
}
118+
}
119+
if (current == nints) {
120+
// Need to make a new entry
121+
nints++;
122+
}
123+
ISRlist[current] = inMask; // List of interrupt in order of when they were attached
124+
ISRcallback[current] = callback; // List of callback adresses
125+
126+
// Look for right CONFIG register to be addressed
127+
if (in > EXTERNAL_INT_7) {
128+
config = 1;
129+
} else {
130+
config = 0;
131+
}
107132

108-
// Configure the interrupt mode
109-
pos = (in - (8 * config)) << 2; // compute position (ie: 0, 4, 8, 12, ...)
133+
// Configure the interrupt mode
134+
pos = (in - (8 * config)) << 2; // compute position (ie: 0, 4, 8, 12, ...)
110135

111-
#if (SAML21 || SAMC21)
112-
EIC->CTRLA.reg = 0; // disable EIC before changing CONFIG
113-
while (EIC->SYNCBUSY.reg & EIC_SYNCBUSY_MASK) { }
114-
#endif
136+
#if (SAML21 || SAMC21)
137+
EIC->CTRLA.reg = 0; // disable EIC before changing CONFIG
138+
while (EIC->SYNCBUSY.reg & EIC_SYNCBUSY_MASK) { }
139+
#endif
115140

116-
uint32_t regConfig = (~(EIC_CONFIG_SENSE0_Msk << pos) & EIC->CONFIG[config].reg); // copy register to variable, clearing mode bits
117-
// insert new mode and write to register (the hardware numbering for the 5 interrupt modes is in reverse order to the arduino numbering, so using '5-mode').
118-
EIC->CONFIG[config].reg = (regConfig | ((5-mode) << pos));
141+
uint32_t regConfig = (~(EIC_CONFIG_SENSE0_Msk << pos) & EIC->CONFIG[config].reg); // copy register to variable, clearing mode bits
142+
// insert new mode and write to register (the hardware numbering for the 5 interrupt modes is in reverse order to the arduino numbering, so using '5-mode').
143+
EIC->CONFIG[config].reg = (regConfig | ((5-mode) << pos));
119144

120-
#if (SAML21 || SAMC21)
121-
EIC->CTRLA.reg = EIC_CTRLA_ENABLE; // enable EIC
122-
while (EIC->SYNCBUSY.reg & EIC_SYNCBUSY_MASK) { }
123-
#endif
145+
#if (SAML21 || SAMC21)
146+
EIC->CTRLA.reg = EIC_CTRLA_ENABLE; // enable EIC
147+
while (EIC->SYNCBUSY.reg & EIC_SYNCBUSY_MASK) { }
148+
#endif
149+
}
124150

125151
// Clear the interrupt flag
126-
EIC->INTFLAG.reg = (1 << in);
152+
EIC->INTFLAG.reg = (inMask);
127153

128154
// Enable the interrupt
129-
EIC->INTENSET.reg = (1 << in);
155+
EIC->INTENSET.reg = (inMask);
130156
}
131157

132158
/*
@@ -138,31 +164,48 @@ void detachInterrupt(uint32_t pin)
138164
if (in == NOT_AN_INTERRUPT || in == EXTERNAL_INT_NMI)
139165
return;
140166

141-
EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT(1 << in);
167+
uint32_t inMask = 1 << in;
168+
EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT(inMask);
142169

143170
// Disable wakeup capability on pin during sleep (WAKEUP always enabled on SAML and SAMC)
144171
#if (SAMD21 || SAMD11)
145-
EIC->WAKEUP.reg &= ~(1 << in);
172+
EIC->WAKEUP.reg &= ~(inMask);
146173
#endif
174+
175+
// Remove callback from the ISR list
176+
uint32_t current;
177+
for (current=0; current<nints; current++) {
178+
if (ISRlist[current] == inMask) {
179+
break;
180+
}
181+
}
182+
if (current == nints) return; // We didn't have it
183+
184+
// Shift the reminder down
185+
for (; current<nints-1; current++) {
186+
ISRlist[current] = ISRlist[current+1];
187+
ISRcallback[current] = ISRcallback[current+1];
188+
}
189+
nints--;
147190
}
148191

149192
/*
150193
* External Interrupt Controller NVIC Interrupt Handler
151194
*/
152195
void EIC_Handler(void)
153196
{
154-
// Test the normal interrupts
155-
for (uint32_t i=EXTERNAL_INT_0; i<=EXTERNAL_INT_15; i++)
197+
// Calling the routine directly from -here- takes about 1us
198+
// Depending on where you are in the list it will take longer
199+
200+
// Loop over all enabled interrupts in the list
201+
for (uint32_t i=0; i<nints; i++)
156202
{
157-
if ((EIC->INTFLAG.reg & (1 << i)) != 0)
203+
if ((EIC->INTFLAG.reg & ISRlist[i]) != 0)
158204
{
159-
// Call the callback function if assigned
160-
if (callbacksInt[i]) {
161-
callbacksInt[i]();
162-
}
163-
205+
// Call the callback function
206+
ISRcallback[i]();
164207
// Clear the interrupt
165-
EIC->INTFLAG.reg = 1 << i;
208+
EIC->INTFLAG.reg = ISRlist[i];
166209
}
167210
}
168211
}

0 commit comments

Comments
 (0)