Skip to content

Commit d0279f2

Browse files
committed
Add facility that schedules ISRs into loop context instead of running them directly as IRQ handler
1 parent 2c6ca16 commit d0279f2

File tree

7 files changed

+168
-89
lines changed

7 files changed

+168
-89
lines changed

cores/esp8266/Arduino.h

+1
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder);
219219
void attachInterrupt(uint8_t pin, void (*)(void), int mode);
220220
void detachInterrupt(uint8_t pin);
221221
void attachInterruptArg(uint8_t pin, void (*)(void*), void* arg, int mode);
222+
void* detachInterruptArg(uint8_t pin);
222223

223224
void preinit(void);
224225
void setup(void);

cores/esp8266/core_esp8266_wiring_digital.cpp

+57-89
Original file line numberDiff line numberDiff line change
@@ -111,22 +111,9 @@ typedef struct {
111111
uint8_t mode;
112112
voidFuncPtr fn;
113113
void * arg;
114-
bool functional;
115114
} interrupt_handler_t;
116115

117-
//duplicate from functionalInterrupt.h keep in sync
118-
typedef struct InterruptInfo {
119-
uint8_t pin;
120-
uint8_t value;
121-
uint32_t micro;
122-
} InterruptInfo;
123-
124-
typedef struct {
125-
InterruptInfo* interruptInfo;
126-
void* functionInfo;
127-
} ArgStructure;
128-
129-
static interrupt_handler_t interrupt_handlers[16] = { {0, 0, 0, 0}, };
116+
static interrupt_handler_t interrupt_handlers[16] = { {0, 0, 0}, };
130117
static uint32_t interrupt_reg = 0;
131118

132119
void ICACHE_RAM_ATTR interrupt_handler(void*)
@@ -145,77 +132,55 @@ void ICACHE_RAM_ATTR interrupt_handler(void*)
145132
if (handler->fn &&
146133
(handler->mode == CHANGE ||
147134
(handler->mode & 1) == !!(levels & (1 << i)))) {
148-
// to make ISR compatible to Arduino AVR model where interrupts are disabled
149-
// we disable them before we call the client ISR
150-
uint32_t savedPS = xt_rsil(15); // stop other interrupts
151-
if (handler->functional)
152-
{
153-
ArgStructure* localArg = (ArgStructure*)handler->arg;
154-
if (localArg && localArg->interruptInfo)
155-
{
156-
localArg->interruptInfo->pin = i;
157-
localArg->interruptInfo->value = __digitalRead(i);
158-
localArg->interruptInfo->micro = micros();
159-
}
160-
}
161-
if (handler->arg)
162-
{
163-
((voidFuncPtrArg)handler->fn)(handler->arg);
164-
}
165-
else
166-
{
167-
handler->fn();
168-
}
169-
xt_wsr_ps(savedPS);
135+
// to make ISR compatible to Arduino AVR model where interrupts are disabled
136+
// we disable them before we call the client ISR
137+
uint32_t savedPS = xt_rsil(15); // stop other interrupts
138+
if (handler->arg)
139+
{
140+
((voidFuncPtrArg)handler->fn)(handler->arg);
141+
}
142+
else
143+
{
144+
handler->fn();
170145
}
146+
xt_wsr_ps(savedPS);
147+
}
171148
}
172149
ETS_GPIO_INTR_ENABLE();
173150
}
174151

175-
extern void cleanupFunctional(void* arg);
176-
177-
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void* arg, int mode, bool functional)
152+
extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void* arg, int mode)
178153
{
179-
// #5780
180-
// https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map
181-
if ((uint32_t)userFunc >= 0x40200000)
182-
{
183-
// ISR not in IRAM
184-
::printf((PGM_P)F("ISR not in IRAM!\r\n"));
185-
abort();
186-
}
187-
188-
if(pin < 16) {
189-
ETS_GPIO_INTR_DISABLE();
190-
interrupt_handler_t* handler = &interrupt_handlers[pin];
191-
handler->mode = mode;
192-
handler->fn = (voidFuncPtr)userFunc;
193-
if (handler->functional && handler->arg) // Clean when new attach without detach
154+
// #5780
155+
// https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map
156+
if ((uint32_t)userFunc >= 0x40200000)
194157
{
195-
cleanupFunctional(handler->arg);
158+
// ISR not in IRAM
159+
::printf((PGM_P)F("ISR not in IRAM!\r\n"));
160+
abort();
196161
}
197-
handler->arg = arg;
198-
handler->functional = functional;
199-
interrupt_reg |= (1 << pin);
200-
GPC(pin) &= ~(0xF << GPCI);//INT mode disabled
201-
GPIEC = (1 << pin); //Clear Interrupt for this pin
202-
GPC(pin) |= ((mode & 0xF) << GPCI);//INT mode "mode"
203-
ETS_GPIO_INTR_ATTACH(interrupt_handler, &interrupt_reg);
204-
ETS_GPIO_INTR_ENABLE();
205-
}
206-
}
207162

208-
extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void* arg, int mode)
209-
{
210-
__attachInterruptFunctionalArg(pin, userFunc, arg, mode, false);
163+
if(pin < 16) {
164+
ETS_GPIO_INTR_DISABLE();
165+
interrupt_handler_t* handler = &interrupt_handlers[pin];
166+
handler->mode = mode;
167+
handler->fn = (voidFuncPtr)userFunc;
168+
handler->arg = arg;
169+
interrupt_reg |= (1 << pin);
170+
GPC(pin) &= ~(0xF << GPCI);//INT mode disabled
171+
GPIEC = (1 << pin); //Clear Interrupt for this pin
172+
GPC(pin) |= ((mode & 0xF) << GPCI);//INT mode "mode"
173+
ETS_GPIO_INTR_ATTACH(interrupt_handler, &interrupt_reg);
174+
ETS_GPIO_INTR_ENABLE();
175+
}
211176
}
212177

213178
extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int mode)
214179
{
215-
__attachInterruptFunctionalArg(pin, (voidFuncPtrArg)userFunc, 0, mode, false);
180+
__attachInterruptArg(pin, (voidFuncPtrArg)userFunc, 0, mode);
216181
}
217182

218-
extern void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin)
183+
extern void __detachInterrupt(uint8_t pin)
219184
{
220185
if (pin < 16)
221186
{
@@ -225,40 +190,43 @@ extern void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin)
225190
interrupt_reg &= ~(1 << pin);
226191
interrupt_handler_t* handler = &interrupt_handlers[pin];
227192
handler->mode = 0;
228-
handler->fn = 0;
229-
if (handler->functional && handler->arg)
230-
{
231-
cleanupFunctional(handler->arg);
232-
}
233-
handler->arg = 0;
234-
handler->functional = false;
193+
handler->fn = nullptr;
194+
handler->arg = nullptr;
235195
if (interrupt_reg)
236196
{
237197
ETS_GPIO_INTR_ENABLE();
238198
}
239199
}
240200
}
241201

202+
extern void* __detachInterruptArg(uint8_t pin)
203+
{
204+
void* arg = (pin < 16) ? interrupt_handlers[pin].arg : nullptr;
205+
__detachInterrupt(pin);
206+
return arg;
207+
}
208+
242209
void initPins() {
243-
//Disable UART interrupts
244-
system_set_os_print(0);
245-
U0IE = 0;
246-
U1IE = 0;
210+
//Disable UART interrupts
211+
system_set_os_print(0);
212+
U0IE = 0;
213+
U1IE = 0;
247214

248-
for (int i = 0; i <= 5; ++i) {
249-
pinMode(i, INPUT);
250-
}
251-
// pins 6-11 are used for the SPI flash interface
252-
for (int i = 12; i <= 16; ++i) {
253-
pinMode(i, INPUT);
254-
}
215+
for (int i = 0; i <= 5; ++i) {
216+
pinMode(i, INPUT);
217+
}
218+
// pins 6-11 are used for the SPI flash interface
219+
for (int i = 12; i <= 16; ++i) {
220+
pinMode(i, INPUT);
221+
}
255222
}
256223

257224
extern void pinMode(uint8_t pin, uint8_t mode) __attribute__ ((weak, alias("__pinMode")));
258225
extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__ ((weak, alias("__digitalWrite")));
259226
extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead")));
260227
extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt")));
261-
extern void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void* arg, int mode) __attribute__((weak, alias("__attachInterruptArg")));
262228
extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt")));
229+
extern void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void* arg, int mode) __attribute__((weak, alias("__attachInterruptArg")));
230+
extern void* detachInterruptArg(uint8_t pin) __attribute__ ((weak, alias("__detachInterruptArg")));
263231

264232
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#include <FunctionalInterrupt.h>
2+
#include <Arduino.h>
3+
4+
#if defined(ESP32)
5+
#define BUTTON1 16
6+
#define BUTTON2 17
7+
#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI)
8+
#define BUTTON1 D4
9+
#define BUTTON2 D3
10+
#else
11+
#define BUTTON1 2
12+
#define BUTTON2 0
13+
#endif
14+
15+
class Button {
16+
public:
17+
Button(uint8_t reqPin) : PIN(reqPin) {
18+
pinMode(PIN, INPUT_PULLUP);
19+
// Arduino C API:
20+
//attachInterruptArg(PIN, [](void* self) {
21+
// static_cast<Button*>(self)->isr();
22+
//}, this, FALLING); // works on ESP32; fails on ESP8266: "ISR not in IRAM"
23+
//attachInterruptArg(PIN, reinterpret_cast<void(*)(void*)>(&isr_static), this, FALLING); // works on ESP32; works on ESP8266
24+
// FunctionalInterrupts API:
25+
attachScheduledInterrupt(PIN, [this](InterruptInfo ii) {
26+
Serial.print("Pin ");
27+
Serial.println(ii.pin);
28+
isr();
29+
}, FALLING); // works on ESP32; works on ESP8266
30+
};
31+
~Button() {
32+
// Arduino C API:
33+
//detachInterrupt(PIN);
34+
// FunctionalInterrupt API:
35+
detachFunctionalInterrupt(PIN);
36+
}
37+
38+
#if defined(ESP8266)
39+
void ICACHE_RAM_ATTR isr()
40+
#elif defined(ESP32)
41+
void IRAM_ATTR isr()
42+
#endif
43+
{
44+
numberKeyPresses += 1;
45+
pressed = true;
46+
}
47+
48+
#if defined(ESP8266)
49+
static void ICACHE_RAM_ATTR isr_static(Button* const self)
50+
#elif defined(ESP32)
51+
static void IRAM_ATTR isr_static(Button* const self)
52+
#endif
53+
{
54+
self->isr();
55+
}
56+
57+
void checkPressed() {
58+
if (pressed) {
59+
Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses);
60+
pressed = false;
61+
}
62+
}
63+
64+
private:
65+
const uint8_t PIN;
66+
volatile uint32_t numberKeyPresses = 0;
67+
volatile bool pressed = false;
68+
};
69+
70+
Button* button1;
71+
Button* button2;
72+
73+
74+
void setup() {
75+
Serial.begin(115200);
76+
Serial.println("FunctionalInterrupt test/example");
77+
78+
button1 = new Button(BUTTON1);
79+
button2 = new Button(BUTTON2);
80+
81+
Serial.println("setup() complete");
82+
}
83+
84+
void loop() {
85+
button1->checkPressed();
86+
button2->checkPressed();
87+
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#######################################
2+
# Datatypes (KEYWORD1)
3+
#######################################
4+
5+
InterruptInfo KEYWORD1
6+
ArgStructure KEYWORD1
7+
8+
#######################################
9+
# Methods and Functions (KEYWORD2)
10+
#######################################
11+
12+
attachScheduledInterrupt KEYWORD2
13+
detachFunctionalInterrupt KEYWORD2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name=FunctionalInterrupt
2+
version=1.0
3+
author=hreintke <[email protected]>
4+
maintainer=hreintke <[email protected]>
5+
sentence=C++ functional and scheduled interrupt handling
6+
paragraph=
7+
category=Other
8+
url=https://github.com/esp8266/Arduino
9+
architectures=esp8266
10+
dot_a_linkage=true

0 commit comments

Comments
 (0)