-
Notifications
You must be signed in to change notification settings - Fork 13.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixes and implementation to expose attachInterruptArg in Arduino.h #6003
Changes from 8 commits
5c8c68f
669b9ce
3c8a851
c59a2c2
b7ca699
20ff987
7e77ba6
c376c97
3594450
d6190fd
c353b51
efafcbd
e04ac23
64a56c9
38ef0f1
254302b
78aa3e8
1ecf11c
1b4077f
f21db95
3a206f2
4a78b1a
b03e7c7
bbd9948
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -109,8 +109,9 @@ typedef void (*voidFuncPtrArg)(void*); | |
|
||
typedef struct { | ||
uint8_t mode; | ||
void (*fn)(void); | ||
void * arg; | ||
voidFuncPtr fn; | ||
void* arg; | ||
bool functional; | ||
} interrupt_handler_t; | ||
|
||
//duplicate from functionalInterrupt.h keep in sync | ||
|
@@ -125,11 +126,10 @@ typedef struct { | |
void* functionInfo; | ||
} ArgStructure; | ||
|
||
static interrupt_handler_t interrupt_handlers[16]; | ||
static interrupt_handler_t interrupt_handlers[16] = { {0, 0, 0, 0}, }; | ||
static uint32_t interrupt_reg = 0; | ||
|
||
void ICACHE_RAM_ATTR interrupt_handler(void *arg) { | ||
(void) arg; | ||
void ICACHE_RAM_ATTR interrupt_handler(void *) { | ||
uint32_t status = GPIE; | ||
GPIEC = status;//clear them interrupts | ||
uint32_t levels = GPI; | ||
|
@@ -147,13 +147,16 @@ void ICACHE_RAM_ATTR interrupt_handler(void *arg) { | |
// to make ISR compatible to Arduino AVR model where interrupts are disabled | ||
// we disable them before we call the client ISR | ||
uint32_t savedPS = xt_rsil(15); // stop other interrupts | ||
ArgStructure* localArg = (ArgStructure*)handler->arg; | ||
if (localArg && localArg->interruptInfo) | ||
{ | ||
localArg->interruptInfo->pin = i; | ||
localArg->interruptInfo->value = __digitalRead(i); | ||
localArg->interruptInfo->micro = micros(); | ||
} | ||
if (handler->functional) | ||
{ | ||
ArgStructure* localArg = (ArgStructure*)handler->arg; | ||
if (localArg && localArg->interruptInfo) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this spot, discerning only null from non-null causes undefined behavior in all non-"functional" uses due to accessing arg->interruptInfo. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, I missed that, thanks for the explanation |
||
{ | ||
localArg->interruptInfo->pin = i; | ||
localArg->interruptInfo->value = __digitalRead(i); | ||
localArg->interruptInfo->micro = micros(); | ||
} | ||
} | ||
if (handler->arg) | ||
{ | ||
((voidFuncPtrArg)handler->fn)(handler->arg); | ||
|
@@ -170,8 +173,7 @@ void ICACHE_RAM_ATTR interrupt_handler(void *arg) { | |
|
||
extern void cleanupFunctional(void* arg); | ||
|
||
extern void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFunc, void *arg, int mode) { | ||
|
||
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void *arg, int mode, bool functional) { | ||
// #5780 | ||
// https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map | ||
if ((uint32_t)userFunc >= 0x40200000) | ||
|
@@ -185,12 +187,13 @@ extern void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFu | |
ETS_GPIO_INTR_DISABLE(); | ||
interrupt_handler_t *handler = &interrupt_handlers[pin]; | ||
handler->mode = mode; | ||
handler->fn = userFunc; | ||
if (handler->arg) // Clean when new attach without detach | ||
handler->fn = (voidFuncPtr)userFunc; | ||
if (handler->functional && handler->arg) // Clean when new attach without detach | ||
{ | ||
cleanupFunctional(handler->arg); | ||
} | ||
handler->arg = arg; | ||
handler->functional = functional; | ||
interrupt_reg |= (1 << pin); | ||
GPC(pin) &= ~(0xF << GPCI);//INT mode disabled | ||
GPIEC = (1 << pin); //Clear Interrupt for this pin | ||
|
@@ -200,12 +203,16 @@ extern void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFu | |
} | ||
} | ||
|
||
extern void ICACHE_RAM_ATTR __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int mode ) | ||
extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int mode) | ||
{ | ||
__attachInterruptArg (pin, userFunc, 0, mode); | ||
__attachInterruptFunctionalArg(pin, userFunc, arg, mode, false); | ||
} | ||
|
||
extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int mode ) { | ||
__attachInterruptFunctionalArg(pin, (voidFuncPtrArg)userFunc, 0, mode, false); | ||
} | ||
|
||
extern void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin) { | ||
extern void __detachInterrupt(uint8_t pin) { | ||
if(pin < 16) { | ||
ETS_GPIO_INTR_DISABLE(); | ||
GPC(pin) &= ~(0xF << GPCI);//INT mode disabled | ||
|
@@ -214,11 +221,12 @@ extern void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin) { | |
interrupt_handler_t *handler = &interrupt_handlers[pin]; | ||
handler->mode = 0; | ||
handler->fn = 0; | ||
if (handler->arg) | ||
if (handler->functional && handler->arg) | ||
{ | ||
cleanupFunctional(handler->arg); | ||
} | ||
handler->arg = 0; | ||
handler->functional = false; | ||
if (interrupt_reg) | ||
ETS_GPIO_INTR_ENABLE(); | ||
} | ||
|
@@ -243,6 +251,7 @@ extern void pinMode(uint8_t pin, uint8_t mode) __attribute__ ((weak, alias("__pi | |
extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__ ((weak, alias("__digitalWrite"))); | ||
extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead"))); | ||
extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt"))); | ||
extern void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void * arg, int mode) __attribute__ ((weak, alias("__attachInterruptArg"))); | ||
extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt"))); | ||
|
||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After reading several times, I don't understand why it is needed to have this
bool functional
.It is always checked along with the not-
nullptr
arg. Why isarg
not sufficient ?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will do a detailed check (maybe thursday. otherwise friday).
I needed it in ESP32 to distinguish the use of attachinterruptArg from user exposed and usage for functional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@d-a-v The
bool functional
prevents undefined behavior of type-castingvoid* arg
toArgStructure*
and dereferencing in all those cases that the whole exercise of exposingvoid attachInterruptArg(uint8_t pin, void (*)(void*), void * arg, int mode)
is done for: whenever it's not an
ArgStructure*
, not null either, but for instance somethis
pointer or whatever else fits intovoid*
(32bit integers).I've revisited the code and I am quite sure it's needed where it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI: I've tested using microsecond resolution instead of CPU cycles for the Software Serial bit timing. The ESP8266 shows no difference, but on the ESP32, at high bit rate (57600cps), the error rate increases measurably.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given the reduction of the complexity of the generated code, I always try to cpu cycle (if measured durations are less than 26 seconds (esp8266@160MHz) or if overflow is managed)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But here (SW serial) it impacts basic usefulness, each worsening in error rate renders it useless in more cases.
My point of view is that unconditionally requiring C++ functional objects in the attachInterruptArg interface through an API that looks like plain C attracts confusion. The compiler can't catch any of it. I have to admit, that I am not entirely happy with the whole construct between FunctionalInterrupt and core_esp8266_wiring_digital, exemplified by the need to copy&paste type definitions
//duplicate from functionalInterrupt.h keep in sync
typedef struct InterruptInfo
because the relationship is bit forced as it is right now.
But anyway, in ESP32 Arduino, it's just the same, so future patches just might take advantage or a more in similarity.