Seeking ideas on using state in callbacks

Status
Not open for further replies.

shawn

Well-known member
These days, as I've been using callbacks more and more (through Arduino-style APIs, for example IntervalTimer or attachInterrupt()), it's become apparent that this programming paradigm depends on there being global state. I'm finding myself rewriting these pieces but I lose the ability to automatically upgrade my code if any of the core code changes, or having very large switch statements that assign specific ISRs, depending on which pin, etc.

My current consternation has to do with finding out which pin triggered an edge interrupt. I can solve this in a few ways:
  1. Reimplement internal APIs that I need (yuck).
  2. Write a callback routine for every pin possibility (yuck again).
  3. ???.

I'm seeking opinions on how to do approach #3. Following is some example code. My challenge to you is to modify the "API Section" of the program so that the ISR (could be new functions) prints the state of the associated Holder object. You aren't allowed to modify the "User Section" because that emulates calling an API.

Code:
// --------------
//  API Section
// --------------

void pin_rising_isr();

// Holds some state.
class Holder {
 public:
  Holder(int state) : state_(state), risingPin_(-1) {}

  void setRisingPin(int pin) {
    if (pin >= CORE_NUM_DIGITAL) {
      return;
    }
    risingPin_ = pin;
    if (pin >= 0) {
      attachInterrupt(pin, pin_rising_isr, RISING);
    }
  }

  int state_;
  int risingPin_;
};

void pin_rising_isr() {
  Serial.println("My caller's state is: ???");
}

// --------------
//  User Section
// --------------

Holder h1{100};
Holder h2{200};

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 4000) {
    // Wait for Serial to initialize
  }

  h1.setRisingPin(2);
  h2.setRisingPin(6);
}

void loop() {
  yield();
}
 
Here's my solution for getting state into GPIO ISRs. I'm using it from a header. It's the "write a callback for every possible pin" solution.

Code:
#include <core_pins.h>

// Holds callback information for one pin.
struct PinCallbackInfo {
  void (*callback)(void *state);
  void *state;
};

// Each element holds a callback function along with its state.
static volatile PinCallbackInfo pinCallbacks[CORE_NUM_DIGITAL];

#define PIN_ISR(N)                                \
  static void pin##N##_isr() {                    \
    void (*f)(void *) = pinCallbacks[N].callback; \
    if (f != nullptr) {                           \
      f(pinCallbacks[N].state);                   \
    }                                             \
  }

PIN_ISR(0)
PIN_ISR(1)
PIN_ISR(2)
PIN_ISR(3)
PIN_ISR(4)
PIN_ISR(5)
PIN_ISR(6)
PIN_ISR(7)
PIN_ISR(8)
PIN_ISR(9)
PIN_ISR(10)
PIN_ISR(11)
PIN_ISR(12)
PIN_ISR(13)
PIN_ISR(14)
PIN_ISR(15)
PIN_ISR(16)
PIN_ISR(17)
PIN_ISR(18)
PIN_ISR(19)
PIN_ISR(20)
PIN_ISR(21)
PIN_ISR(22)
PIN_ISR(23)
PIN_ISR(24)
PIN_ISR(25)
PIN_ISR(26)
#ifndef KINETISL
PIN_ISR(27)
PIN_ISR(28)
PIN_ISR(29)
PIN_ISR(30)
PIN_ISR(31)
PIN_ISR(32)
PIN_ISR(33)
#ifdef CORE_PIN34_PORTREG
PIN_ISR(34)
PIN_ISR(35)
PIN_ISR(36)
PIN_ISR(37)
PIN_ISR(38)
PIN_ISR(39)
PIN_ISR(40)
PIN_ISR(41)
PIN_ISR(42)
PIN_ISR(43)
PIN_ISR(44)
PIN_ISR(45)
PIN_ISR(46)
PIN_ISR(47)
PIN_ISR(48)
PIN_ISR(49)
PIN_ISR(50)
PIN_ISR(51)
PIN_ISR(52)
PIN_ISR(53)
PIN_ISR(54)
PIN_ISR(55)
PIN_ISR(56)
PIN_ISR(57)
PIN_ISR(58)
PIN_ISR(59)
PIN_ISR(60)
PIN_ISR(61)
PIN_ISR(62)
PIN_ISR(63)
#endif  // CORE_PIN34_PORTREG
#endif  // !KINETISL

#undef PIN_ISR

// This is used to assign a function via attachInterrupt().
static void (*pinISRs[CORE_NUM_DIGITAL])(){
    pin0_isr,
    pin1_isr,
    pin2_isr,
    pin3_isr,
    pin4_isr,
    pin5_isr,
    pin6_isr,
    pin7_isr,
    pin8_isr,
    pin9_isr,
    pin10_isr,
    pin11_isr,
    pin12_isr,
    pin13_isr,
    pin14_isr,
    pin15_isr,
    pin16_isr,
    pin17_isr,
    pin18_isr,
    pin19_isr,
    pin20_isr,
    pin21_isr,
    pin22_isr,
    pin23_isr,
    pin24_isr,
    pin25_isr,
    pin26_isr,
#ifndef KINETISL
    pin27_isr,
    pin28_isr,
    pin29_isr,
    pin30_isr,
    pin31_isr,
    pin32_isr,
    pin33_isr,
#ifdef CORE_PIN34_PORTREG
    pin34_isr,
    pin35_isr,
    pin36_isr,
    pin37_isr,
    pin38_isr,
    pin39_isr,
    pin40_isr,
    pin41_isr,
    pin42_isr,
    pin43_isr,
    pin44_isr,
    pin45_isr,
    pin46_isr,
    pin47_isr,
    pin48_isr,
    pin49_isr,
    pin50_isr,
    pin51_isr,
    pin52_isr,
    pin53_isr,
    pin54_isr,
    pin55_isr,
    pin56_isr,
    pin57_isr,
    pin58_isr,
    pin59_isr,
    pin60_isr,
    pin61_isr,
    pin62_isr,
    pin63_isr,
#endif  // CORE_PIN34_PORTREG
#endif  // !KINETISL
};

static void attachInterruptWithState(uint8_t pin,
                                     void (*callback)(void *state), void *state,
                                     int mode) {
  pinCallbacks[pin].callback = callback;
  pinCallbacks[pin].state = state;
  attachInterrupt(pin, pinISRs[pin], mode);
}

static void detachInterruptWithState(uint8_t pin) {
  pinCallbacks[pin].callback = nullptr;
  pinCallbacks[pin].state = nullptr;
  detachInterrupt(pin);
}
 
Status
Not open for further replies.
Back
Top