简体   繁体   中英

How to read a rotary encoder with interrupts on ESP32 and global flags

When using interrupts, any kind of digitalRead should be obsolete, or so i think. Let me explain my understanding of this to show my problem:

Say, a quadrature incremental rotary encoder (RE) is pulled up on both signal pins and is therefore idle HIGH. If i configure an interrupt service routine (ISR) on the falling edge on both pins and have the ISR for pin B do nothing more than set the flag "flag_B" to false (representing the logic state at the pin) and the ISR for pin A sets or resets a flag "flag_clockwise" depending on the state of flag_B during A's ISR, then i should get the right direction for every turning step, right? My only task is to then set the state of pin B to high again after the ISRs and my code should be ready for the next interrupt. But that doesn't work, with many random cases of the wrong direction being displayed or showing counter clockwise on the first half of the turning step when you can feel a little "bump" while turning the knob and then going clockwise on the second half, when the shaft has completed a single step, basically performing two opposite actions while only turning one step. Is my logic flawed? All signals are debounced in hardware and look like expected on the oscilloscope.

ISRs:

//re-CLK signal
void IRAM_ATTR reCLK_ISR(void){
  if(myFlags->pinBstate){
    //this means the first interrupt of a turning step comes from the clock, not data
    myFlags->direction = CCW;
  }
  else{
    //this means the interrupt of a turning step occured from data first
    myFlags->direction = CW;
  }
}

//re-DATA signal
void IRAM_ATTR reDATA_ISR(void){
  myFlags->pinDATAstate = false;
}

further processing:

//some function 

//scroll = CW / CCW
    if((myFlags->direction) != STOP){

      if(myFlags->direction == CW){ 

        //DEBUG
        digitalWrite(PIN_LED_DEBUG, LOW);

        }
      }
      if(myFlags->direction == CCW){

        //DEBUG
        digitalWrite(PIN_LED_DEBUG, HIGH);

      }
      myFlags->pinDATAstate = true;
      myFlags->direction = STOP;   //direction has 3 states: CW, CCW and STOP
    }

the last 2 statements should restore all affected flags to the state before the encoder was touched, meaning ready for the next turn. The LED is just a replacement for different calculations which should be performed depending on the turning direction.

Even if the pin is assigned as interrupt, you can still use digitalRead() to determine the direction. For example:

const uint8_t pinA = 25;
const uint8_t pinB = 26;
volatile int counter;

void IRAM_ATTR isr(){
  if(digitalRead(pinA) == digitalRead(pinB)) {
    //Clockwise
    counter++;
  } else {
    //Counter Clockwise
    counter--;
  }
}

You don't even need another ISR to determine the direction. I used this method to determine both the speed and the direction;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM