Hello, I have been experimenting with a 200 PPR encoder and I am trying to measure accurate distances with it at high speed.
The problem that I am running into is that when the shaft turns at high speed in forward or reverse, I begin to get a negative pulse count.
Here is the exact circuit that I am using-- pull down resistors do not work:
I am using an Official Arduino Mega running this code:
#include "Arduino.h"
#include <digitalWriteFast.h> // library for high performance reads and writes by jrraines
// see http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1267553811/0
// and http://code.google.com/p/digitalwritefast/
// It turns out that the regular digitalRead() calls are too slow and bring the arduino down when
// I use them in the interrupt routines while the motor runs at full speed creating more than
// 40000 encoder ticks per second per motor.
// Quadrature
//encoder
#define c_EncoderInterrupt 0
#define d_EncoderInterrupt 1
#define c_EncoderPinA 2
#define c_EncoderPinB 3
#define EncoderIsReversed
volatile bool _EncoderBSet;
volatile long _EncoderTicks = 0;
void setup()
{
Serial.begin(115200);
// Quadrature encoder
pinMode(c_EncoderPinA, INPUT); // sets pin A as input
digitalWrite(c_EncoderPinA, LOW); // turn on pullup resistors
pinMode(c_EncoderPinB, INPUT); // sets pin B as input
digitalWrite(c_EncoderPinB, LOW); // turn on pullup resistors
attachInterrupt(c_EncoderInterrupt, HandleMotorInterruptAfirst, RISING);
}
void loop()
{
Serial.println(_EncoderTicks);
delay(20);
}
void HandleMotorInterruptAfirst()
{
// Test transition; since the interrupt will only fire on ‘rising’ we don’t need to read pin A
_EncoderBSet = digitalReadFast(c_EncoderPinB); // read the input pin
// and adjust counter + if A leads B
#ifdef EncoderIsReversed
_EncoderTicks -= _EncoderBSet ? -1 : +1;
#else
_EncoderTicks += _EncoderBSet ? -1 : +1;
#endif
}
Here the author of the code says that he is measuring "40000 encoder ticks per second" with his setup. Why am I not seeing this sort of performance- bad Omron encoder?
How fast is "fast"? 200PPR encoder would hit 40000 ticks per second at around 12000RPM.
You could try reducing the capacitor value-by my rusty RC maths the time constant for your circuit may be limiting it to around 10000 ticks per second depending on the output impedance of the sensor itself.
There is also the possibility that the output pulse is so short that you are missing it even within the interrupt routine. You may wish to investigate the low-level port manipulation functions (Arduino Reference - Arduino Reference) that allow you to read both pins simultaneously.
The specifications for encoders can be ambiguous. Sometimes 200 PPR refers to all four quadrature states available, and as @rw950431 says, that would be 40,000 counts per second at 12000 rpm. That is 200 counts x 200 rps. Sometimes 200 PPR refers just to the basic pattern of one channel without reference to the quadrature and there will be 4 x 200 pulses seen per revolution.
The first thing I notice is that your encoder algorithm is only reading only one of the four quadrature states available. It triggers only on Channel A RISING and then determines direction by looking at Channel B. This is not a bad scheme with high resolution encoders and helps them to be read faster. Other algorithms will look at ChannelA CHANGE, or both A and B CHANGE. You can read 1,2,or 4 of the quadrature pulses available from the encoder. Because the number of pulses read depends on the algorithm, that why sometimes the PPR specification is referenced to the fundamental pattern of one channel.
I have tested your code with a quadrature generator (encoder emulator) which I use, and it was accurately reading 50K counts per second. This was reading only 1 of 4 pulses from 200K available to be read.
I believe that the main reason you are not reading higher counts and are getting negative counts is due to your hardware. The 10K pull ups look good, but the other RC components in the output legs look like hardware debounce circuitry. I do not believe that a 200PPR encoder will require debounce as it will be optical or magnetic. The debounce components are killing your signal.
Eliminate the cap and the resistor on the output lead, and I believe that you will read significantly faster. I was seeing 1000 counts for the 20 ms read period of your code.
The previous circuit showed 10K pull up resistors to 5v.
Yes, I thought that I should be connecting one end of the 10k resistor to ground and the other to the signal pin; now I have the 10k going from 5v to the signal pin and it works very well!
Thankyou so much cattledog and rw950431 for your expert help!
Now I can accurately read the encoder at speeds past 25 MPH easily with a 3.1" wheel!
One final refinement. Because the variable _EncoderTicks is more than 1 byte, it can change when it is being read from the ISR if an interrupt occurs while it is being read. The standard proceedure is to "protect" the value. Disable interrupts, read the variable, and then reenable the interrupts. See Nick Gammon's excellent tutorial on interrupts which addresses this point. Gammon Forum : Electronics : Microprocessors : Interrupts
]
#include "Arduino.h"
#include <digitalWriteFast.h> // library for high performance reads and writes by jrraines
// see http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1267553811/0
// and http://code.google.com/p/digitalwritefast/
// It turns out that the regular digitalRead() calls are too slow and bring the arduino down when
// I use them in the interrupt routines while the motor runs at full speed creating more than
// 40000 encoder ticks per second per motor.
// Quadrature
//encoder
#define c_EncoderInterrupt 0
#define d_EncoderInterrupt 1
#define c_EncoderPinA 2
#define c_EncoderPinB 3
#define EncoderIsReversed
volatile bool _EncoderBSet;
volatile long _EncoderTicks = 0;
long copy_EncoderTicks = 0; //new variable for protected copy
void setup()
{
Serial.begin(115200);
// Quadrature encoder
pinMode(c_EncoderPinA, INPUT); // sets pin A as input
digitalWrite(c_EncoderPinA, LOW); // turn on pullup resistors
pinMode(c_EncoderPinB, INPUT); // sets pin B as input
digitalWrite(c_EncoderPinB, LOW); // turn on pullup resistors
attachInterrupt(c_EncoderInterrupt, HandleMotorInterruptAfirst, RISING);
}
void loop()
{
noInterrupts();
copy_EncoderTicks = _EncoderTicks;
interrupts();
//do whatever with the transferred variable
Serial.println(copy_EncoderTicks);
delay(20);
}
void HandleMotorInterruptAfirst()
{
// Test transition; since the interrupt will only fire on 'rising' we don't need to read pin A
_EncoderBSet = digitalReadFast(c_EncoderPinB); // read the input pin
// and adjust counter + if A leads B
#ifdef EncoderIsReversed
_EncoderTicks -= _EncoderBSet ? -1 : +1;
#else
_EncoderTicks += _EncoderBSet ? -1 : +1;
#endif
}
Today I connected the encoder up to a Kollmorgen servo motor that has a 3,000 PPR encoder in it and my encoder is basically right on with an Arduino Mega compatible. When I ran the same test with the official Mega, the results were off by 4 pulse counts whereas the compatible only was off by 2 pulses- I wonder if the knockoff has a slightly faster crystal!