Incremental Rotary Encoder and Arduino...

So I have bought this 12v Motor that has an Incremental 64 CPR Encoder installed on it.
The classic gray code A/B sensors.

I wrote the arduino code, similar to examples found on the net.
Tried polling the sensors both: on Loop and using Interrupts.

Problem is, the motor turns fast. Not lightning fast, but fast and some codes are being skipped.
When I turn the motor by my hand 'click' by 'click' all codes are in proper order both directions turned.

But, if I try to turn it, even by hand a little bit faster, then some codes will be skipped.
Is that normal with a 64 CPR encoder for arduino? Is it "high" precision for the Speed Arduino can actually poll the data?

Please note: the code is optimized, does not have bugs or does expensive operations that delay the execution.
(for instance, yes. the sensor pins are polled once per loop and saved into two variables, then used as those variables)

I am starting to wonder, do I have to place an Absolute Encoder to make this work?
Or an even smaller precision like 4 CPR to be sure arduino can process every pulse correctly?

Thanks.

Interrupts sound like the way to go. Post your code though.

Tried already with attachInterrupts.
Still skips codes... =(

Please note. Skipping of codes does not happen if I rotate very slowly.

antonisk:
Tried already with attachInterrupts.
Still skips codes... =(

Please note. Skipping of codes does not happen if I rotate very slowly.

Again, I'm not familiar with the encoder or pulses you are receiving or, how you set up the interrupts. Did you set the interrupt as a positive flank detection instead of pin change? That's quite an important part.

Are you running the serial port? I don't have the serial port library with me but, my guess is that when you send data through it, you disable interrupts while you're sending data and then enable them when the transmission if finished. At higher speeds, this causes a problem in your software.

Do this, delete all the Serial.print*() functions from the software. Create a condition that you'll only send data if a request from the serial port is received. Say, every time you send a "T" over the serial protocol, the program sends the count to you. Run the encoder fast for a set number of times, stop, and check the count on the encoder input.
This will tell you if the problem is on the serial port or not.

However, the best way to do this task is to free the system from processing just to count.

Check how to set up Timer 1 as a counter, and then place the encoder signal in that input. It won't detect direction, but it will count. probably fast enough for you to catch it.

I'm at work now, but if you can't find it by then, I'll post an example for you to see what I'm talking about.

hmm, very interesting and you are right on spot with your guesses.

-When I tried the interrupts I added on Pin Change.
-To test if it was working properly I also Serial.Printed the Sensors Output

So both have problems it seems.
I will make the changes you recommended retest and see if it worked.

Thanks, I will post the results once I can.

well I'll be damned.
The serial.print was slowing the polling of the sensor by around 35 times.

I was: Polling Sensors - > Serial.Printing -> Polling Sensors...
The counts from around 150 went once I removed the serial.print to 5300

Now, even without interrupts it works almost perfectly.
I guess if I want to make it not lose a tick, I will need to place it with Interrupts.

So thanks allot, I couldn't have imagined that serial.print has so much overhead.
And I was beginning to feel cornered with this problem...

To do list:

  • Share knowledge
  • Help someone out

Now that I got on with my to do list... LOL

Where I am internet is really slow, and any download gets reset at about 50k... so downloading enough manual on the ATmega328 to see the Timer1 register configuration is just not happening here.

If you can pass on the pages with the register description for timer1 I promise to do my best in giving you an example on how to count encoder pulses properly and still being able to have the software do a bit more. :\

If you set your timer 1 (16 bit, up to 65536 counts) or timer 0,2 (8 bits, up to 255 counts) to get it's signal clock from an outside pin, you'll have the timer counting the pulses. You'll then set an interrupt for the specified timer overflow and when that overflow happens, you know 255 (or 65536) pulses have gone by. If you need to, you can also read the actual counts you have at the moment.

The code will look something like this:

unsigned long bigLaps = 0; 

setup()
{ 
//setup timer 1 (16 bits) as a counter 

T1CR= ;

//define counter source

//clear T1 counter

TCNT1 = 0; 

//enable interrupt
TIMSK=...; 
}

ISR(TIMER1_OVF_vect )
{
//when this runs, you had 65365 pulses counted.
bigLaps++; 
}

unsigned int readCounter()
{
unsigned int count; 
cli();
count = TCNT1;//might skip one or two pulses.
TCNT1 = 0;  
sei();
return count; 
}

loop()
{

//send data 
if (Serial.available())
	{
	char C = Serial.read(); 
	if (C == 'T')
		{
		Serial.print("big laps ");
		Serial.println(bigLaps);
		Serial.print("actual count ");
		Serial.println(readCounter());
		}
	}
}

Ok... good internet now.

I'll post the example for the timer1 since this one is a 16 bit one and has a few "tricks".

unsigned long bigLaps = 0; 

setup()
{ 
//set pin T1 as input. Must verify T1 pin is actually Arduino pin 5 
pinMode(5, INPUT);           // set pin to input
digitalWrite(5, HIGH);       // turn on pullup resistors


TCCR1A = 0; //no waveform generation needed. 
TCCR1B = 7; //clock coming from pin T1 on rising edge. T1 is Arduinos Pin 5
TCCR1C = 0; //No force output compare. 

TCNT1 = 0; 

TIMSK= 1; //enable overflow interrupt

}

ISR(TIMER1_OVF_vect )
{
//when this runs, you had 65365 pulses counted.
bigLaps++; 
}

unsigned int readCounter()
{
unsigned int count; 
cli();
count = TCNT1;//might skip one or two pulses. 
sei();
return count; 
}

loop()
{

//send data 
if (Serial.available())
	{
	char C = Serial.read(); 
	if (C == 'T')
		{
		Serial.print("big laps ");
		Serial.println(bigLaps);
		Serial.print("actual count ");
		Serial.println(readCounter());
		}
	}
}

Now, I haven't compiled or tested this code. It may have problems, but if you test and post results, I'm sure we can all pitch in to get it going. I think this is a far more elegant solution than polling or interrupt counting, but it's up to you.
Plus, this way you might learn a bit more about Timers and counters running on Arduino. :wink:

Post the results...

Interesting, I will have to lookup timers on arduino.
Thanks again for the replies. Once I have the time to make the code work with timers I will post the results.