Connecting two quad encoders to Arduino USB

Hello All,

I am an extreme newb when it comes to microchips, so please excuse my ignorance.

I was looking for a little help. I'm attempting to build a stitch regulator for my wife and her quilting machine. My plan is to have a little arm that dangles below a carriage that has a spinning wheel/encoder. It would be fixed to the carriage that moves back and forth along the x/y axis of the frame. The encoder will drag against the frame and spin an encoder. Since the wheel is a fixed diameter, this should be able to tell me the distance, direction and speed of travel. Based on that Info. I'm either going to try and control the stock AC motor via PWM or utilize a DC motor and control that is driven by step/dir pins. I'm really not sure on that front either.

Ideally, I'd like to be able to place X number of stiches per length of travel. I.e. 10 stitchs per inch.

Sooooo, I picked up a USB arduino in the hopes that I can do this.

My initial code was pilfered from the example in the playground for a Rotary Encoder internet and use the interupt pins.

Unfortunately, if I look at the Arduino documentation correctly. There is only 2 interupt pins. (2/3) Althought in the code it used 2/4 and the initial example used 3/4. So I'm a little confused that the INT disgination on the atmel website is.

Either way,

I'm going to need 4 pins. Additionally, when using the interupt pins, it would appear that I'm missing pulses or gaining them. It's hard to determine exactly.

I'm utilizing a US digital E4p-300-250-HM. I'm slightly concerned that these may be generating more pulses than the Arduino can handle??

Is there a better way? If I only have 2 interupt pins, would it be better to use some type of constant loop. I.e. watch pins, as they change do cool stuff? Then I could reserve the interupt pins for an e-stop type button.

Or would it be better to use a loop regardless? I've seen some examples where they are using time diffs to perform different functions. I.e. every 2us check the encoders for changes?

Do I have unrealistic expectations? I.e. is the board not up to the task? As I stated before, I'm just a microchip newb with a honeydo chore.

Thanks for any help.

Well just a few thoughts on your project. First without knowing the RPM and steps per revolution of that your encoders will be turning it's hard to determine the timing requirements that the Arduino will have to handle.

I played with two optical encoders wired to my Arduino and was able to have both operating using interrupts. I used interrupt pin 2 for channel A of encoder #1 and the other interrupt pin 3 for channel A of encoder #2, channel B of #1 wired to digital pin 4 and B of #2 wired to digital pin 5.

Things I learned about my encoder(s):

While my manufacture rated my encoder as 128 steps per revolution, one can get either 128, 256 or 512 steps per revolution on it depending on if you use both interrupt pins for one encoder and utilize either FALLING or CHANGE interrupt mode. If you are using two encoders then I couldn't have the highest step rate, 512, as each encoder requires at least one interrupt pin. Read the comments on my posted sketch to see if you can see how the different configurations can effect the resulting step rate. Also I'm posting the version using the Arduino digitalRead(pin) functions, but if one uses direct port reads instead there can be quite a faster response (faster interrupt routine time) to steps and therefore able to handle a faster RPM.

Keep the Interrupt functions as short and simple as possible if you want maximum speed capacity and remember that you can't use any functions that utilize interrupts while you are inside a interrupt routine.

// Proof of concept- handling two optical rotory encoders using Arduino interrupts 0 & 1
// Also added a digital output pin for both direction and step pulses for each encoder


#define encoderPinA 2                  // Encoder #1, A channel, Arduino pin2, Interrupt0
#define encoderPinB 4                  // Encoder #1, B channel, Arduino pin3
#define encoderdir 6                   // Encoder #1, direction output, Arduino pin6
#define encoderstep 7                  // Encoder #1, step output, Arduino pin7

#define encoder2PinA 3                 // Encoder #2, A channel, Arduino pin2, Interrupt1
#define encoder2PinB 5                 // Encoder #2, B channel, Arduino pin5
#define encoder2dir 8                  // Encoder #2, direction output, Arduino pin8
#define encoder2step 9                 // Encoder #2, step output, Arduino pin9

volatile unsigned int encoderPos = 0;  // Variables for encoder #1
unsigned int encoderLast = 0;
int position = 0;
int positionB = 0;

volatile unsigned int encoder2Pos = 0;  // Variables for encoder #2
unsigned int encoder2Last = 0;
int position2 = 0;
int position2B = 0;



void setup() {

  // Setup Encoder #1 pins
  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  digitalWrite(encoderPinA, HIGH);  // activate soft pull-ups
  digitalWrite(encoderPinB, HIGH);
  pinMode(encoderdir, OUTPUT);   //Encoder #1, output direction pin
  digitalWrite(encoderdir, LOW); 
  pinMode(encoderstep, OUTPUT);  //Encoder #1, step output pin
  digitalWrite(encoderstep, LOW); 
  
  // Setup Encoder #2  pins 
  pinMode(encoder2PinA, INPUT);
  pinMode(encoder2PinB, INPUT);
  digitalWrite(encoder2PinA, HIGH);  // activate soft pull-ups
  digitalWrite(encoder2PinB, HIGH);
  pinMode(encoder2dir, OUTPUT);   //Encoder #2, output direction pin
  digitalWrite(encoderdir, LOW); 
  pinMode(encoder2step, OUTPUT);  //Encoder #2, step output pin
  digitalWrite(encoder2step, LOW);
  
  //  Enable Interrupts 
  attachInterrupt( 0, doEncoder1, FALLING ); // Encoder #1, FALLING=128 steps/rev CHANGE=256 steps/rev
  attachInterrupt( 1, doEncoder2, FALLING ); // Encoder #2, FALLING=128 steps/rev CHANGE=256 steps/rev
  // Note that if one uses just one encoder wired to both interrupt pins using CHANGE on can increase to 512 steps/rev
  Serial.begin(57600);
  Serial.println ("Encoder ready");   // End of setup 
}

void loop() {
  
 // Start of Rotary Encoder #1 Code

  if ( encoderPos != encoderLast ) {
    if ( encoderPos > encoderLast )
       position = ++position;
    else position = --position; 
  encoderLast = encoderPos; 
    
  Serial.print ("Encoder #1 = ");       // use serial output for demonstration showing encoder movement
  Serial.print (position, DEC);
  Serial.print ("      Encoder #2 = ");
  Serial.println (position2, DEC);   


//  digitalWrite(encoderstep, HIGH);   // generate step pulse 1 millsec
//  delay(1);                          // or use other methods to set pulse width
//  digitalWrite(encoderstep, LOW);   
  }
 // End of Rotary #1 Encoder Code
  
 // Start of Rotary Encoder #2 Code
  if ( encoder2Pos != encoder2Last ) {
    if ( encoder2Pos > encoder2Last )
         position2 = ++position2;
    else position2 = --position2;
  encoder2Last = encoder2Pos;

  Serial.print ("Encoder #1 = ");     // use serial output for demostration showing encoder movement
  Serial.print (position, DEC);
  Serial.print ("      Encoder #2 = ");
  Serial.println (position2, DEC);   

  digitalWrite(encoder2step, HIGH);  // generate step pulse 1 millsec
  delay(1); 
  digitalWrite(encoder2step, LOW);   
  }
 // End of Rotary Encoder #2 Code
 
 // 
 //  Any additonal application code goes here
 //
}


 // Start of Interrupt routine for encoder #1
void doEncoder1() {
    if (digitalRead(encoderPinA) == HIGH) {       // test for a low-to-high on channel A
    // reserve for direct port I/O
      if ( digitalRead(encoderPinB) == LOW ) {     // check channel B to see which way encoder is turning
          // reserve for direct port I/O
        encoderPos = ++encoderPos;                // CW rotation
        digitalWrite(encoderdir, HIGH);           // Set direction output pin to 1 = forward
            // reserve for direct port I/O
      }
      else {
         encoderPos = --encoderPos;                // CCW rotation
         digitalWrite(encoderdir, LOW);            // Set direction output pin to 0 = reverse
             // reserve for direct port I/O
      }
    }
    else {                                        // it was a high-to-low on channel A
      if ( digitalRead(encoderPinB) == HIGH ) {    // check channel B to see which way encoder is turning
        // reserve for direct port I/O  
        encoderPos = ++encoderPos;                // CW rotation
        digitalWrite(encoderdir, HIGH);           // Set direction output pin to 1 = forward
            // reserve for direct port I/O
      }
      else {
        encoderPos = --encoderPos;                // CCW rotation
        digitalWrite(encoderdir, LOW);            // Set direction output pin to 0 = reverse
            // reserve for direct port I/O
      }
  }
  digitalWrite(encoderstep, HIGH);   // generate step pulse 1 millsec
      // reserve for direct port I/O
  digitalWrite(encoderstep, LOW); 
          // reserve for direct port I/O  
}     // End of interrupt code for encoder #1


 // Start of Interrupt routine for encoder #2
void doEncoder2(){
  if (digitalRead(encoder2PinA) == HIGH) {       // test for a low-to-high interrupt on channel A
    if ( digitalRead(encoder2PinB) == LOW ) {    // check channel B to see which way encoder is turning
      encoder2Pos = ++encoder2Pos;               // CW rotation
      digitalWrite(encoder2dir, HIGH);           // Set direction output pin to 1 = forward
    }
    else {
      encoder2Pos = --encoder2Pos;               // CCW rotation
      digitalWrite(encoder2dir, LOW);            // Set direction output pin to 0 = reverse
    }
  }
  else {                                         // it was a high-to-low interrupt on channel A
    if ( digitalRead(encoder2PinB) == HIGH ) {   // check channel B to see which way encoder is turning
      encoder2Pos = ++encoder2Pos;               // CW rotation
      digitalWrite(encoder2dir, HIGH);           // Set direction output pin to 1 = forward
    }
    else {
      encoder2Pos = --encoder2Pos;               // CCW rotation
      digitalWrite(encoder2dir, LOW);            // Set direction output pin to 0 = reverse
    }
  }
  digitalWrite(encoder2step, HIGH);              // generate step pulse 1 millsec
  digitalWrite(encoder2step, LOW); 
}   // End of interrupt code for encoder #2

On your main motor control, you can't control speed of most AC motors using PWM, only DC motors. Controlling the speed of a AC motor is not an easy task as most are controlled by the AC power frequency of the AC voltage applied to the motor, so would require a invertor driver that can change frequency of the AC power, not cheap or simple, so utilize a DC motor using PWM control or a DC stepper motor using digital outputs.

Good luck and work with little sections at a time rather then try and write/build the whole project at once.

Lefty

Thanks for the reply.

I'll look through the code and see if I can decipher it.

I believe that the encoders are 1024 per revolution, but I can't seem to cross reference US Digital's site.

If I have to, I'll buy new ones. The 30 bucks a piece isn't too hard to swallow if I can make this work.

I have two sets of encoders, one doesn't appear to have any marks on it. But is a US digital E4.

The other is the one posted above, which I believe is 300 cycles per revolution. But doesn't seem to say how many pulses per revolution. ((400-1440 list on the website))

If I can get the arduino to capture them. I suspose I could mark the wheel and spin it.

The wheel is one inch in diameter. I would expect that the machine will not move more 20-30 inchs per minute. I'll figure worst case will be 50 inches per minute.

Which is I do my math right... Worst case....
1440 pulses per inch of travel or one revolution....
1440 x 50 inches... = 72000 pulses.
Multiplied two encoders...
144000 in a minute....

I'm not sure if that is right, seems like it would be more pulses.

scratches brain

I got the code set up with just one encoder for now.

Spinning it with my finger in a "kinda" controlled manor appears to be suffering the same lost steps. I.e. spin it one revolution to the right and it displays ~300 up from zero. Spin it the other direction and it will be 20-30 instead of the expect zero.

Edit
Running the encoder really fast across my desk is definately loosing steps. Since I'm only moving in one direction. I get an output that is roughly the same amount as if I move in really slow at a much shorter distance.
Edit

I would assume that I have some type of debounce, also that I might not be turning it exactly the same amount.

I have a small box that the Aruino kit came in. Roughly about 4 inches across. If I roll the wheel to one side. I get X then holding it steady and rolling it back. I don't come back to the numerical starting point.

Any ideas on debounce or missed steps.

I'd guess that there are some level of steps lost as it does the interupt code sequence. I'm assuming that there is no way to create multi-tasking. I.e. I create a code section that does nothing but update a reference variable.

Then the rest of my logic goes off of it.. I read somewhere that multitasking was not possible, but that people were cheating via timeslices aka timediffs.

I ordered the getting to know your Arduino book from amazon, or whatever the title was.

Hopefully it will help with my newbness.

Edit
Assuming that this guys it not up to the task and that your code is working perfectly. (I was getting similar results with the code I used)

Is there another microchip that might be more up to the task. Also one that I don't have to spend a lifetime learning memory stack locations and assembly code. Ease of use on the Arduino was big for me. I just want to make the product, not learn how to be an assembly programmer. I have a pile of other projects that don't require the mass input of signals from encoders.

Or maybe there is some other device that I can put inline with this guy, between the encoder and the arduino. ponders

I guess I just want to make sure that I don't waste any time, if this guy won't be up to this task.

According to this link...

The max frequency is 60 kHz, assuming that I'm reading the chart correctly.

I suspose another option is to find a much lower resolution encoder.

grumbles

Edit

:cry: :o :o :cry:

Any ideas on debounce or missed steps.

My first attempt at learning to use encoders were with one of those cheap $5 mechanical encoders with mechanical detents, that makes a click click sound as you turn them. The contact bounce was terrible and the mechanical detent were not always at a consistent "inter-step" state. That is not to say they aren't useful as a simple user interface control with possibly a little help with some software contact debouncing, but for mechanically coupling to a rotating shaft I found them useless. So then I found a surplus sale for a quality optical encoder and what a difference in performance! No bouncing at all so no debouncing required and having no mechanical detents one can move to any position without weird jittering or steps.

As far as missing steps, again the trick is to make sure the interrupt service routine is as simple and short as possible and if still missing steps then trying using direct port I/O access Vs using digitalRead() functions, that made quite a difference in how fast I could spin my encoders without missing steps. However there will always be a case where the encoders high resolution (more steps per rev) coupled with high shaft speed will exceed what your Arduino and your code can handle. If you don't require a high resolution encoder then having one with a more resonable resolution might be a simple fix.

One can always go with some external digital logic circuits that can process encoder signals and converts it to a more simple 2 digital signal, a step pulse bit and a direction bit.

Lefty

I found a company called LSI/CSI that appears to have alot of IC's that interact with encoders.

US digital appears to carry most of the items at

http://www.usdigital.com/products/interfaces/ics/

I see two potential options...

lfls7184
This chip takes the A/B phase and turns it into a clock pulse and a Dir signal. Do you think that I would gain anything from using this? I would think that my interupt code would be alot simpler. Unfortunately, I don't know if the amount of signals would still be streaming out of the new chip as quickly as the encoders. Thereby overloading the micro, or causing missed steps during the interupt code.

Another option is the LFLS7366R-S
This appears to be able to do the same thing, but send serial signals to the micro. This chip is beginning to step outside the area of my comfort zone. Additionally, from the hardware page. The serial interface is usuable within arduino, it it's default form.

I kinda feel like I'm starting be sucked into the rabbit hole.. I hope that the Queen of Hearts doesn't chop off my head...

Would it be a bad idea, to think that I could just create small loop in the main, that reads the state of the ports involve and does some math on their changing. I.e. Don't use the interupt pins.

Please excuse my psuedocode. I'm hoping that it makes sense.

Void Main (

Check counter1.
start timer...
while timer is less than 10us
how many pulses did I receive - set variable to counterspeed1
what was my direction.

Check counter2.
start timer...
while timer is less than 10us
how many pulses did I receive - set variable to counterspeed2
what was my direction.

Spinmotor
If counterspeed1 is > 0 or counterspeed2 is > 0
then we need to spin the motor....

)

The main problem that I see here, is that the Spinmotor might stutter as the micro is processing the other requests. But I would expect that this would be the same on interupt method.

Any ideas?

Has anyone used the lfls7184 chip?

It says that it filters some of the dithering that I might be experiencing...