21
21
22
22
#include "Arduino.h"
23
23
#include "wiring_private.h"
24
+ #include "nrf_gpiote.h"
24
25
25
26
#include <string.h>
26
27
@@ -95,25 +96,59 @@ int attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode)
95
96
return 0 ;
96
97
}
97
98
98
- for (int ch = 0 ; ch < NUMBER_OF_GPIO_TE ; ch ++ ) {
99
- if (channelMap [ch ] == -1 || (uint32_t )channelMap [ch ] == pin ) {
100
- channelMap [ch ] = pin ;
101
- callbacksInt [ch ] = callback ;
102
- callbackDeferred [ch ] = deferred ;
103
-
104
- NRF_GPIOTE -> CONFIG [ch ] &= ~(GPIOTE_CONFIG_PORT_PIN_Msk | GPIOTE_CONFIG_POLARITY_Msk );
105
- NRF_GPIOTE -> CONFIG [ch ] |= ((pin << GPIOTE_CONFIG_PSEL_Pos ) & GPIOTE_CONFIG_PORT_PIN_Msk ) |
106
- ((polarity << GPIOTE_CONFIG_POLARITY_Pos ) & GPIOTE_CONFIG_POLARITY_Msk );
99
+ // All information for the configuration is known, except the prior values
100
+ // of the config register. Pre-compute the mask and new bits for later use.
101
+ // CONFIG[n] = (CONFIG[n] & oldRegMask) | newRegBits;
102
+ //
103
+ // Three fields are configured here: PORT/PIN, POLARITY, MODE
104
+ const uint32_t oldRegMask = ~(GPIOTE_CONFIG_PORT_PIN_Msk | GPIOTE_CONFIG_POLARITY_Msk | GPIOTE_CONFIG_MODE_Msk );
105
+ const uint32_t newRegBits =
106
+ ((pin << GPIOTE_CONFIG_PSEL_Pos ) & GPIOTE_CONFIG_PORT_PIN_Msk ) |
107
+ ((polarity << GPIOTE_CONFIG_POLARITY_Pos ) & GPIOTE_CONFIG_POLARITY_Msk ) |
108
+ ((GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos ) & GPIOTE_CONFIG_MODE_Msk ) ;
109
+
110
+ int ch = -1 ;
111
+ int newChannel = 0 ;
112
+
113
+ // Find channel where pin is already assigned, if any
114
+ for (int i = 0 ; i < NUMBER_OF_GPIO_TE ; i ++ ) {
115
+ if ((uint32_t )channelMap [i ] != pin ) continue ;
116
+ ch = i ;
117
+ break ;
118
+ }
119
+ // else, find one not already mapped and also not in use by others
120
+ if (ch == -1 ) {
121
+ for (int i = 0 ; i < NUMBER_OF_GPIO_TE ; i ++ ) {
122
+ if (channelMap [i ] != -1 ) continue ;
123
+ if (nrf_gpiote_te_is_enabled (NRF_GPIOTE , i )) continue ;
124
+
125
+ ch = i ;
126
+ newChannel = 1 ;
127
+ break ;
128
+ }
129
+ }
130
+ // if no channel found, exit
131
+ if (ch == -1 ) {
132
+ return 0 ; // no channel available
133
+ }
107
134
108
- NRF_GPIOTE -> CONFIG [ch ] |= GPIOTE_CONFIG_MODE_Event ;
135
+ channelMap [ch ] = pin ; // harmless for existing channel
136
+ callbacksInt [ch ] = callback ; // caller might be updating this for existing channel
137
+ callbackDeferred [ch ] = deferred ; // caller might be updating this for existing channel
109
138
110
- NRF_GPIOTE -> INTENSET = (1 << ch );
139
+ uint32_t tmp = NRF_GPIOTE -> CONFIG [ch ];
140
+ tmp &= oldRegMask ;
141
+ tmp |= newRegBits ; // for existing channel, effectively updates only the polarity
142
+ NRF_GPIOTE -> CONFIG [ch ] = tmp ;
111
143
112
- return (1 << ch );
113
- }
144
+ // For a new channel, additionally ensure no old events existed, and enable the interrupt
145
+ if (newChannel ) {
146
+ NRF_GPIOTE -> EVENTS_IN [ch ] = 0 ;
147
+ NRF_GPIOTE -> INTENSET = (1 << ch );
114
148
}
115
149
116
- return 0 ;
150
+ // Finally, indicate to caller the allocated / updated channel
151
+ return (1 << ch );
117
152
}
118
153
119
154
/*
@@ -129,14 +164,14 @@ void detachInterrupt(uint32_t pin)
129
164
130
165
for (int ch = 0 ; ch < NUMBER_OF_GPIO_TE ; ch ++ ) {
131
166
if ((uint32_t )channelMap [ch ] == pin ) {
167
+ NRF_GPIOTE -> INTENCLR = (1 << ch );
168
+ NRF_GPIOTE -> CONFIG [ch ] = 0 ;
169
+ NRF_GPIOTE -> EVENTS_IN [ch ] = 0 ; // clear any final events
170
+
171
+ // now cleanup the rest of the use of the channel
132
172
channelMap [ch ] = -1 ;
133
173
callbacksInt [ch ] = NULL ;
134
174
callbackDeferred [ch ] = false;
135
-
136
- NRF_GPIOTE -> CONFIG [ch ] &= ~GPIOTE_CONFIG_MODE_Event ;
137
-
138
- NRF_GPIOTE -> INTENCLR = (1 << ch );
139
-
140
175
break ;
141
176
}
142
177
}
@@ -148,30 +183,39 @@ void GPIOTE_IRQHandler()
148
183
SEGGER_SYSVIEW_RecordEnterISR ();
149
184
#endif
150
185
151
- uint32_t event = offsetof(NRF_GPIOTE_Type , EVENTS_IN [0 ]);
152
-
186
+ // Read this once (not 8x), as it's a volatile read
187
+ // across the AHB, which adds up to 3 cycles.
188
+ uint32_t const enabledInterruptMask = NRF_GPIOTE -> INTENSET ;
153
189
for (int ch = 0 ; ch < NUMBER_OF_GPIO_TE ; ch ++ ) {
154
- if ((* (uint32_t * )((uint32_t )NRF_GPIOTE + event ) == 0x1UL ) && (NRF_GPIOTE -> INTENSET & (1 << ch ))) {
155
- if (channelMap [ch ] != -1 && callbacksInt [ch ]) {
156
- if ( callbackDeferred [ch ] ) {
157
- // Adafruit defer callback to non-isr if configured so
158
- ada_callback (NULL , 0 , callbacksInt [ch ]);
159
- }else {
160
- callbacksInt [ch ]();
161
- }
190
+ // only process where the interrupt is enabled and the event register is set
191
+ // check interrupt enabled mask first, as already read that IOM value, to
192
+ // reduce delays from AHB (16MHz) reads.
193
+ if ( 0 == (enabledInterruptMask & (1 << ch ))) continue ;
194
+ if ( 0 == NRF_GPIOTE -> EVENTS_IN [ch ]) continue ;
195
+
196
+ // If the event was set and interrupts are enabled,
197
+ // call the callback function only if it exists,
198
+ // but ALWAYS clear the event to prevent an interrupt storm.
199
+ if (channelMap [ch ] != -1 && callbacksInt [ch ]) {
200
+ if ( callbackDeferred [ch ] ) {
201
+ // Adafruit defer callback to non-isr if configured so
202
+ ada_callback (NULL , 0 , callbacksInt [ch ]);
203
+ } else {
204
+ callbacksInt [ch ]();
162
205
}
163
-
164
- * (uint32_t * )((uint32_t )NRF_GPIOTE + event ) = 0 ;
165
- #if __CORTEX_M == 0x04
166
- volatile uint32_t dummy = * ((volatile uint32_t * )((uint32_t )NRF_GPIOTE + event ));
167
- (void )dummy ;
168
- #endif
169
206
}
170
207
171
- event = (uint32_t )((uint32_t )event + 4 );
208
+ // clear the event
209
+ NRF_GPIOTE -> EVENTS_IN [ch ] = 0 ;
172
210
}
211
+ #if __CORTEX_M == 0x04
212
+ // See note at nRF52840_PS_v1.1.pdf section 6.1.8 ("interrupt clearing")
213
+ // See also https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html for why
214
+ // using memory barrier instead of read of an unrelated volatile
215
+ __DSB (); __NOP ();__NOP ();__NOP ();__NOP ();
216
+ #endif
173
217
174
218
#if CFG_SYSVIEW
175
- SEGGER_SYSVIEW_RecordExitISR ();
219
+ SEGGER_SYSVIEW_RecordExitISR ();
176
220
#endif
177
221
}
0 commit comments