Not sure if best to post here and/or as a Github issue:
I did open this up as an issue a couple of days ago:
calling attachInterrupt(pin, ISR) within a c++ objects constructor · Issue #56 · arduino/ArduinoCore-renesas (github.com)
And mentioned in a different thread about the Encoder library.
Simply put: any class that uses the attachInterrupt in its constructor, will fail for any of these objects that are global.
I have a simple example sketch, that shows it:
#define USE_ONESTEP_CONSTRUCTOR
class test_stuff_in_constructor {
public:
void begin(uint8_t pin) {
if (_pin != 0xff) {
Serial.println("begin: was previously called");
return;
}
_pin = pin;
pinMode(pin, INPUT_PULLUP);
attachInterrupt(pin, &changeFunc, CHANGE);
pinMode(LED_BUILTIN, OUTPUT);
_counter = 0;
}
test_stuff_in_constructor() {}
test_stuff_in_constructor(uint8_t pin) {
begin(pin);
}
uint32_t count() {
return _counter;
}
uint8_t pin() {
return _pin;
}
private:
uint8_t _pin = 0xff;
static volatile uint32_t _counter;
static void changeFunc() {
digitalWrite(LED_BUILTIN, HIGH);
_counter++;
}
};
#ifdef USE_ONESTEP_CONSTRUCTOR
test_stuff_in_constructor testobj(2);
#else
test_stuff_in_constructor testobj;
#endif
volatile uint32_t test_stuff_in_constructor::_counter = 0;
extern void DumpIRQTable();
void setup() {
while (!Serial && millis() < 4000)
;
Serial.begin(115200);
Serial.println("Test constructor stuff");
DumpIRQTable();
#ifndef USE_ONESTEP_CONSTRUCTOR
testobj.begin(2);
DumpIRQTable();
#endif
Serial.print("Pin: ");
Serial.println(testobj.pin());
}
uint32_t previous_count = 0;
void loop() {
// put your main code here, to run repeatedly
uint32_t count = testobj.count();
if (count != previous_count) {
Serial.println(count, DEC);
previous_count = count;
}
}
Note: I am in process of trying to localize the issue. And I have added some instrumentation code
to the attachInterrupt (interrupts.cpp). Saved away stuff, and add a print debug information, that I can call later...
(Don't think I can call Serial.print() in constructor, and hardware debug not not implemented? yet.
//////////////////////////////
pin_size_t lai_pinNumber;
voidFuncPtrParam lai_func;
PinStatus lai_mode;
void* lai_param;
CIrq *lai_irq_context;
int lai_ch;
////////////////////////
void DumpIRQTable() {
Serial.println("Dump IRQ Table");
Serial.print(lai_pinNumber, DEC); Serial.print(" ");
Serial.print((uint32_t)lai_func, HEX); Serial.print(" ");
Serial.print(lai_mode, DEC); Serial.print(" ");
Serial.print((uint32_t)lai_param, HEX); Serial.print(" ");
Serial.print(lai_ch, DEC); Serial.print(" ");
Serial.println((uint32_t)lai_irq_context, HEX);
for (int i = 0; i < MAX_IRQ_CHANNEL; i++) {
Serial.print(i, DEC);
CIrq *pcirq = IrqChannel.get(i, false);
if (pcirq) {
Serial.print(" 0x");
Serial.print((uint32_t)pcirq->fnc_void, HEX);
Serial.print(" ");
Serial.println(pcirq->cfg.irq);
} else {
Serial.println(" <None>");
}
}
}
Mucked up attach:
/* -------------------------------------------------------------------------- */
void attachInterruptParam(pin_size_t pinNumber, voidFuncPtrParam func, PinStatus mode, void* param) {
/* -------------------------------------------------------------------------- */
lai_pinNumber = pinNumber;
lai_func = func;
lai_mode = mode;
lai_param = param;
CIrq *irq_context = nullptr;
int ch = pin2IrqChannel(pinNumber);
if(ch >= 0 && ch < MAX_IRQ_CHANNEL) {
irq_context = IrqChannel.get(ch,true);
}
lai_ch = ch;
lai_irq_context = irq_context;
...
So if I call it in the constructor:
Test constructor stuff
Dump IRQ Table
2 4101 2 0 1 20000A30
0 <None>
1 0x0 0
2 <None>
3 <None>
4 <None>
5 <None>
6 <None>
7 <None>
8 <None>
9 <None>
10 <None>
11 <None>
12 <None>
13 <None>
14 <None>
Pin: 2
And the ISR is never fired.
Changing to the two step initialization and things work.
Test constructor stuff
Dump IRQ Table
0 0 0 0 0 0
0 <None>
1 <None>
2 <None>
3 <None>
4 <None>
5 <None>
6 <None>
7 <None>
8 <None>
9 <None>
10 <None>
11 <None>
12 <None>
13 <None>
14 <None>
Dump IRQ Table
2 4101 2 0 1 20000A58
0 <None>
1 0x4101 5
2 <None>
3 <None>
4 <None>
5 <None>
6 <None>
7 <None>
8 <None>
9 <None>
10 <None>
11 <None>
12 <None>
13 <None>
14 <None>
Pin: 2
And the ISR is called...
It almost looks like the code logically called detachInterrupt() or ... I don't think it called the IrqPool destructor as it would have cleared the memory allocation so would not shown anything in slot 1.
Also could be some other c++ constructor called later that messed it up.
Still debugging