|
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,24 +96,54 @@ int attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode)
|
95 | 96 | return 0;
|
96 | 97 | }
|
97 | 98 |
|
| 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). |
98 | 113 | 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 |
101 | 122 | callbacksInt[ch] = callback;
|
102 | 123 | 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 |
| - |
112 | 124 | return (1 << ch);
|
113 | 125 | }
|
114 | 126 | }
|
115 | 127 |
|
| 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 |
116 | 147 | return 0;
|
117 | 148 | }
|
118 | 149 |
|
|
0 commit comments