How to use Interrupt inside class

I want to write a rotary encoder code
but i don't know how to use interrupt inside the class on Raspberry Pi pico
this is my code

class rotaryEncoder {
  private:
    uint8_t PhaseCLK, PhaseDT;
    int counter;
    bool aState;
    bool aLastState;
    void encoderInterrupt();
  public:
    void setCLK(uint8_t);
    void setDT(uint8_t);
    void setPhase(uint8_t, uint8_t);
    void init();
    void startCounter();
    void resetCounter();
    void stopCounter();
    int read(); 
};
void rotaryEncoder::setCLK(uint8_t CLKpin){ PhaseCLK = CLKpin; }
void rotaryEncoder::setDT(uint8_t DTpin){ PhaseDT = DTpin; }
void rotaryEncoder::setPhase(uint8_t CLKpin, uint8_t DTpin) {
  PhaseCLK = CLKpin;
  PhaseDT = DTpin;
}
void rotaryEncoder::init(){
  pinMode(PhaseCLK, INPUT);
  pinMode(PhaseDT, INPUT);
}
void rotaryEncoder::startCounter(){
  counter = 0;
  attachInterrupt(digitalPinToInterrupt(PhaseCLK), encoderInterrupt, CHANGE);
  aLastState = digitalRead(PhaseCLK);
}
void rotaryEncoder::encoderInterrupt(){
  aState = digitalRead(PhaseCLK);

  if (aState != aLastState){

    if (digitalRead(PhaseDT) != aState) {
      counter ++;
    } else {
      counter --;
    }
  }
  aLastState = aState;
}
void rotaryEncoder::resetCounter(){
  detachInterrupt(PhaseCLK);
  counter = 0;
  attachInterrupt(digitalPinToInterrupt(PhaseCLK), encoderInterrupt, CHANGE);
  aLastState = digitalRead(PhaseCLK);
}
void rotaryEncoder::stopCounter(){
  detachInterrupt(PhaseCLK);
}
int rotaryEncoder::read(){
  return(counter);
}
uint8_t rotaryEncoder::PhaseCLK, rotaryEncoder::PhaseDT;
int rotaryEncoder::counter;
bool rotaryEncoder::aState;
bool rotaryEncoder::aLastState;


rotaryEncoder Encoder1;
rotaryEncoder Encoder2;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Encoder1.setPhase(2, 3);
  Encoder1.init();
  Encoder1.startCounter();

  Encoder2.setPhase(4, 5);
  Encoder2.init();
  Encoder2.startCounter();
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.println("Encoder1:" + String(Encoder1.read()));
  Serial.println("Encoder2:" + String(Encoder2.read()));
  delay(100);

}

Why? Several excellent libraries are already out there.

Why do you think you need to use an interrupt to read out a rotary? I always just poll them; works fine.

The ISR will evidently not be part of the class. So simple answer: you don't.

I want to read 10 rotary encoder, but the libraries I found can't seems do that

I hope it can not only read rotary encoder , but also transfer data to the computer

In a recent project I read 4, using polling, on an ATMega328p (much slower than your Pi Pico), using an existing rotary encoder library. So apparently it can be done...

My project did the same, albeit reporting data over I2C on request by a master. It doesn't miss a beat. No interrupts needed for the rotaries.

1 Like

thank you very much

If you want to have the whole interrupt handling inside your class, you need a function to set the interrupt vector. This depends on the board you are using. For ARM-MC it is defined in the specific header files, e.g. for my Nano BLE 33 it is the function
__NVIC_SetVector((IRQn_Type) x, (dword) irqHandlerStatic)
defined in core_cm4.h (reference in Arduino.h, no need to search).
The function irqHandlerStatic has to be static. From this function you call your instance specific interrupt handlers.
For each instance of the class you have to define a static variable pointing to your instance
className *className::instPtr0 = NULL;
className *className::instPtr1 = NULL;
In your class constructor you load this vector with your class interrupt handler like
instPtr0 = this;
And from your static interrupt handler you call your class interrupt handler with
void class::irqHandlerStatic0()
{
if(instPtr0 == NULL) return;
instPtr0->irqHandler();
}
I think, it makes sense only if you have the same handler (that of your class) for several instances with different interrupt sources. E.g. if You write a universal timer class for all possible timers (5 with Nano BLE 33).

As You have only one interrupt source and only one instance of your class (I suppose), it is more simple to attach an interrupt handler defined in the (let me call it) C-area of your sketch. Make an instance of your class like
ClassName classInst;
and call the interrupt handler of your class from your outside handler with
classInst.intHandler();
Good luck

1 Like

I like that thought; it would indeed be a solution, although (as I argued above) I personally think it would make more sense to evade the problem altogether.

However, to latch into your train of thought: another possibility is to have one ISR defined in the main sketch, and an update-routine in the class that can be called by the ISR. You could have any (reasonable) number of class instances register with the ISR, much like how you register timer interrupt handlers on e.g. an ESP platform.

In practice this approach works fairly well, but there are platform-specific requirements to take note of (much like in your own proposed solution). For instance, on ESP platforms it requires that the ISR-called routines are stored in IRAM to prevent long jumps in the ISR, which may (or may not...in practice it sometimes works OK) trigger the watchdog and cause a kernel panic. I don't know what the caveats on Pi Pico would be, but there are likely to be some. Hence my recommendation to avoid the problem altogether as in my experience it doesn't compare favorably to the lower complexity of an alternative solution.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.