みなさん、Arduinoやってますか〜?

 

私は、Arduinoそのものもやってるし、ESP32でも、そしてRaspberryPI picoでも、お世話になっています。

とはいうものの、Arduino IDEから最近は、VSCode & PlatformIO に移行しつつありますが。

 

さて、Arduinoで、ハードウェア割り込みを使う時に、

 

attachInterrupt

 

って使ってますよね。

 

たとえば、ロータリーエンコーダの入力とか(loopで処理してもいいけど、なんか無駄な感じが・・・)、まあ少し複雑なプログラム書くときには必須ですよね。

 

で、普通だと、


  void _isr(){
      //割り込みコード
  }

  void begin(){
      attachInterrupt(PIN, _isr, CHANGE);
  }

みたいに書くんだと思いますが、

 

少しクラスとか使ってたり、同じ割り込みを複数で使いたかったりすると、


  void _isr(){
      //割り込みコード
      // ここでPIN_Aと、PIN_Bで処理分けたいけど・・・
  }

  void begin(){
      attachInterrupt(PIN_A, _isr, CHANGE);
      attachInterrupt(PIN_B, _isr, CHANGE);
  }

こん感じで、1つの割り込みで、複数の処理させたいとか、やりたいですよね。

 

なので、例えば、


  struct Param {
      unsigned char pin;
      int otherData;
  };

  void _isr(const Param* param){
      if(param->pin == PIN_A){
          // PIN_Aの処理
      }else if(param->pin == PIN_A){
          // PIN_Bの処理
      }
  }

  Param a = {PIN_A, 100};
  Param b = {PIN_B, 200};

  void begin(){
      attachInterruptWithParam(PIN_A, _isr, &a, CHANGE);
      attachInterruptWithParam(PIN_B, _isr, &b, CHANGE);
  }

みたいに、1つだけでも引数渡せれば、みたいな処理したくなりますよね。

 

クラスメソッド渡すときとかでも、


  class Sample {
  private:
     int count = 0;
  public:
     static void onChange(Sample* _this){
        // 変更された!
        _this->count++;
     }
  public:
     void begin(){
        attachInterruptWithParam(PIN_A, onChange, this, CHANGE);
     }
  }

こんな感じで使えたり。

 

で、いろいろ試行錯誤して、とりあえず〜、って感じでできたのが、これ。

 

まあ、要は渡される変数を、固定領域に格納しておいて、attachInterruptの中は、Lambda式で記載することで、そしてマクロを活用することで、実現しています。

※固定領域では256個程度の割り込みは格納できるようにしているけど、まあそこまで使わないよね、という想定。

※また、__COUNT__マクロをつかちゃってるので、他で利用している場合は要注意です。


  #define __MAX_ATTACH_INTERRUPT_WITH_PARAM 256

  void* __attachInterruptParam[__MAX_ATTACH_INTERRUPT_WITH_PARAM];

  #define attachInterruptWithParam(PIN, CB, P, F)                               \
  {if((__COUNTER__ / 3) < __MAX_ATTACH_INTERRUPT_WITH_PARAM){                   \
    __attachInterruptParam[(__COUNTER__ - 1) / 3] = P;                          \
    attachInterrupt(PIN, [](){                                                  \
      CB(static_cast<typeof(p)>(__attachInterruptParam[(__COUNTER__ - 2) / 3]));           \
    }, F);                                                                      \
  }}

 

コピペして使えるレベルだとは思いますが、

ある程度は理解して使っていただければと。