Using a 10,000 Pulse per revolution encoder with an Arduino

Hi Gang,

A big shout out to all that post here. This is my first post but not my first visit. I have been on and off here for years, grabbing info and reading the details of the struggles and victories.

I have become stumped on an issue.. I have a 10,000 pulse per revolution encoder in a machine and it is expensive and hard to swap out and I am unable to get it to work at speed.

Here is a short, point form of what I have done:

  • used the encoder.h library and had success to a point.
  • tried putting decade counters on the A and B of the encoder outputs to try to bring the pulses to a tenth of what they were.. no...
  • employed the LS7184 to hash out the encoder into a 'direction signal' and 'clock pulse'. This was damn tidy. ...and put to an interrupt, it does a great job ...but at speed.. nope.

===================

  • added a decade counter to the output of the LS7184 to chop that back to a tenth... I still need to try it, but I am not optimistic.

It keeps up with my drill at like about 4khz. The machine can generate about 120 khz.

So my short request is should I keep dividing this frequency or give up and swap the encoder or is there something I am not seeing?

I have a lower resolution encoder working, patched in poorly, to buy time and it just does not bring the resolution I need to monitor speed.

For me, this has become a (hardware) issue that ultimately exasperates the Arduino, every time I try to count the pulses.
I was using a Mega and it almost crashed trying to keep up. I have moved to a dedicated Arduino and "I2C" the data over to the Mega.

Does anyone know the top end frequency I should stay under for an interrupt pin?

Thanks in advance!
Gary

karnak:
So my short request is should I keep dividing this frequency or give up and swap the encoder or is there something I am not seeing?

I have a lower resolution encoder working, patched in poorly, to buy time and it just does not bring the resolution I need to monitor speed.

My guess is that you need to divide the frequency or use a much faster microprocessor.

But it is the second part of the quote that particularly caught my eye. I would expect to have good speed information with a single pulse per revolution and I would consider 16 pulses per reolution a luxury,

...R

Hi,
Welcome to the forum.

What speed is the encoder rotating at?
Is speed all you need to monitor?
Can you post a link to data/spec of your encoder?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Can you please post a copy of your sketch, using code tags?
They are made with the </> icon in the reply Menu.
See section 7 http://forum.arduino.cc/index.php/topic,148850.0.html

Does your encoder need output resistors, ie is it open circuit collector type?

Thanks.. Tom... :slight_smile:

You mention 120kHz as your maximum pulse rate. That's a pulse every 8uS or so

A clean piece of code using an interrupt and micros() might get you there.

or Increment a counter and examine it say 10 times/second...

regards
Allan

ps I've never heard of a 10,000 pulse/rev encoder....pretty special - and perhaps unnecessary?

regards

Allan

Thanks for the quick replies!
I will do my best to hit everyone's questions in order (sort of)

Robin:
Dividing was my best solution so far and may continue doing that to see where it goes...

The encoder is used for speed, counting distance, and later, position functions. (Speed and counting distance are the primary uses)

Tom:
I am uncertain of the speed the encoder is actually spinning. There are belts and gearboxes that muddy the data. I would have to measure it independently. I did get about 50khz from it at about half the machine speed

Q2, see above

The encoder is a Teledyne Gurley 8235 - 10000 - CDSA

I have attached a sketch i did on my phone and hope it posts ok... I can do better if needed.

The area marked "Note 1" is with regards to the need for resistors at the encoder input. My test encoder needs them while the machine does not.

Thanks for the tip! Here is the code:

/* 
  Quadrature encoder example
  Interface to LSI LS7184 quadrature clock converter
  (USDigital part #LFLS7184)
  
  Demonstrates reading quadrature encoder using Arduino interrupts.
  Clock output is connected to pin 2 (Int0); direction output is 
    connected to pin 4.
  Current position is displayed in Serial Monitor window.
*/
long Garys = 0;
int GarysPerMin = 0;
int newencoderACount = 0;
const int clkPinA = 2;
const int dirPinA = 4;
volatile long encoderACount = 0;
long newEncoderACount = 0;
long GaryVar = 4377; // set this to the rotation pulses of one turn of a roller
volatile boolean changeFlag = false;
bool I2CUpdateState = LOW;

long previousGarys = 0;


int analogUpdateInterval = 500; // 5 x timerInterval (500 ms now)
int analogUpdateTemp = 0;

unsigned long timerPrevMillis = 0; // always start at 0
const long timerInterval = 200; // ms

void setup() {
  Serial.begin(115200);
  pinMode(clkPinA, INPUT);  
  pinMode(dirPinA, INPUT);  
  attachInterrupt(0, encoderIntA, RISING);
  }

void loop() 
{
  unsigned long currentMillis = millis();

  //Count Garys
  if (encoderACount >= GaryVar)
  {
    Garys++;
    encoderACount = 0;  //Have to reset this! Otherwise it gets totally out of hand! 
  }

  //Print messages
 if (currentMillis - timerPrevMillis >= timerInterval) 
  {
    GarysPerMin = (Garys - previousGarys) * 20;
    
    Serial.print("                                   Garys : ");Serial.println(Garys); 
    Serial.print("Previous Garys: ");Serial.println(previousGarys);
    Serial.print("Garys per minute : ");Serial.println(GarysPerMin);
    Serial.print("Garys per second : ");Serial.println(GarysPerMin/60);
    Serial.println(encoderACount);  

    timerPrevMillis = currentMillis;
    previousGarys = Garys;
  }
}

void encoderIntA() {
  if (digitalRead(dirPinA) == HIGH)
    encoderACount++;
  else
    encoderACount--;
}

Allan:
I agree.. the khz band is slow compared to modern computer speeds, so this should be doable. I have been looking at the square waves and the peak voltages, etc to see where I am off the rails and then I still need headroom on the processor for I2C or other commands. It could be that the Print statements in my test sketch are causing the issue. I had intended to remove them after the debugging.

I also agree that there are too many pulses for what I need. It is a good encoder and properly mounted. It had been connected to an old Motorola VME system. I am replacing it with some Arduinos

:slight_smile:

Thanks, Gary

I worked on the Motorola VME development systems years ago. Clunky... but they worked..

Allan

I think that with the way you are using the LS7184 is not really reducing you interrupt count. It should be producing a count at each of the four quadrature transitions produced.

You could be directly reading the encoder in a way that reads one quarter of those counts. You can use the code you have but just bring the A/B outputs directly to pin 3 and 4 of the Arduino.

The divider may be the best way to go, but only reading 1 of the 4 quadrature transitions will help significantly.

You are not handling the reading of encoderACount properly in that it is not protected from changing while being accessed. I would count within the ISR, and set a flag true when that count == GaryVar. Check the flag rather than the actual count each pass of the loop.

@Allan:
Ya...clunky.. but there are bunches of hardware redundancies in them.. great, esp. in its day.. But sadly, all kept together by battery backed up stuff.. uug.

I am persisting on the encoder and not outright dumping it since I have a second machine that may go down at some point (I want to be ready). I am happy to move away from the encoder if all else fails but I will need to make sure it is not a "one of" build.

@cattledog:
I agree.. I am not reducing the pulse count. The decade counter is intended to do that to assist in keeping my interrupt form getting too loaded.

My encoder has A, A-, B, B- I, I-. I thought that by using only A and B, I was helping keep the encoder "an encoder" and not just a pulse counter. I have completely disregarded the other outputs.

I had tried early on to use the Index (I) on the encoder but going from 10,000 to 1 was a bit drastic.

My goal is to have a stable pulse count that I can send over to my main mega (when requested) and boil down the math there (distance/time) and use it in various ways to run some of the VME boards.

I will look further into reading and using the "encoderACount" better. There have been many versions of this sketch. The base sketch was from the web (a tutorial) and I have made some changes but tried to keep it mainly intact.

Oddly, on the bench with my 600 pulse encoder, multiplied (1:4) by the LS7184 to 2400 pulses, it is solid as a rock.. then I go plug into the big encoder at 1:1 and ..aaaahhh.

I am, admittedly persistent, but my Arduino skill set is not a strong as my will and it takes time for me to get my head around the how or why...

Thanks!
Gary

I had tried early on to use the Index (I) on the encoder but going from 10,000 to 1 was a bit drastic.

Reducing the count from 10000 to 2500 by interrupting on A RISING and reading B for direction should give you plenty of resolution and will put your interrupt count in the range of the 600 ppr at 2400 counts/revolution.

If resolution is the issue, you can read A CHANGE and get 5000 counts/revolution. The ISR stays the same.

EDIT: The ISR does need to be modified as follows to determine if A was rising or falling and if the direction pin was in the same of different state.

void encoderIntA() {
 if (digitalRead(clkPinA)== digitalRead(dirPinA))
   encoderACount++;
 else
   encoderACount--;
}

Hi,
Do you need to know direction of the speed?

Tom... :slight_smile:

Thanks cattledog. I had not looked at it that way... I will mess around and post back when I have had some results. That could do it.

Tom, the direction is not critical as the machine travels in one direction. If I move from the encoder then a different sensor altogether would be a good plan.

Thanks,Gary

ok.. finally had some time to dig in and see what was what...

One thing I had to do was consider high frequency noise. I added a .1uf tantalum capacitor to stop any crap from coming into the decade counter chip. With things (encoder) disconnected, the instability was obvious.

I went in with a scope and checked all the waves and all were good and things seemd to be ok. "A" from the encoder went to the decade counter and to an interrupt (pin 2). I was unable to reduce the amount of pulses to less than the designed number.

In an earlier post, I mentioned 50khz. I got back there (before the decade counter), but then noticed the frequency drop to 43 and go back to 50.. I checked the other outputs and had the same result. My intense search for a stable pulse led me to realize the encoder was likely going bad. Frustrating.
My sketch was running erratic and this was the issue...

So to recap..
Is it feasible to put a 10000 pulse encoder into an Arduino? Yes.
At great speeds? No. The decade counter was needed (I will reserve the right to be wrong on this, but I could not get there).

A slow, precision application would be a good fit for this (star tracker).

The encoder chip did not help in this application but I did enjoy using it and will consider using it again in a different application.

I will take my application to a different sensor type and use the code with Pin2 and move on.

Thanks to all whom helped. I hope this post helps someone else in some way.

Gary

Thanks for the feedback.

Perhaps you could clarify what you mean by this

The decade counter was needed (I will reserve the right to be wrong on this, but I could not get there).

...R

16e6 / 12e4 = ~1.25e2 = 125.

I am still surprised an interrupt did not solive the issue. At 120khz...you have 125 clock cycles at 16MHz between pulses. 125 cycles should have been more than enough to increment a counter in the ISR and have a basic function in the main () IMHO.

Robin and Johnny,

Both of your responses come down to the issue that the encoder was not reliable enough to go where I needed to go in terms of frequency.

I was using the decade counter to bury this symptom (I was unaware of the encoder's issue and thought it was an input issue), and I agree, this, on paper works.

I was not able to bring it to conclusion. I do know that bogging down an interrupt is possible and that print statements and other communication tasks will use resources that may be needed for counting.

So I am forced to back away from this. The actual feasibility (at speed) remains unknown. If I get another opportunity, I will take it.

:slight_smile:

Thanks, Gary

Sometime ago a wrote a quadrature output simulator program which can be helpful in working through your code and hardware problems without using the actual encoder. Here's a link to the original post. If you have a second Arduino, you can run this program on it, and jumper pins 9 and 10 to the inputs on your reader hardware/program.

//Quadrature signal generator two outputs offset 90 degrees
//emulate rotary encoder
//Timer 1 CTC with ICR1 TOP and 

void setup() {

  pinMode(9, OUTPUT); //output A
  pinMode(10, OUTPUT); //output B

  TCCR1A = 0; //clear timer registers
  TCCR1B = 0;
  TCNT1 = 0;
  GTCCR |= 1 << PSRASY; //reset prescaler

  //ICR1 and Prescaler sets frequency
  //no prescaler .0625 us per count @ 16mhz
  //prescaler 8 .5 us per count

  TCCR1B |=  _BV(CS11); // prescaler 8
  //TCCR1B |= _BV(CS10); //no prescaler

  //counts are zero indexed 2edges per ICR1 period
  //numerical values for prescaler 8.
  //e.g. 10k period give 20k encoder counts

  ICR1 = 199;//10k ICR1 period  20k encoder counts
  //ICR1 = 99; //20k ICR1 period 40k encoder counts
  //ICR1 = 49; //40K ICR1 period 80k encoder counts 
  //ICR1 = 46; //42.5K ICR1 period 85k encoder counts 
  //ICR1 = 41; //47.5k ICR1 period 95K encoder counts 
  //ICR1 = 39; //50k ICR1 period 100k encoder counts
  //ICR1 = 29; //66.6K ICR1 period 133k encoder counts
  //ICR1 = 19; //100k ICR1 period 200k encoder counts
  
  OCR1A = ICR1 - 1; //two different pulse widths almost 100% duty cycle
  OCR1B = OCR1A / 2; //offset by half period

  TCCR1B |= _BV(WGM13) | _BV(WGM12); //CTC mode with ICR1
  TCCR1A = _BV(COM1A0) | _BV(COM1B0); //Toggle OC1A/OC1B on compare match
}
void loop () {}

Here are the images from a scope showing the two offset square waves. One shot at 80khz and another at 500khz. With 4 edges per cycle, there should be 320k and 2m counts/second from the quadrature. It's somewhat confusing in that the timer cycle is 2x the square wave frequency because of the output pin toggle on match, and the quadrature counts per second are 4x the square wave frequency (i.e. 2x the timer cycle).

74ae338da2ad15e77a6451335865e7c175a5ccc5.jpg 1bb38e358a6a34b5204b5b2fd180a2b25ae9736a.jpg