Rotary encoder

Hi all,

Learning to use a rotary encoder, I was wondering if it is possible to shorten some of the code. The code I would like to shorten are the If statements in the checkRotary loop.

I have tried this code,

( digitalRead( clockPin) == digitalRead( dataPin)) ? displayCounter ++ : displayCounter --;

but that is only adding 1 to my displayCounter every time the main loop is re-starts.

I was wondering if I could shorten the code to a one or two liner.

Please find following the complete code

const int clockPin = 2;
const int dataPin = 3;
const int switchPin = 4;

long previousDebounceTime = 0;
int debounceDelay = 10;

int previousClk;
int previousData;

int displayCounter = 0;
int oldDisplayCounter = -1;

void setup() {
  Serial.begin( 115200);

  pinMode( clockPin, INPUT);
  pinMode( dataPin, INPUT);
  pinMode( switchPin, INPUT_PULLUP);

  previousClk  = digitalRead( clockPin);
  previousData = digitalRead( dataPin);
}

void loop() {
  if( (millis() - previousDebounceTime) > debounceDelay) {
    checkRotary();                                    // check if rotary is turned
    previousClk  = digitalRead( clockPin);
    previousData = digitalRead( dataPin);
    previousDebounceTime = millis();
  }

  if( digitalRead( switchPin) == LOW){
    displayCounter = 0;                               // reset counter
  }
}

void checkRotary(){
//  ( digitalRead( clockPin) == digitalRead( dataPin)) ? displayCounter ++ : displayCounter --;

    if(( previousClk == 0) && ( previousData == 1)) {
      if(( digitalRead( clockPin) == 1) && ( digitalRead( dataPin) == 0)) displayCounter ++;
      if(( digitalRead( clockPin) == 1) && ( digitalRead( dataPin) == 1)) displayCounter --;
    }
  
    if(( previousClk == 1) && ( previousData == 0)) {
      if (( digitalRead( clockPin) == 0) && ( digitalRead( dataPin) == 1)) displayCounter ++;
      if (( digitalRead( clockPin) == 0) && ( digitalRead( dataPin) == 0)) displayCounter --;
    }
  
    if(( previousClk == 1) && ( previousData == 1)) {
      if (( digitalRead( clockPin) == 0) && ( digitalRead( dataPin) == 1)) displayCounter ++;
      if (( digitalRead( clockPin) == 0) && ( digitalRead( dataPin) == 0)) displayCounter --;
    }  
  
    if(( previousClk == 0) && ( previousData == 0)) {
      if(( digitalRead( clockPin) == 1) && ( digitalRead( dataPin) == 0)) displayCounter ++;
      if(( digitalRead( clockPin) == 1) && ( digitalRead( dataPin) == 1)) displayCounter --;
    }

  if( displayCounter != oldDisplayCounter){
    Serial.println( displayCounter);
    oldDisplayCounter = displayCounter;
  }
}

Anyway, thanks for reading.

See the StateChangeDetection example.

Any particular reason you need to shorten things to a line or two and for not using pin change interrupts to handle the encoder inputs?

Blackfin:
[..] pin change interrupts to handle the encoder inputs

One interrupt would be enough. All you have to do is:

  • check if clockPin changed from HIGH to LOW (ie. FALLING)
  • if so, check the value of dataPin, that will give you the direction of rotation

You may not even need an interrupt at all, depending on how busy the Arduino is doing other things.

Well for checking the state of a switch I don't think I need an interrupt. Because switches are slow compared to interrupts.
At the other hand, if this is the way to make things easy and simple, I am in.
The need to shorten things is that I try to squeeze as much program code in an arduino nano as possible.
I know I could take a Mega, but I try to stick to the available memory of the nano.
Another part is that I try to understand the logic behing the "shorter" markup.
Therefor I would like to know if it is feasable to shorten the code?
With an interrupt it is possible to check the "CHANGE" of an interrupt pin and act accordingly.
So next up is to check the suggested StateChangeDetection example.
Thanks for your input!

spa2036:
Well for checking the state of a switch I don't think I need an interrupt. Because switches are slow compared to interrupts.

Absolutely correct on that matter! :sunglasses:

spa2036:
At the other hand, if this is the way to make things easy and simple, I am in.

Not really. :roll_eyes:

spa2036:
The need to shorten things is that I try to squeeze as much program code in an Arduino Nano as possible.
I know I could take a Mega, but I try to stick to the available memory of the Nano.

Definitely. :grinning:

spa2036:
With an interrupt it is possible to check the "CHANGE" of an interrupt pin and act accordingly.

It is. The suggestion of using an interrupt on one pin only however immediately restricts you from consistently detecting single step movement of the encoder.

Paul__B:
The suggestion of using an interrupt on one pin only however immediately restricts you from consistently detecting single step movement of the encoder.

Why is that, Paul?

Because if the phase that isn't connected to the interrupt pin changes, nothing happens. It should, because that is a valid encoder position change.

Thank you, Paul... :wink:

But you're wrong. With every click of the encoder, both outputs will change twice:
1: A goes LOW
2: B goes LOW
3: A goes HIGH
4: B goes HIGH

Rotating it anticlockwise reverses the order of events:
1: B goes LOW
2: A goes LOW
3: B goes HIGH
4: A goes HIGH

So, when you detect a falling edge on A, you know the encoder has moved a stop. And when you check the value of B at that moment, you know the direction of rotation: HIGH means a click to the right, LOW a click to the left. It's as simple as that.

PS.: It's working for me, although not using an interrupt, but polling.

Sure, but maybe not all encoders are constructed that way. What you describe is one possible arrangement of an encoder with a detent wheel. Many don't have one at all.

Optical shaft encoders do not have detents, and to obtain the highest angular resolution possible (called 4X mode), one has to detect state changes on both the A and B channels.

Paul Stoffregen's Arduino encoder library is quite flexible and handles all the typical cases, with or without use of interrupts.

PS.: It's working for me, although not using an interrupt, but polling.

That's a good thing.

The interrupt method on one pin in one direction (i.e. FALLING or RISING) is not self correcting for switch bounce.

For example, if the interrupt pin falls, bounces high and falls again triggering another interrupt, the other pin has not changed at all and the resulting increment or decrement will happen twice.

jremington:
Optical shaft encoders do not have detents, and to obtain the highest angular resolution possible (called 4X mode), one has to detect state changes on both the A and B channels.

I didn't know that, thank you. So, my idea only works for the El Cheapo "mechanical" rotary encoders.

cattledog:
The interrupt method on one pin in one direction (i.e. FALLING or RISING) is not self correcting for switch bounce.

So I found out, the hard way, even without interrupts.. :wink:

seems to me that offloading the encoders to something like a LS7366R chip
which is SPI would free up Arduino for the important work(dealing with the encoder data).

TommySr:
seems to me that offloading the encoders to something like a LS7366R chip
which is SPI would free up Arduino for the important work(dealing with the encoder data).

That would be more palatable if the encoders were bundled with one in a module. In principle, I like helper chips but they're harder to integrate into a prototype or one-off than an integrated, all on one PCB device. I have created some real jungles of wires and modules, and now I'm proud when I can do it with only one or two.

they’re harder to integrate into a prototype or one-off

aarg, that’s what I dig about the [b][color=#626262]robogaia 3 axis [/color][/b]encoder shield
it plugs right in to the mega or uno, that and the Crystal is at 27 MHz and the encoder chips could read at maximum freq/4 = 6.75 MHz

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