forked from adafruit/Adafruit_nRF52_Arduino
-
Notifications
You must be signed in to change notification settings - Fork 0
/
WInterrupts.c
221 lines (184 loc) · 6.52 KB
/
WInterrupts.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/*
Copyright (c) 2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <nrf.h>
#include "Arduino.h"
#include "wiring_private.h"
#include "nrf_gpiote.h"
#include <string.h>
#if defined(NRF52) || defined(NRF52_SERIES)
#define NUMBER_OF_GPIO_TE 8
#else
#define NUMBER_OF_GPIO_TE 4
#endif
#ifdef GPIOTE_CONFIG_PORT_Msk
#define GPIOTE_CONFIG_PORT_PIN_Msk (GPIOTE_CONFIG_PORT_Msk | GPIOTE_CONFIG_PSEL_Msk)
#else
#define GPIOTE_CONFIG_PORT_PIN_Msk GPIOTE_CONFIG_PSEL_Msk
#endif
static voidFuncPtr callbacksInt[NUMBER_OF_GPIO_TE];
static bool callbackDeferred[NUMBER_OF_GPIO_TE];
static int8_t channelMap[NUMBER_OF_GPIO_TE];
static int enabled = 0;
/* Configure I/O interrupt sources */
static void __initialize()
{
memset(callbacksInt, 0, sizeof(callbacksInt));
memset(channelMap, -1, sizeof(channelMap));
memset(callbackDeferred, 0, sizeof(callbackDeferred));
NVIC_DisableIRQ(GPIOTE_IRQn);
NVIC_ClearPendingIRQ(GPIOTE_IRQn);
NVIC_SetPriority(GPIOTE_IRQn, 3);
NVIC_EnableIRQ(GPIOTE_IRQn);
}
/*
* \brief Specifies a named Interrupt Service Routine (ISR) to call when an interrupt occurs.
* Replaces any previous function that was attached to the interrupt.
*
* \return Interrupt Mask
*/
int attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode)
{
if (!enabled) {
__initialize();
enabled = 1;
}
if (pin >= PINS_COUNT) {
return 0;
}
pin = g_ADigitalPinMap[pin];
bool deferred = (mode & ISR_DEFERRED) ? true : false;
mode &= ~ISR_DEFERRED;
uint32_t polarity;
switch (mode) {
case CHANGE:
polarity = GPIOTE_CONFIG_POLARITY_Toggle;
break;
case FALLING:
polarity = GPIOTE_CONFIG_POLARITY_HiToLo;
break;
case RISING:
polarity = GPIOTE_CONFIG_POLARITY_LoToHi;
break;
default:
return 0;
}
// All information for the configuration is known, except the prior values
// of the config register. Pre-compute the mask and new bits for later use.
// CONFIG[n] = (CONFIG[n] & oldRegMask) | newRegBits;
//
// Three fields are configured here: PORT/PIN, POLARITY, MODE
const uint32_t oldRegMask = ~(GPIOTE_CONFIG_PORT_PIN_Msk | GPIOTE_CONFIG_POLARITY_Msk | GPIOTE_CONFIG_MODE_Msk);
const uint32_t newRegBits =
((pin << GPIOTE_CONFIG_PSEL_Pos ) & GPIOTE_CONFIG_PORT_PIN_Msk) |
((polarity << GPIOTE_CONFIG_POLARITY_Pos) & GPIOTE_CONFIG_POLARITY_Msk) |
((GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos ) & GPIOTE_CONFIG_MODE_Msk ) ;
int ch = -1;
int newChannel = 0;
// Find channel where pin is already assigned, if any
for (int i = 0; i < NUMBER_OF_GPIO_TE; i++) {
if ((uint32_t)channelMap[i] != pin) continue;
ch = i;
break;
}
// else, find one not already mapped and also not in use by others
if (ch == -1) {
for (int i = 0; i < NUMBER_OF_GPIO_TE; i++) {
if (channelMap[i] != -1) continue;
if (nrf_gpiote_te_is_enabled(NRF_GPIOTE, i)) continue;
ch = i;
newChannel = 1;
break;
}
}
// if no channel found, exit
if (ch == -1) {
return 0; // no channel available
}
channelMap[ch] = pin; // harmless for existing channel
callbacksInt[ch] = callback; // caller might be updating this for existing channel
callbackDeferred[ch] = deferred; // caller might be updating this for existing channel
uint32_t tmp = NRF_GPIOTE->CONFIG[ch];
tmp &= oldRegMask;
tmp |= newRegBits; // for existing channel, effectively updates only the polarity
NRF_GPIOTE->CONFIG[ch] = tmp;
// For a new channel, additionally ensure no old events existed, and enable the interrupt
if (newChannel) {
NRF_GPIOTE->EVENTS_IN[ch] = 0;
NRF_GPIOTE->INTENSET = (1 << ch);
}
// Finally, indicate to caller the allocated / updated channel
return (1 << ch);
}
/*
* \brief Turns off the given interrupt.
*/
void detachInterrupt(uint32_t pin)
{
if (pin >= PINS_COUNT) {
return;
}
pin = g_ADigitalPinMap[pin];
for (int ch = 0; ch < NUMBER_OF_GPIO_TE; ch++) {
if ((uint32_t)channelMap[ch] == pin) {
NRF_GPIOTE->INTENCLR = (1 << ch);
NRF_GPIOTE->CONFIG[ch] = 0;
NRF_GPIOTE->EVENTS_IN[ch] = 0; // clear any final events
// now cleanup the rest of the use of the channel
channelMap[ch] = -1;
callbacksInt[ch] = NULL;
callbackDeferred[ch] = false;
break;
}
}
}
void GPIOTE_IRQHandler()
{
#if CFG_SYSVIEW
SEGGER_SYSVIEW_RecordEnterISR();
#endif
// Read this once (not 8x), as it's a volatile read
// across the AHB, which adds up to 3 cycles.
uint32_t const enabledInterruptMask = NRF_GPIOTE->INTENSET;
for (int ch = 0; ch < NUMBER_OF_GPIO_TE; ch++) {
// only process where the interrupt is enabled and the event register is set
// check interrupt enabled mask first, as already read that IOM value, to
// reduce delays from AHB (16MHz) reads.
if ( 0 == (enabledInterruptMask & (1 << ch))) continue;
if ( 0 == NRF_GPIOTE->EVENTS_IN[ch]) continue;
// If the event was set and interrupts are enabled,
// call the callback function only if it exists,
// but ALWAYS clear the event to prevent an interrupt storm.
if (channelMap[ch] != -1 && callbacksInt[ch]) {
if ( callbackDeferred[ch] ) {
// Adafruit defer callback to non-isr if configured so
ada_callback(NULL, 0, callbacksInt[ch]);
} else {
callbacksInt[ch]();
}
}
// clear the event
NRF_GPIOTE->EVENTS_IN[ch] = 0;
}
#if __CORTEX_M == 0x04
// See note at nRF52840_PS_v1.1.pdf section 6.1.8 ("interrupt clearing")
// See also https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html for why
// using memory barrier instead of read of an unrelated volatile
__DSB(); __NOP();__NOP();__NOP();__NOP();
#endif
#if CFG_SYSVIEW
SEGGER_SYSVIEW_RecordExitISR();
#endif
}