Encoder

Hello,

I bought a motor with encoder attach to it. Pololu - 131:1 Metal Gearmotor 37Dx57L mm 12V with 64 CPR Encoder (No End Cap)

I am trying to get the encoder to work with arduino

I am using this code

int val; 
 int encoder0PinA = 3;
 int encoder0PinB = 4;
 int encoder0Pos = 0;
 int encoder0PinALast = LOW;
 int n = LOW;

 void setup() { 
   pinMode (encoder0PinA,INPUT);
   pinMode (encoder0PinB,INPUT);
   Serial.begin (9600);
 } 

 void loop() { 
   n = digitalRead(encoder0PinA);
   if ((encoder0PinALast == LOW) && (n == HIGH)) {
     if (digitalRead(encoder0PinB) == LOW) {
       encoder0Pos--;
     } else {
       encoder0Pos++;
     }
     Serial.print (encoder0Pos);
     Serial.print ("/");
   } 
   encoder0PinALast = n;
 }

The problem i am facing the faster the motor turns the lower counts I get for a turn.

For example slow turn ~ 2100 counts
Fastest turn around ~ 300 counts

I want to relate the count to the rotation in angle.

Any help how to fix this ?

I am using this code

from where ?

At low speed, things look correct. Your encoder reading code is looking for a rising change on one of two pins, and will read 1 count for every four encoder states. At 64 available counts per motor revolution you will show 16 per motor revolution. Your motor is geared 131:1 so you should see 2096 counts per revolution through the gear box. Close enough to 2100.

At higher speeds you appear to be missing counts. I am not sure if the counts should continue to read 2100 even at higher speeds, or if they would go down based on the code you have.

What arduino are you using?

What output rpm is the slow turn and what is the fast turn?

Are you showing all your code?

encoder0Pos should be declared as a long, since you can easily overflow the int.

What do you see with Serial.begin(115200), or better yet, pull the serial display from the counting loop, run the motor for a fixed period of time, and then read/display encoder0Pos. The serial output is blocking.

The encoder routine can be made faster with alternatives to digitalRead and switching to an interrupt based procedure, but before you head down that path, lets make sure that missing counts explain the lower counts at higher speeds.

You are missing encoder counts, because Serial.print takes time. Even at printing 115200 baud, you may miss counts. As already suggested, take Serial.print out of the loop.

The OPs code is from the Playground:
http://playground.arduino.cc/Main/RotaryEncoders

Hello again

I am using arduino uno

I didn't try interrupt yet

I have 4 encoders and i want to get reading from them.

What is the best way to do it ? Interrupt? digital read? Any idea where to look at would be great?

Use pin change interrupts. That will take 8 pins, but get one encoder working first.

Hello again,

I manage to use interrupt + digitalreadfast and i am reading 2 encoders and it is working great so far.

But arduino uno has only two interrupts.

I try use your suggestion about changing other pin to interrupt and test it out.

This is my way of reading an encoder in a effective way:

//needed declarations
const char encTable[16] ={0, 1, -1, -0, -1, 0, -0, 1, 1, -0, 0, -1, -0, -1, 1, 0};//gives -1, 0 or 1 depending on encoder movement
byte encState;//remembering the encoder output and acting as index for encTable[]
long actPos;//actual position from encoder

//code in a TIGHT loop (or you will miss pulses)
encState = ((encState<<2)|(digitalRead(encPinB)<<1|digitalRead(encPinA))&15;//use encoder bits and last state to form index
actPos += encTable[encState];//update actual position on encoder movement

Be aware the if the encoder moves more than one edge between two adjacent read you will miss pulses.

There is a relatively simple way for you to add two more encoders with two pin change interrupts. You can leave the existing external interrupts (pin2, pin 3) for two encoders.

You have not posted any interrupt code, but with your two existing external interrupts you want to have them configured for CHANGE, so they will be the same as the new ones you add. You may have already done that, but your initial code was based on RISING. You will get twice the number of counts per rotation as before, and you want to be sure that you are reliably reading them with your setup. If you actually need the full encoder resolution and interrupts on both pins of each encoder, the code will get much more complex, but given your gear ratio and speeds, I believe you will be OK with the half resolution.

A good ISR to use with these interrupts is

if (digitalRead(encoderPinA) == digitalRead(encoderPinB)) {
    encoderValue++; 
  } else {
    encoderValue--;
  }

You say that you are using digitalReadFast, but if you don't want to use that library, its very simple to change from digitalRead to use bitRead(PINX , number) which is a fast direct reading of the Port without using bitshifts and masks. I'll use that form in my post, but you can always use the digitalReadFast if you prefer.

One of the things which will simplify the pin change interrupts, is that we will add them to pins on different Ports, so you will not have to figure out what pin changed. You should familiarize yourself with the architecture of the AT 328 so you understand about pins, ports and registers. The ATmega328P data sheet is good to have at hand, and a good review can be found at RCArduino: RC Arduino Traction Control - The Traction Alarm - Draft

Two choices for your added interrupts are digital pin8 (PORTB, 0) and analog pin A0 (PORTC, 0). You will declare the pins as input or input pullup as standard. It's easier to keep things straight if you put the non interrupt pin of the encoder on an adjacent pin, e.g. Pin 9 and Pin A1.

The process to add the interrupts is simple. You enable the interrupts on a Port with the Pin Change Interrupt Control Register (PCICR) by setting the correct PCIE (pin change interrupt enable) bit in the register. Then you enable each pin you want on the port with the Pin Change Mask Resister(PCMSK). You do this in setup.
Here's the syntax

//Enable Pin Change Interrupt on Pin 8
PCICR |= (1<<PCIE0); //enable group interrupts on PORTB PCINT[7:0]
 PCMSK0 |= (1<<PCINT0); //enable interrupt pin 8
//Enable Pin Change Interrupt Pin A0
PCICR |= (1<<PCIE1); //enable group interrupts on PORTC PCINT[13:8]
 PCMSK1 |= (1<<PCINT8); //enable interrupt pin A0

There are now two ISR's to define for what happens when these interrupts occur. ISR's are called PCINT#_vect

//ISR for Pin 8 
ISR (PCINT0_vect)    // handle pin change interrupt for D8-D13;  triggered by 8
{
 if(bitRead(PINB,0)== bitRead(PINB,1)) {//(PINB, x) is the syntax to read the input state from PORTB, bit x 
    encoderValue++;
  } else {
    encoderValue--;
  } 
 }
//ISR for Pin A0
ISR (PCINT1_vect)   // handle pin change interrupt for A0-A5; triggered by A0
{
 if(bitRead(PINC, 0)== bitRead(PINC, 1)) {
    encoderValue++;
  } else {
    encoderValue--;
  } 
 }

Here is a sketch which uses Pin Change Interrupts. I am using only one encoder for testing.

//Pin change interrupts on one pin of encoder pins A and B
//reads only half of quadrature states available
//for multiple encoders define encoder0PinA, encoder0Value, etc.

//PORTB pins; 
#define encoderPinA 8 //PB0
#define encoderPinB 9 //PB1

//PORTC pins; 
//#define encoderPinA A0 //PC0
//#define encoderPinB A1 //PC1

#define buttonPin  5 //reset button for encoder counts

//variables changed within interrupt make volatile
volatile long encoderValue;

long lastEncoderValue = 0; 

void setup() { 

  Serial.begin (115200);
  Serial.println("Half of Quadrature Counts");

  pinMode(encoderPinA, INPUT_PULLUP); 
  pinMode(encoderPinB, INPUT_PULLUP);
  pinMode(buttonPin, INPUT_PULLUP);


  //enable pin change interrupts and set pin masks; 
  //interrupt pin selection or wires from encoder can be switched for correct cw/ccw

  PCICR |= (1<<PCIE0);//enable group interrupts on PORTB PCINT[7:0]
  PCMSK0 |= (1<<PCINT0);//enable interrupt pin 8
  //PCMSK0 |= (1<<PCINT1);//enable interrupt pin 9

  //PCICR |= (1 << PCIE1);//enable group interrupts on PORTC PCINT[8-13]
  //PCMSK1 |= (1<<PCINT8);//enable interrupt pin A0
  //PCMSK1 |= (1<<PCINT9);//enable interrupt pin A1
}

void loop() {

 //With fast counts, you may want to change this routine  to only print increments of n counts,  or counts per second, etc

  if(encoderValue != lastEncoderValue){
    Serial.print("Count:  ");
    Serial.print(encoderValue);
    Serial.println();

    lastEncoderValue=encoderValue;
  }

  //reset index
  if(digitalRead(buttonPin)==LOW){
    encoderValue=0;
  }

}

ISR (PCINT0_vect)//handle pin change interrupt for d8 to D13, triggered by 8
{ 
  if( bitRead(PINB,0)== bitRead(PINB,1)) //compare Pin 8 and 9
  {
    encoderValue++;
  } 
  else 
  {
    encoderValue--;
  } 
}

/*
ISR (PCINT1_vect)// handles interrupts A0 to A5, triggered by A0
 { 
 if( bitRead(PINC,0)== bitRead(PINC,1)) //compare Pin A0 and A1
 {
 encoderValue++;
 } 
 else 
 {
 encoderValue--;
 } 
 }
/*
 
/*
 ISR (PCINT2_vect) // handle pin change interrupt for D0 to D7 
 { // handle pin change interrupt for D0 to D7 
 if( bitRead(PIND,#)== bitRead(PIND,#)) //compare Pins
 encoderValue++;
 } else {
 encoderValue--;
 }
 }
 */

Thanks again for the replies,

I moved to arduino mega since it has 6 interrupt pins, thanks again for sharing the idea on how to do it with pin interrupt change.

This is my loop() part

void loop()
{

  Serial.print(_FirstEncoderTicks);
  Serial.print("\t");
  Serial.print(_SecondEncoderTicks);
  Serial.print("\t");
  Serial.print(_ThirdEncoderTicks);
  Serial.print("\t");
  Serial.print(_FourthEncoderTicks);
  Serial.print("\n");
 
  delay(20);
}

I am trying to get the actual time each data was gathered.

Can I say first data at 0ms ,second data at 20ms, third data at 40ms ?

Or is there a way to relate time into Serial reading ?

Any help would be great.

You can do that by setting a flag in the ISR. In the main loop you check that flag, print out the information and clear the flag(don't forget)

What you mean by flag?

 Serial.print("Time: ");
  time = millis();
  //prints time since program started
  Serial.println(time);

Do you think this will work to keep track of time ?

A flag is a variable that signals that something has happened.
Declare it globally: int printFlag;
Set it inside the ISR :printFlag = 1;
Check it inside your loop:

if(printflag){
  Serial.println(millis());
  printFlag = 0;
}//if

The concept can be expanded

Global flags used in interrupts generally have to be declared "volatile", or they get ignored by the compiler, e.g.

volatile int printFlag;

Hello All,
I would like to read the full resolution of an encoder - it is a 32 CPR encoder, and using the pin change interrupt does not give me enough resolution - i.e. 16 counts per rev (gearbox of 19.2:1). I would like
to detect the full 32 counts per rev. Would I just read the pins for Chan A and Chan B, and detect
both high and low states? Is this possible with an Arduino Uno?

I am currently using the ISR in example 9 above from Cattledog -
ISR (PCINT0_vect) // handle pin change interrupt for D8-D13; triggered by arduino pin 13
{
if(bitRead(PINB,5)== bitRead(PINB,3)) {
encoderValue++;
} else {
encoderValue--;
}
}

Any input will be of great value.
thanks,
ewholz

You have to enable pin change interrupts for both pins, in order to get full possible resolution. Then add more logic to detect all possible states.

ewholz

You will need to set the pinchange interrupts for both pins. You can then use the approach that Nilton61 posted in repy 8. Use the bitRead instead of the digitalRead. Remember to declare variables as volatile when they are changed in the isr.

Nilton's method of using the past and present states of the two pins is the logic jremington refers to.

Hello Jim R and/or Cattledog,
Thanks for the clarification for counting the full resolution above.
I have a question on pin change interrupts. I am using the sample interrupt you show above
for pins 13 and 11 on an Arduino Uno (chan B on 13, chan A on 11). One motor I have (pololu)
has a gear ratio of 131 with a (supposedly) 64 CPR encoder. I am a little confused about the
decoding scheme - I will guess since the pin change checks when both pins are at a high level,
I am only counting half the value of the CPR? This seems to be the case for this motor, as 131*32=4192, which is approx 1 rev of the gearbox side of the motor. This encoder seems to be a little sloppy, the the counts vary, by 30 to 50 counts.

I have another motor which has a much more sophisticated encoder (which also has an index pulse).
It has a gear ratio of 19.2 to 1, and a 32 CPR encoder. So if I use 1619.2=307, and reset encoder counts for this motor when I hit 307 - it turns only 1/4 turn!, if I set count to 1228 - it gives 1 turn, with a count variance of only 1 count. No problem for this application (neither is 30 or 50 off, etc).
I can change the value to 64
19.2=1229 which gives one turn. Am I missing counts here?
the main difference seems to be the total counts for each motor- i.e. pololu 64 CPR, and 32*131 gives 4192 which gives me one turn.

But the better motor at 32 CPR give only 1/2 turn using the same formula 16*19.2 = motor turns only 1/2 turn.

I am using this formula to derive the Input for PID controller. (the library (brett beauregard's) :
speed_act = ((encoderCount - countAnt)(60(1000/LOOPTIME)))/(32*131);

LOOPTIME is 100, countAnt holds the last count, and the rest of the equation is pseudo-rpm.
for example if my Setpoint is 20, the Input goes to 20 and the output drives the motor fine.

Your posts are great resources -
Thanks,
ewholz

ewholz

I think that there is some confusion in the vendor's descriptions between encoder descriptions of PPR (pulses per revolution) and CPR (counts per revolution). Given the 90 degree offset between the A and B pulses, there are four states(counts) available per pulse set. There are routines available for reading 1,2 or 4 of these changes. You are wise to test your encoders and determine what is actually referred to by the description.

Pololo seems like a highly reliable supplier, and their encoder with 64 CPR (16 PPR), fits the standard nomenclature. You are reading only half the available counts with the ISR in my old post. The +/- 30 to 50 counts seems high but since that is only a few degrees of rotation, and I don't know how well you can determine exactly 1 complete rotation.

The second encoder, by observation, could be described as a 128 CPR (32 PPR) model. I don't know what the spec sheet really shows but it sounds like the 32 refers to pulses and not counts.

I think that most encoders are usually specified by pulses per revolution (PPR) because the counts per revolution will be a factor of how the encoder is read.

Did you revise your program to put interrupts on both pins, use Nilton61's way of reading the pin states when the interrupts are triggered, and move your count up to 4 per pulse? It sounds like you may not need the increased resolution.