Arduino encoder reading

I'm thinking about what's the better way to read a pulse from an encoder and at the same time obtain a dc motor control signals. I did'nt practice PULSEIN and Interupt too much anyway. Any help? I've a single-output Hall rotary encoder connected to a dc brush motor, l293 driven.

thanks

sUper16

Hi, I to have been looking into reading an incremental optical encoder. So far, just googling around, I have found that these encoders can have different types of wheels built into them - But most follow these basic configurations: They have two tracks of patterns (90 degrees out of phase to each other when read by the optical sensor) providing a series of pulses to 2 channels (usually called Channel A and Channel B leading out of the encoder). This encoding is referred to as Quadrature. By reading the values from each channel and then comparing which came first (Channel A was High before Channel B for instance) it is possible to determine direction. Speed could be calculated by measuring how many pulses existed between a set time period. I have a HP 9873 encoder attached to a small DC motor from an old printer. This particular encoder has no data sheet available so I had to do a bit of mucking around to find out what the pins where. If anyone has this particular encoder the pin-out is:

Pin1: GND
Pin2: Channel A
Pin3: +5 Volts
Pin4: Channel B

This type of encoder has only 2 Channels. However some have effectively another channel that is sometimes referred to as a Reference Channel. This provides a "Home" position for the encoder. I believe the HEDS style of encoders from HP/Agilent might have this feature.

Attaching the Encoder to my Arduino was simple. I have connected the +5 Volt line via a 220R resistor to protect the Encoder circuitry and haven't bothered with any pull-up resistors on any of the channels. The Voltage drops to around 3V but seems to function properly. Inside Arduino, I have tried to use the example code from the Playground and have found this to be a good starting point but not suitable. The encoder wheel simply spins to fast and sends too many pulses for this method to work. So I have tried to use interrupts as this code demonstrates. It works a little better, however my terminal view appears to be missing steps in the encoder. I think the serial connection probably misses them but in reality the steps were probably counted. I have no way of checking this at the moment. The motor appears to be moving and stopping in the right places but I do suspect some sort of cumulative error. Hope this helps!

int encoder0PinA = 2;
int encoder0PinB = 3;
volatile int encoder0Pos = 0;
volatile int encoder0PinALast = LOW;
volatile int n = LOW;
int valNew = 0;
int valOld = 0;
volatile int m = LOW;

void setup()
{
  pinMode (encoder0PinA,INPUT); 
  pinMode (encoder0PinB,INPUT);
  Serial.begin (9600);
  attachInterrupt(1, CountA, CHANGE);
  attachInterrupt(0, StateB, FALLING);
}

void loop()
{
  encoder0PinALast = n;
  valNew = encoder0Pos;
  if (valNew != valOld) {
    Serial.print (encoder0Pos, DEC); 
    //Serial.print (m);
    Serial.print ("/");
    valOld = valNew;
  }

}

void CountA()
{
  n = digitalRead(encoder0PinA); 
  if ((encoder0PinALast == LOW) && (n == HIGH)) { 
    if (m == LOW) { 
      encoder0Pos--; 
    } 
    else { 
      encoder0Pos++; 
    } 
  }
}
void StateB()
{
  m = digitalRead(encoder0PinB);
}

PART 1 of 2 posts
/* ---------------------------------------
This sketch reads a rotary encoder with interrupts and uses the index track to always
recalibrate the encoder every turn. It solves problems with high resolution
encoders or high speed applications.
Tested 1000 turns @ 3000 RPM with a 500cpr encoder and it counted perfect.
Encoder hooked up with common to GROUND, encoder0PinA (or encoder0PinB) to pin 3,
encoder0PinC (Index Track) to pin 2.
Most of the code here was originally coppied from the arduino playground
(by max wolf and Paul Badger)
and then modified to include a way of using the index track to recalibrate for missed
encoder counts at high speeds or at high resolutions. It uses both interupts on the arduino.
Hope this helps,
-MattB

PROBLEMS - for some reason it misses one tenth of a turn when the counter goes from 0.0 to -0.1.
Maybe someone could fix this?? it is not a huge problem.
Also, I'm not a programmer, if anyone sees ways I can clean up this code please let me know.
--------------------------------------- */
#include <avr/interrupt.h>

#define encoder0PinA 3 //Quadrature Track A
#define encoder0PinB 4 //Quadrature Track B
#define encoder0PinC 2 //Index Track

//encoder variables
volatile unsigned int encoder0Pos = 0; //the encoder position variable.
int start = true; //variable used to indicate the first time the encoder sees the index track.
int turn = 0; //the total turns.
int decimal = 0; //tenths of a turn.
int encoderHome = 0; //used to reset encoder0Pos if any counts are missed during a rotation.
int ccw = false; //which direction the encoder is spinning.
int display = 0; //variable for display data on LCD screen.

void setup(){

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

attachInterrupt(1, doEncoder, CHANGE); // encoder track A on interrupt 1 - pin 3
attachInterrupt(0, doIndex, RISING); // encoder Index track on interupt 0 - pin 2

beginSerial(19200); //start communications with LCD screen Matrix Orbital LCD0821
Serial.print(254, BYTE); //turn off the cursor on LCD
Serial.print(84, BYTE); //turn off the cursor on LCD
Serial.print(254, BYTE); // turn on the LCD backlight //remove any of these if needed
Serial.print(66, BYTE); // turn on the LCD backlight
Serial.print(0); // turn on the LCD backlight
}

void loop(){
Serial.print(12, BYTE); //clears the LCD screen.
Serial.println ("Turns ="); //print "Turns =" on the LCD screen.
decimal = (encoder0Pos / 50); //500 encoder counts per rev divided by 50 is tenths.
if (turn <= -1){ //if turns goes into negative numbers do some stuff to show negative on the LCD.
display = (abs(turn + 1));
decimal = (9 - decimal); //tenths now counts in the reverse direction when turns is negative.
Serial.print ("-"); //print a negative sign when turns is negative
} else {
display = turn; //if turns is not negative then the display equals the turns
}
Serial.print (display); //show turns on LCD
Serial.print ("."); //print a decimal point after the turns
Serial.print (decimal); //print the tenths of a turn after the decimal point.
delay(20); //delay 20ms so that the LCD screen doesnt flood
}

void doIndex(){ //every time the index track comes around doIndex will run.
if (start == true){ //if this is the first time the index track has come around
encoderHome = encoder0Pos; //the encoder Home position will equal the current encoder position.
start = false; //tell the arduino that start is over
} else {
encoder0Pos = encoderHome; } //if this is not the first time index has come around reset the encoder position
} //so that if any counts are missed because of high speed or too high of resolution
//they do not mess up the total turns.

CONTINUED ON NEXT POST

PART 2 of 2 (continued from previous post)

void doEncoder(){ //every time a change happens on encoder pin A doEncoder will run.

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 spinning
ccw = true; // CCW
} else {
ccw = false; // CW
}
} else { //found a high-to-low on channel A
if (digitalRead(encoder0PinB) == LOW) { // check channel B to see which way encoder is spinning
ccw = false; // CW
} else {
ccw = true; // CCW
}
}
if (ccw == true){
encoder0Pos = (encoder0Pos - 1); //if encoder is spinning CCW subtract a count from encoder position
if (encoder0Pos == -1){ // if encoder position is equal to -1
encoder0Pos = 499; //make encoder position equal to 499. (500 count encoder, change if needed)
turn = (turn - 1); } //if concoder position is changed to 499 subtract 1 from turns.
//this keeps rolling over the encoder positions so that it never
//gets below 0 counts
} else {
encoder0Pos = (encoder0Pos + 1); //if encoder is spinning CW add a count to encoder position
if (encoder0Pos == 500){ // if encoder position is equal to 500.(500 count encoder, change if needed)
encoder0Pos = 0; //make encoder position equal to 0.
turn = (turn + 1); } //if concoder position is changed to 0 then add 1 to turns.
} //this keeps rolling over the encoder positions so that it never
} //gets over 499 counts

//reset the counter (display) with start=true, encoder0Pos=0, and turns=0

i have problem with my, A and B that flip over in very fast application.

and the value is miss.

More info would be nice. Are you using the interupts? if you don't have an index track you could attach the second interupt to the b track.
-Matt

Yes i using interrupt and it still fail for very fast counting. also i don't have Z

how fast is fast?
I was using a 500 count encoder @ 3000 rpms
it worked for me. What is the rated speed of the encoder? Could the encoder be malfunctioning? Is it an optical encoder or something else? No one can answer you question unless you post a detailed description of your problem, including how things are wired to your arduino.

are you using pull down resistors so that the pins don't float high between counts?

-Matt

forget the pull down resistor thing.

I am trying to use this sketch to connect an Accucoder to a SerIO.

Com Black F
+VDC Red D
A White A
A' Brown H
B Blue B
B' Violet E
Z Orange C
Z' Yellow J
Case Green G
Shield Bare -

Does anyone know where I would connect the Com wire?

I assume +VDC goes to one of the 5V plugs

I wonder what I should do with A' B' and Z'

Any clues woud be much appriciated.

The data sheet for the encoder i am tryong to use is at the link below.

http://www.encoder.com/literature/datasheet-702-shaft.pdf

I am assuming that once I connect this with the SerIO and run sthe sketch that I should se a response in the Serial Monitor window is that right?

I am trying to use this sketch to connect an Accucoder to a SerIO.

Com Black F
+VDC Red D
A White A
A' Brown H
B Blue B
B' Violet E
Z Orange C
Z' Yellow J
Case Green G
Shield Bare -

Does anyone know where I would connect the Com wire?

I assume +VDC goes to one of the 5V plugs

I wonder what I should do with A' B' and Z'

Any clues woud be much appriciated.

It's good that you posted a link to the encoder. However if you look at the datasheet there are many optional encoder output types. We would need the full part number of your specific encoder to see which options dictate how the outputs should be wired.

You state: " I am trying to use this sketch to connect an Accucoder to a SerIO."

What is a SerIO? If you mean the Arduino serial data pins 0 & 1, there would be no direct connection wired between the encoder outputs and the arduino serial pins, that would be a software function that you would have to include in your sketch.

Lefty

The name of the Arduino product is SerIO.

I got it working late last night.
I tried about 8 different code examples, finally this one posted below worked best:
The only thing I am not too happy with is the encoder has 3600 steps per revolution, if I turn it more than about 2 RPM it misses counts, I can live with that if I have to.

Now I am working to figure out a way to reset the value of the variable "rotor' to zero.

byte A = 2; // One quadrature pin
byte B = 3; // the other quadrature pin
volatile int Rotor = 0;

void setup() {

      // set DIO pins
      pinMode(A, INPUT);
      pinMode(B, INPUT);

      // Attach interrupt to pin A
      attachInterrupt(0, UpdateRotation, FALLING);

      // Use serial port to keep user informed of rotation
      Serial.begin(57600);
      }

      void loop() {
      }

      void UpdateRotation() {
            // This is the subroutine that runs every time pin 2
            // switches from high to low.

            if (digitalRead(B)) {
                  Rotor++;
                  } else {
                  Rotor--;
            }
            Serial.println(Rotor, DEC);
}

Serial.print() and ISRs don't go together. It takes a relatively long time to output serial data, which is why you are missing interrupts.

In the ISR, you should just update the Rotor value. Then, in loop, output the value periodically.

Is this what you meant for me to do?
I am really just starting at this so I probably didn't understand.

I tried it but the number returned seem kind of random.

Once I get you improvment implemented I will work on printing only values that are divisible by 10, not sure how to do that efficiently though.

byte A = 2; // One quadrature pin
byte B = 3; // the other quadrature pin
volatile int Rotor = 0;

void setup() {

      // set DIO pins
      pinMode(A, INPUT);
      pinMode(B, INPUT);

      // Attach interrupt to pin A
      attachInterrupt(0, UpdateRotation, FALLING);

      // Use serial port to keep user informed of rotation
      Serial.begin(57600);
      }
      void loop() {
                    if (digitalRead(B)) {
                  Rotor++;
                  } else {
                  Rotor--;
                    }
      }
      void UpdateRotation() {
            // This is the subroutine that runs every time pin 2
            // switches from high to low.
            Serial.println(Rotor, DEC);
}

No. Exactly the opposite. The code to read the encoder state and update Rotor goes in the ISR. The code to print out the value of Rotor goes in loop(). The value should not br printed on every pass through loop, obviously, but every 10 seconds or so would be OK.