High Speed Counts from Rotary Encoder

Hi, sorry if this is a dumb question but any help would be much appreciated!

I'm trying to set up a counter to monitor angular motor position from a dc motor's attached rotary encoder. the encoder is 100ppr and connected directly to the motor shaft. I have wired up the encoder and am using the example rotary encoder code from the Arduino playground.

I am getting counts displayed over serial but they seem to be very wrong and im guessing this is because the frequency is too high. roughly it takes 5 seconds to do one rev of the 200:1 output shaft. so 20000 pulses in 5 seconds. so ~ 4kHz....

I don't really know what maximum frequency i can expect from this sort of counting.. Is this kind of frequency just way too fast? and if so is there a way to make it work?

Thanks again for any help, as a mechanical engineer i think there may be a few gaping holes in my electronics knowledge.

Can you post your sketch, and maybe a diagram of your setup?

am getting counts displayed over serial

Depending on how you are counting the pulses then sending a number over the serial connection could have the effect of missing some. Serial data takes a long time to transmit. Have you tried changing the baud rate and seeing if that affects the readings?

4KHz sounds like it should be OK for the arduino, is this an optical rotary encoder? If not then a mechanical one could be struggling at this speed.

1 Like

Are you using the interupt routines to detect the pulses from the encoder? http://www.arduino.cc/playground/Main/RotaryEncoders If so, make sure you do as little as possible in the interupt routine, ie just add or subtract from a counter depending on the direction the shaft is spinning.

Here is the cut and paste code im using:

/* read a rotary encoder with interrupts
   Encoder hooked up with common to GROUND,
   encoder0PinA to pin 2, encoder0PinB to pin 4 (or pin 3 see below)
   it doesn't matter which encoder pin you use for A or B  

   uses Arduino pullups on A & B channel outputs
   turning on the pullups saves having to hook up resistors 
   to the A & B channel outputs 

*/ 

#define encoder0PinA  2
#define encoder0PinB  4

volatile unsigned int encoder0Pos = 0;

void setup() { 


  pinMode(encoder0PinA, INPUT); 
  digitalWrite(encoder0PinA, HIGH);       // turn on pullup resistor
  pinMode(encoder0PinB, INPUT); 
  digitalWrite(encoder0PinB, HIGH);       // turn on pullup resistor

  attachInterrupt(0, doEncoder, CHANGE);  // encoder pin on interrupt 0 - pin 2
  Serial.begin (115200);
  Serial.println("start");                // a personal quirk

} 

void loop(){
// do some stuff here - the joy of interrupts is that they take care of themselves
}


void doEncoder(){
  if (digitalRead(encoder0PinA) == HIGH) {   // found a low-to-high on channel A
    if (digitalRead(encoder0PinB) == LOW) {  // check channel B to see which way
                                             // encoder is turning
      encoder0Pos = encoder0Pos - 1;         // CCW
    } 
    else {
      encoder0Pos = encoder0Pos + 1;         // CW
    }
  }
  else                                        // found a high-to-low on channel A
  { 
    if (digitalRead(encoder0PinB) == LOW) {   // check channel B to see which way
                                              // encoder is turning  
      encoder0Pos = encoder0Pos + 1;          // CW
    } 
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }

  }
  Serial.println (encoder0Pos, DEC);          // debug - remember to comment out
                                              // before final program run
  // you don't want serial slowing down your program if not needed
}

/*  to read the other two transitions - just use another attachInterrupt()
in the setup and duplicate the doEncoder function into say, 
doEncoderA and doEncoderB. 
You also need to move the other encoder wire over to pin 3 (interrupt 1). 
*/

The counts fluctuate up and down when the motor is rotating in one direction. Which seems weird as i would think even if it missed some the count would always increase..

iv tried changing the serial baud but the counts still fluctuate in the same way.

any ideas for a fix or some debugging suggestions would be appreciated.

My personal preference is for no Serial comms in interrupt service routines. Try putting your prints into "loop ()". I'd also be inclined to do just two "digitalRead"s of the two pin states in the ISR also.

Which seems weird as i would think even if it missed some the count would always increase.

No:- if it misses a count or there is other interference on the inputs it miss identifies the direction of rotation resulting in a count that goes up and down.

You did not answer the question about the nature of the rotary encoder. If it is mechanical it is not really suitable for this application.

Oh sorry, its an optical encoder.

May not help, but try simpler code?

void doEncoder(){ if (digitalRead(encoder0PinA)=(digitalRead(encoder0PinB)) {

encoder0Pos++ } else { encoder0Pos-- } }

if (digitalRead(encoder0PinA)==(digitalRead(encoder0PinB)) {

If you want it to compile ;)

Thanks!!! :-)

I havn't thought this through completely so im not sure if the position would drift after time, but if you are driving the motor with the arduino then you would already know the direction the shaft is spinning, therefore you could just add or subtract the count accordingly. The less you put in the interupt routine the better.

void doEncoder() { if (flag == 1) // flag is either 0 or 1 depending on the direction you set the motor count++;

else count--; }

this might not actually make sense though.... its been a long day.

Correct me if im wrong but glt's code:

void doEncoder(){
 if (digitalRead(encoder0PinA)=(digitalRead(encoder0PinB)) {
                               
     encoder0Pos++
   }
   else {
     encoder0Pos--
   }
 }

will make a count every time the code loops. Really you only want to count when there is a step from the encoder. So I assume the rate of encoder pulses needs to be much less then the frequency the code loops around at, or am i missing something...

Maybe the arduino just isn't fast enough :'(

will make a count every time the code loops.

That code is a replacement for the ISR it is not in the main loop. Remember it should be == not just = in the if()

Referring to your original code the section and assuming you have removed the print statement from the ISR and put it in the loop.

Then :- if (digitalRead(encoder0PinA) == HIGH) { // found a low-to-high on channel A if (digitalRead(encoder0PinB) == LOW) { // check channel B to see which way

You get to the first if() statement on the edge but the second if() occurs some time after. That is the reads are not being done at the same time. As it is an optical encoder then I would not expect the signals to be too noisy but it seems that noise is getting in. It would be good if you could look at these signals with an oscilloscope to confirm that.

However, try an use an external pull up resistor of about 1K to give you a bit more noise immunity on the lines. Also try a bit of extra decoupling as you might be getting interference from your motor. Maybe a 100pF to ground on each of your quadrature signals might help as well.