0

I am writing code for reading in a quadrature encoder. I used attachInterrupt on pin 2 and 3, but by using the same ISR.

    int encAPin= 2;
    int encBPin= 3;
    volatile int encAValue= 0;
    volatile int encBValue= 0;
    volatile int encCount= 0;

    void setup() {
      // put your setup code here, to run once:
      pinMode(encAPin, INPUT);
      pinMode(encBPin, INPUT);
      attachInterrupt(digitalPinToInterrupt(encAPin),encUpdate, CHANGE);
      attachInterrupt(digitalPinToInterrupt(encBPin),encUpdate, CHANGE);
      Serial.begin(9600);
    }

    void loop() {
      // put your main code here, to run repeatedly:
      Serial.println(encCount);
    }

    void encUpdate() {
      if(digitalRead(encAPin)==RISING){
        if(digitalRead(encBPin)==LOW){
          encCount++;
        }else if(digitalRead(encBPin)==HIGH){
          encCount--;
        }
      }
      if(digitalRead(encAPin)==HIGH){
        if(digitalRead(encBPin)==RISING){
          encCount++;
        }else if(digitalRead(encBPin)==FALLING){
          encCount--;
        }
      }
      if(digitalRead(encAPin)==FALLING){
        if(digitalRead(encBPin)==HIGH){
          encCount++;
        }else if(digitalRead(encBPin)==LOW){
          encCount--;
        }
      }
      if(digitalRead(encAPin)==LOW){
        if(digitalRead(encBPin)==FALLING){
          encCount++;
        }else if(digitalRead(encBPin)==RISING){
          encCount--;
        }
      }
    }

When I tried Serial.print to check whether or not the pin A's and B's inputs were changing, I noticed that only pin A's (pin 2) input was changing. I made sure that the circuit and the wiring were all correct. Is there something about my code that is not reading pin B's input properly?

  • 1
    What's this? if(digitalRead(encAPin)==RISING) - that doesn't make any sense. It is either HIGH or LOW. – Nick Gammon Jan 30 '17 at 7:58
  • I apologize. I was merely following my notes, and it indicated that there are 8 cases for the input graphs of pin A and B. Thank you. – Skipher Jan 30 '17 at 8:07
  • I think you have mixed up interrupts and pin reading. A digital pin will only ever he HIGH or LOW, The attachInteruppt() function takes a wider variety of states (I don't know if CHANGE and RISING are valid). – Code Gorilla Jan 30 '17 at 11:20
  • There are only 4 states that a pair of pins can be in. That is, 2² - 2 states (HIGH and LOW), and ² pins. – Majenko Jan 30 '17 at 11:30
2

You're confusing interrupt events and pin states here.

A pin can be either HIGH or LOW. Never anything else.

An interrupt can trigger on one of four events:

  1. The pin transitions from HIGH to LOW (FALLING)
  2. The pin transitions from LOW to HIGH (RISING)
  3. The pin transitions from one state to another (CHANGE)
  4. The pin remains low (LOW)

Your code should be (as you have done) attaching the interrupt to the pins using CHANGE so you can see any change in the state of the pins.

Your interrupt code should then examine the two pins to see their current state.

But there is one bit missing from your code, and that is what was the state before this interrupt triggered? - i.e., from what did the pins change from to get to this state?

The simplest way of coding this kind of thing is to think of each pin as a bit in a 2-bit binary number. Join the two pins together to form a number between 0 and 3:

uint8_t pinVal = (digitalRead(encBPin) << 1) | digitalRead(encAPin);

The truth table for that would be:

B | A | B<<1 | pinVal
0 | 0 |  00  |   00 (0)
0 | 1 |  00  |   01 (1)
1 | 0 |  10  |   10 (2)
1 | 1 |  10  |   11 (3)

There you have 4 states. And if you remember that state from one interrupt call to the next, you can compare the current value to the previous value. And that is where you get your 8 states from. Encoders use Gray Code so the sequence of numbers you would get as you rotate the encoder would be:

0, 1, 3, 2, 0, 1, 3, 2 ...

Or the other way:

2, 3, 1, 0, 2, 3, 1, 0 ...

And it's the transition between those values that tells you what the encoder is doing:

0 <-> 1 <-> 3 <-> 2 <-> 0 ...

Each of those transitions can go two ways, and if you count the arrows (< and >) there are 8 of them there, which are the 8 states you were thinking of before.

So if the previous value is stored in oldVal from the last time the interrupt triggered, and the new value is in newVal, your 8 states are:

  • oldVal == 0 && newVal == 1
  • oldVal == 1 && newVal == 3
  • oldVal == 3 && newVal == 2
  • oldVal == 2 && newVal == 0
  • oldVal == 0 && newVal == 2
  • oldVal == 2 && newVal == 3
  • oldVal == 3 && newVal == 1
  • oldVal == 1 && newVal == 0

The first four are progressing in one direction, the last four in the other - one set is clockwise, one is anti-clockwise.

So you could have an (untested) interrupt handler that looks something like this:

void encUpdate() {
    static uint8_t oldVal = 0; // Static variable to remember the last state
    uint8_t newVal = (digitalRead(encBPin)) << 1 | digitalRead(encAPin);

    if (
        (oldVal == 0 && newVal == 1) ||
        (oldVal == 1 && newVal == 3) ||
        (oldVal == 3 && newVal == 2) ||
        (oldVal == 2 && newVal == 0)) {
            encCount++;
    } else {
        encCount--;
    }
    oldVal = newVal;
}

You notice I've only implemented the comparison for the first four states and not the last four. Since there are only 8 states, and those states cause one of only two things to happen, if it's not one of the first four states, which would increase your count, it must then be one of the last four states which decrease your count. No point in checking for them since it can't be anything else. If you turned it clockwise then increase the value. Otherwise you must have turned it anti-clockwise since there is no other direction you can possibly turn it in, so decrease the count.

This is precisely why Gray Code is used - only one pin ever changes at a time, so there is no ambiguity in what has happened - either you turned it one code-point in one direction or you turned it one code-point in the other. Even if there is lots of bounce it will just bounce between two neighbouring code-points and settle on the final new code point (as long as the interrupt can react fast enough to catch all the bounces).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.