At the moment I try to make a library which uses attachInterrupt(?,?,RISING) to read a sensor. The article of Nick Gammon on Calling an ISR from a class helped me a lot, but his "Glue routines" get pretty ugly with more than 30 potential Interrupt Pins on the ESP32, which all need their own static void. Has anyone got a more elegant idea how to do this "Gluing"?
- 31
- 4
-
2Can you give more details about the kind of interrupts you want to handle? Type of board? In general, you will need one glue function _per interrupt_, not necessarily one per interrupt _pin_. This can make a difference if several pins share an interrupt, which is the case for the pin change interrupts on AVR. – Edgar Bonet May 30 '20 at 12:32
-
An "Arduino" library, which is ESP32 specific, is a bit problematic. An ESP32 isn't an Arduino at all, strictly speaking. – DataFiddler May 30 '20 at 13:14
-
How about going more low level? `attachInterrupt()` uses different interrupt types depending on the used pin. Most chips only have 1 or 2 external interrupt pins, so the rest is done via pin change interrupts, which cover a whole port, meaning 1 interrupt for a group of 8 pins. You could configure these interrupts directly via their control registers. Though I'm not sure, how this is organized on an ESP. – chrisl May 30 '20 at 13:17
1 Answers
As a matter of efficiency, I would favor chrisl’s advise to use the
platform's low-level interrupts if at all possible. This, however, comes
at the cost of portability: you would need an implementation specialized
for each platform you want to support. If you don't want to or cannot
maintain all this platform-specific code, attachInterrupt() may be a
reasonable option.
Then, you will absolutely need all those glue routines: since the interrupt system calls your handlers with no parameters, you will not be able to share handlers between interrupts, and you end up with as many handlers as interrupts you are going to use.
There is, however, an elegant way of creating all these specialized functions without writing them explicitly: you can ask the compiler to write them for you on the basis of a template you provide.
Below is a version of Nick Gammon's example code modified to use a template class. Note that you do not necessarily need a class to use templates: you could use template functions as interrupt handlers if that is appropriate for your use case.
template <uint8_t pin>
class myClass
{
static volatile bool switchChanged;
public:
static void begin()
{
pinMode(pin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(pin),
switchPressed, FALLING);
}
static void switchPressed()
{
switchChanged = true;
}
};
template <uint8_t pin>
volatile bool myClass<pin>::switchChanged;
myClass<2> foo;
myClass<3> bar;
void setup()
{
foo.begin();
bar.begin();
}
void loop(){}
Note also that myClass<2> and myClass<3> are two different classes:
you actually have a different class for each pin. This means that
foo::switchChanged and bar::switchChanged are two different class
members even though both are static. Since you should (in principle)
have at most one instance of each pin-specific class, you can make
everything within the class static.
The main caveat of this method is that the pin numbers have now to be compile-time constants. The great advantage is that you will have a class created for each interrupt that is actually used: no need for an array of 32 instances on an ESP32 if only a handful of interrupt pins are going to be actually used.
- 39,449
- 4
- 36
- 72
-
Thanks a lot! It works brilliant for me. I mostly got what you did, but there's one question left: Why do we have to initialize our private `volatile bool myClass
::switchChanged;` outside the class definition? – Adrian Immer Jun 01 '20 at 12:00 -
It's not an initialization, it's a definition. Static class members are _declared_ within the class definition but have to be _defined_ outside of it. Otherwise you get the link-time error “_undefined reference to `myClass<(unsigned char)2>::switchChanged`_” (and the same for `myClass<3>`). – Edgar Bonet Jun 01 '20 at 12:25