Rotary ENCODER inconsistent direction readings [SOLVED]

While new on this forum I thought I'd share my findings on manual operating digital rotary encoders I used (Quadrature Encoders). I hope it clearifies the pitffall I ran into a bit better and helps someone with there project.
While building an X/Y axis manipulator I encountered "false" readings on my two Encoders running code on a Mega board. After searching the forums and the internet I couldn't find a fix for my problem. When I use the standard code provided on this forum (or the internet) I still couldn't get my readings to work flawlessly. Th ecode would give my switching directions while turning in one direction . See code below:

 aState = digitalRead(outputA); // Reads the "current" state of the outputA
   // If the previous and the current state of the outputA are different, that means a Pulse has occured
   if (aState != aLastState){     
     // If the outputB state is different to the outputA state, that means the encoder is rotating clockwise
     if (digitalRead(outputB) != aState) { 
       counter ++;
     } else {
       counter --;
     }
     Serial.print("Position: ");
     Serial.println(counter);
   } 
   aLastState = aState; // Updates the previous state of the outputA with the current state

When I turn the knob on the encoder slowly, my readings are just fine. Both Channel A & B were giving (reasonable) good pulses. But when I turned the knob at a higher speed I get really strange readings. The counter would increase or decrease randomly. This pointed me in the right direction. If the speed of the knob is influencing the readings then there has to be a relation with the code, or in better words the processing speed of the code and the state of the encoder. Then it struck me.... The code above 'assumes' that the encoder state is read, always, in the first part of the square wave (A-Channel signal) generated by the encoder. Being state "10" on CW turning on Channel A in the top half of the picture or state "11" at CCW on Channel A. see picture below.

But.... Since the processor has nothing else to do (in my code) but polling the two channels of the encoder it will read the states very quickly. Occasionaly it will read the state at the end of the square wave. An theirin I found my solution. A "false" reading will occur in that specific situation.. If you look at the fifth dotted line in the top half of the picture (CW-state "11") when Channel A is HIGH, Channel B is also HIGH. This is the exact same state as the the first dotted line in the bottom half (CCW state "11" ) of the picture.
So there is no way of telling if I turned CW or CCW according the state of the channels which is porcessed in the code above!

This means I have to isolate the specific states of the square waves/Channels. By determening the RISING or FALLING state of Channel A, I can isolate those states.
In the code snipped below (This is not the full code but a part of my solution) I first read the Two INPUTS as quickly as possible. This gives me a good snapshot of the states of both Channels at the same time. Then I look at the RISING or FALLING state of Channel A and it's combination. The combination can be read by looking at the picture above. The different states can be found in the above pciture (e.g. CW: 01", "00","10", etc.). So, the RISING or FALLING state of Channel A excludes "false" readings. The "01" or "00" states are equal to the HIGH or LOW readings of the stae sin my code.

// First read a quick as possible BOTH states to "snap" the current encoder states! It is mandatory to capture both encoder channels at "the same time" in orde to use the code below.
  Manual_ENCODER_ROTATION_Current_State_A = analogRead(REMOTE_Rotation_CALIBRATION_MANUAL_ENCODER_A_Pin); // Reads the "current" state of the outputA
  Manual_ENCODER_ROTATION_Current_State_B = analogRead(REMOTE_Rotation_CALIBRATION_MANUAL_ENCODER_B_Pin); // Reads the "current" state of the outputB

// Due to the use of analog inputs and for easier use of the states later, transform the analog states to digital ones.
  if (Manual_ENCODER_ROTATION_Current_State_A > AnalogEncoderTreshold) { Manual_ENCODER_ROTATION_Current_State_A = HIGH; } else { Manual_ENCODER_ROTATION_Current_State_A = LOW; }  
  if (Manual_ENCODER_ROTATION_Current_State_B > AnalogEncoderTreshold) { Manual_ENCODER_ROTATION_Current_State_B = HIGH; } else { Manual_ENCODER_ROTATION_Current_State_B = LOW; }  

  // Only if the A-Channel state changes, react to a change.
  if (Manual_ENCODER_ROTATION_Current_State_A != Manual_ENCODER_ROTATION_Last_State_A) { 
    if ((Manual_ENCODER_ROTATION_Current_State_A == HIGH) && (Manual_ENCODER_ROTATION_Current_State_B==LOW)) {
      Manual_ENCODER_ROTATION_DIR = ToRight;
    }
    if ((Manual_ENCODER_ROTATION_Current_State_A == LOW) && (Manual_ENCODER_ROTATION_Current_State_B == LOW)) {
      Manual_ENCODER_ROTATION_DIR = ToLeft;
    }
  }

Finally I have a rock solid reading of my encoders. Slow turning, high speed turning. It doesn't matter. It will read flawlessly.

I hope I was able to cleaifiy someting that is not easy to clearify on paper but if you have comments, better code practises, questions, etc. , please comment...

Best Regards, Marcel.

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