@@ -179,30 +179,58 @@ void GPIOTE_IRQHandler()
179
179
SEGGER_SYSVIEW_RecordEnterISR ();
180
180
#endif
181
181
182
- uint32_t event = offsetof(NRF_GPIOTE_Type , EVENTS_IN [0 ]);
183
-
182
+ // Read this once (not 8x), as it's a volatile read
183
+ // across the AHB, which adds up to 3 cycles.
184
+ uint32_t const enabledInterruptMask = NRF_GPIOTE -> INTENSET ;
184
185
for (int ch = 0 ; ch < NUMBER_OF_GPIO_TE ; ch ++ ) {
185
- if ((* (uint32_t * )((uint32_t )NRF_GPIOTE + event ) == 0x1UL ) && (NRF_GPIOTE -> INTENSET & (1 << ch ))) {
186
- if (channelMap [ch ] != -1 && callbacksInt [ch ]) {
187
- if ( callbackDeferred [ch ] ) {
188
- // Adafruit defer callback to non-isr if configured so
189
- ada_callback (NULL , 0 , callbacksInt [ch ]);
190
- }else {
191
- callbacksInt [ch ]();
192
- }
186
+ // only process where the interrupt is enabled and the event register is set
187
+ // check interrupt enabled mask first, as already read that IOM value, to
188
+ // reduce delays from AHB (16MHz) reads.
189
+ if ( 0 == (enabledInterruptMask & (1 << ch ))) continue ;
190
+ if ( 0 == NRF_GPIOTE -> EVENTS_IN [ch ]) continue ;
191
+
192
+ // If the event was set and interrupts are enabled,
193
+ // call the callback function only if it exists,
194
+ // but ALWAYS clear the event to prevent an interrupt storm.
195
+ if (channelMap [ch ] != -1 && callbacksInt [ch ]) {
196
+ if ( callbackDeferred [ch ] ) {
197
+ // Adafruit defer callback to non-isr if configured so
198
+ ada_callback (NULL , 0 , callbacksInt [ch ]);
199
+ } else {
200
+ callbacksInt [ch ]();
193
201
}
194
-
195
- * (uint32_t * )((uint32_t )NRF_GPIOTE + event ) = 0 ;
196
- #if __CORTEX_M == 0x04
197
- volatile uint32_t dummy = * ((volatile uint32_t * )((uint32_t )NRF_GPIOTE + event ));
198
- (void )dummy ;
199
- #endif
200
202
}
201
203
202
- event = (uint32_t )((uint32_t )event + 4 );
204
+ // clear the event
205
+ NRF_GPIOTE -> EVENTS_IN [ch ] = 0 ;
203
206
}
207
+ #if __CORTEX_M == 0x04
208
+ // To quote from nRF52840 product specification:
209
+ // --------------------------------------------------------------------------
210
+ // Note: To avoid an interrupt reoccurring before a new event has come,
211
+ // the program should perform a read from one of the peripheral
212
+ // registers. For example, the event register that has been cleared,
213
+ // or the INTENCLR register that has been used to disable the
214
+ // interrupt. This will cause a one to three cycle delay and ensure
215
+ // the interrupt is cleared before exiting the interrupt handler.
216
+ // Care should be taken to ensure the compiler does not remove the read
217
+ // operation as an optimization. If the program can guarantee a four-cycle
218
+ // delay after event being cleared or interrupt disabled in any other way,
219
+ // then a read of a register is not required.
220
+ // --------------------------------------------------------------------------
221
+ //
222
+ // Unfortunately, simply marking a variable as volatile is not enough to
223
+ // prevent GCC from reordering or combining memory accesses, without also
224
+ // having an explicit sequence point.
225
+ // See https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html
226
+ //
227
+ // Here, ensure a sequence point by adding a memory barrier
228
+ // followed with four nops before exiting the ISR
229
+ asm volatile ("" : : : "memory" );
230
+ __asm__ __volatile__ ("nop\n\tnop\n\tnop\n\tnop\n" );
231
+ #endif
204
232
205
233
#if CFG_SYSVIEW
206
- SEGGER_SYSVIEW_RecordExitISR ();
234
+ SEGGER_SYSVIEW_RecordExitISR ();
207
235
#endif
208
236
}
0 commit comments