Using Motor Encoder with Arduino?

So I was wondering exactly how one would set up motor encoders? One of my worries is that im using most of the timers on the Arduino for PWM to control motors, and im assuming with quadrature encoders you would need two timers per encoder to keep track of both channels? Im just looking to get some info on how to monitor 2 to 4 quadrature encoders and make a PID algorithm to keep the wheels at the same speed. Ive searched around but havent seen any really solid examples, and now im worried I dont even have enough timers for my encoders!!

One of my worries is that im using most of the timers on the Arduino for PWM to control motors, and im assuming with quadrature encoders you would need two timers per encoder to keep track of both channels?

Encoders don't need any timers. They typically are set up to generate interrupts.

If you want to use them to keep track of distance traveled and compare average speed of each motor wouldnt you need to keep a running average of the pulses? Not just check an interrupt every once and a while?

If you want to use them to keep track of distance traveled and compare average speed of each motor wouldnt you need to keep a running average of the pulses? Not just check an interrupt every once and a while?

The interrupt gets the pulses, and determines whether to increment or decrement the count. No timers involved.

Keeping track of distance traveled is a matter of multiplying pulse count by distance between pulses. No timers involved.

Comparing average speed is a matter of comparing pulses per unit of time from each encoder. You need a clock for that, not a timer. The clock (the millis() function) and a timer are not the same thing.

So, I'm still failing to see the problem. Sorry.

Ive obviously got the wrong idea about something here.. This is how im thinking about Encoders

In order to keep track of pulses you need to count them obviously so for every hi pulse you would increment a variable. You would need to monitor two channels for each encoder, so overall to read a quad encoder you need two pins and two variables. This is just a simplification of things, ignoring overflow.

An Arduino can only handle one task at a time, unless your using a hardware peripheral like a counter, you can also cause something to create an interrupt which will halt the rest of the processor to run the interrupt handler. So if one needed to count every pulse of an encoder using an interrupt you would set up an interrupt handler to increment you variable every time pin X goes hi. Now wont this cause the mcu to stop everything to increment the variable? at 141 pulses per revalution x 2 channels x 2 motors thats, alot of interuption to the main program wont that kill everything else you have going on?

Obviously you know what your talking about, Im just trying to figure out where my logic is wrong

rwgast:
So if one needed to count every pulse of an encoder using an interrupt you would set up an interrupt handler to increment you variable every time pin X goes hi. Now wont this cause the mcu to stop everything to increment the variable? at 141 pulses per revalution x 2 channels x 2 motors thats, alot of interuption to the main program wont that kill everything else you have going on?

That depends on how quickly the motor is rotating, and what is going on in your program.

Keep in mind, incrementing a variable is a very quick task, in the order of hundreds of nanoseconds.

Ok so lets say 240RPM, 4 motors, thats 8 encoder channels to watch an increment a variable. This works out to 4512 variable increments total every second or 564 increments per channel per second.. im looking to also control the motors with a closed loop pid algorithm, monitor a battery pack with the ADC and a DS1620, and do serial communication to a master chip thats acually telling the ARV to turn the wheels, motor speed, etc...

Is this going to work.... im sure ive seen 4 wheel arduino bots using wheel encoders but this seems like a lot of cpu power to me.

4512 interrupts per second is not an unreasonable number, provided you keep the ISR short and efficient.

Do you need the full resolution provided by the encoders, or will half the resolution be sufficient? If half the resolution is acceptable, then you can generate an interrupt on just one of the encoder outputs, instead of on both of them.

Well thinking about this an atmega168 will do 16 million instructions per second, and avr's execute most instructions in one cycle so if the interupt is only something like

Var++

assuming that maps to one asm instruction this would still give me about 11.5 usable mhz of speed after the encoders are implemented correct? This could be totally off the wall wrong thats why im asking.

As far as using the full resalution of the encoders, I really only need one channel to determine direction with, the other needs to be at full resolution. I had also thought about using a 74xxLogic counter to maybe track every pulse then the arduino would only have to call an interrupt every 1024 pulses or how ever high the counter goes, but this is more complication I dont want to deal with I just want to get this motor control board put together and off a breadboard

If you're interested, here's a link to some code I use to read four encoders. One encoder is attached to pins 2 and 3, the others are two to a port. The code uses all available external interrupts.
https://github.com/WizenedEE/arduino/blob/master/rover5/interface/readencoders.cpp

My interrupts each take about 75 clock cycles to execute.
Var++; in an interrrupt would have to

  1. Save the state of about 10 registers (~20 cycles)
  2. Read Var into memory (2, 4, or 8 cycles depending on size)
  3. Add 1 to Var (2, 4, or 8 cycles depending on size)
  4. Store Var back into memory (2, 4, or 8 cycles)
  5. Restore the registers to their previous values (~20 cycles)

Awesome thank you very much, do you happen to have any code using PID that keeps the wheels the same speed?

rwgast:
Ive obviously got the wrong idea about something here.. This is how im thinking about Encoders

In order to keep track of pulses you need to count them obviously so for every hi pulse you would increment a variable. You would need to monitor two channels for each encoder, so overall to read a quad encoder you need two pins and two variables. This is just a simplification of things, ignoring overflow.

Two pins and one variable. You only need interrupt handler on only one of the pins (in CHANGE mode) if you're
prepared to reduce precision by a factor of two. On the pin change XOR the two pins together - result is direction.

digitalRead() is quite slow, so direct port manipulation can really speed up such interrupt processing.

rwgast:
Awesome thank you very much, do you happen to have any code using PID that keeps the wheels the same speed?

haha no, that's quite a bit way down the road. First I'm working on tracking where the robot is based on the encoder values (differentiate to find speeds, rotate vector depending on angle, integrate to get new distance).

If you want to measure velocity profiles with your encoder, then you definitely need to use a timer/counter. I have used the Mega to do this a few times. You put the 2 quadrature encoder channels to the INT0 and INT1 pins 20 and 21 of the MEGA. You set up the interrupt vectors for either transition:
EICRA = B00000101; //EITHER EDGE FOR THE INTERRUPT
EIMSK = B00000011; //INT0 and INT1 ENABLED

You have two interrupt routines ISR(INT0_vect) and ISR(INT1_vect). Both basically do the same thing. In those routines, you grab the two bits from the encoder channels and determine the way the encoder is turning. (I do it by using two arrays CW and CCW.) You also grab the count from the interval timer, reset it and start it running again. The ISRs store the encoder position in one unsigned integer array, the position in the first position, the timer interval in the second position. The MEGA has 8K of SRAM so you can store about 2000 samples without resorting to external RAM.

byte CW[4] = {0x01,0x03,0x00,0x02};
byte CCW[4] = {0x02,0x00,0x03,0x01};

//BOTH ISRs do the same thing

ISR(INT0_vect){
byte encoded = PIND & 0x03; //converting the 2 lower bit value to single number

last_e_direction = e_direction;
if (CW[encoded] == lastEncoded) {encoderValue ++; e_direction = 1;}
else if (CCW[encoded] == lastEncoded) {encoderValue --; e_direction = 0;}

lastEncoded = encoded;

//count is an unsigned integer that keeps track of the number of positions and timer intervals stored
count+=2;
// ptr points to the array I am saving values in
*ptr++ = (unsigned int) encoderValue;
// we use 16 bit timer 3 of the MEGA
TCCR3B=0x00; //stop timer
//saving counter (time) value to the array
*ptr++ = (unsigned int) TCNT3;
TCNT3=0x00; // counter reset to zero;

//enable overflow and input capture interrupts

/* start timer, without prescaler, rising edge*/

TCCR3B=0x41;
//Collect data untill you have filled the array

if (count >= HIGHEST_COUNT) EIMSK = B00000000; //INT0 and INT1 DISABLED; //stop the interrupts when we have collected the full number
}