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 */
2931static 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 */
152195void 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