Skip to content

Commit 3ac53c5

Browse files
committed
Fix GPIOTE channel usage
First, when looking for a channel, first loop through all mapped channels to see if the GPIO was already mapped. This prevents a single GPIO being configured for more than one channel at a time. Second, do not use a GPIOTE channel if the channel is already in use (e.g., by a library, sketch, or other code outside the Board Support Package).
1 parent d28219b commit 3ac53c5

File tree

1 file changed

+42
-11
lines changed

1 file changed

+42
-11
lines changed

cores/nRF5/WInterrupts.c

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include "Arduino.h"
2323
#include "wiring_private.h"
24+
#include "nrf_gpiote.h"
2425

2526
#include <string.h>
2627

@@ -95,24 +96,54 @@ int attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode)
9596
return 0;
9697
}
9798

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+
// To avoid unexpected consequences, first loop through the channelMap
111+
// to preferably re-use any channel that was already in use (even if
112+
// earlier channel is no longer in use).
98113
for (int ch = 0; ch < NUMBER_OF_GPIO_TE; ch++) {
99-
if (channelMap[ch] == -1 || (uint32_t)channelMap[ch] == pin) {
100-
channelMap[ch] = pin;
114+
if ((uint32_t)channelMap[ch] == pin || channelMap[ch] == -1) {
115+
// The pin is already allocated in the channelMap
116+
// update the polarity (when events fire) and callbacks
117+
uint32_t tmp = NRF_GPIOTE->CONFIG[ch];
118+
tmp &= oldRegMask;
119+
tmp |= newRegBits;
120+
NRF_GPIOTE->CONFIG[ch] = tmp;
121+
NRF_GPIOTE->INTENSET = (1 << ch); // old code did this ... no harm in ensuring this is set
101122
callbacksInt[ch] = callback;
102123
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);
107-
108-
NRF_GPIOTE->CONFIG[ch] |= GPIOTE_CONFIG_MODE_Event;
109-
110-
NRF_GPIOTE->INTENSET = (1 << ch);
111-
112124
return (1 << ch);
113125
}
114126
}
115127

128+
// When the pin isn't already configured for interrupts, then attempt to
129+
// find an unused GPIOTE channel. This code is identical to the above,
130+
// except for also validating that the MODE bits show the channel is disabled.
131+
for (int ch=0; ch < NUMBER_OF_GPIO_TE; ch++) {
132+
// skip if already used in AttachInterrupt for another pin
133+
if (channelMap[ch] != -1) continue;
134+
// skip if channel is not disabled (e.g., in use by some other component or library)
135+
if (nrf_gpiote_te_is_enabled(ch)) continue;
136+
uint32_t tmp = NRF_GPIOTE->CONFIG[ch];
137+
tmp &= oldRegMask;
138+
tmp |= newRegBits;
139+
// TODO: make check/set for new channel an atomic operation
140+
NRF_GPIOTE->CONFIG[ch] = tmp;
141+
NRF_GPIOTE->INTENSET = (1 << ch);
142+
callbacksInt[ch] = callback;
143+
callbackDeferred[ch] = deferred;
144+
return (1 << ch);
145+
}
146+
// Else, pin was neither already setup, nor could a GPIOTE be allocated
116147
return 0;
117148
}
118149

0 commit comments

Comments
 (0)