Quadrature Decoders

There has been quite a lot of discussion regarding Quadrature Encoders and Arduino. I have typically used Microchip PIC24 with embedded hardware or added external circuity to off load the cpu. I do want to have example code for class. This is my implementation on an ATMega8 (Dagu Mini Driver) which has only 2 external interrupts.

/*
* Arduino sketch to demonstrate processing quadrature encoded
* pulses on a processor such as the Dago Mini Driver that only
* supports 2 external interrupts.
*
* For this sketch:
*
* Wire the left encoder A signal to pin 2 and B to pin 4.
* Wire the right encoder A signal to pin 3 and B to pin 5.
*
* For speed purposes use the hardware PORT names instead of the
* Arduino builtin pin read functions.
*
* Notes:
* 1. My test jig actually is a Sparkfun AT328P board with hand cranked
* rotary encoder.
* 2. The counts are limited to -128 … +127. If you can possibly overflow
* the signed 8-bit storage either decrease the sample interval or
* change to 16-bit signed integers.
* 3. Make sure that the interrupt does not occur while copying and
* reseting the counters!!
* 4. With only one interput per encoder you do lose half the speed
* resolution but do keep the direction.
* 5. You may need to change the sign on one of the encoder counts for
* dual opposing encoders.
*
* This is free code. No restrictions, no complaints accepted!
*
*/

#include <Arduino.h>

#define L 0
#define R 1

int8_t count[2][2];

void setup()
{
int
i, j;

<span style="color: #008000; font-weight: bold;">for</span> (i <span style="color: #666666;">=</span> <span style="color: #666666;">0</span>; i <span style="color: #666666;">&lt;</span> <span style="color: #666666;">2</span>; i<span style="color: #666666;">++</span>)
{
<span style="color: #008000; font-weight: bold;">for</span> (j <span style="color: #666666;">=</span> <span style="color: #666666;">0</span>; j <span style="color: #666666;">&lt;</span> <span style="color: #666666;">2</span>; j<span style="color: #666666;">++</span>) count[i][j] <span style="color: #666666;">=</span> <span style="color: #666666;">0</span>;
}

attachInterrupt(<span style="color: #666666;">0</span>, intrQuadL, CHANGE);
attachInterrupt(<span style="color: #666666;">1</span>, intrQuadR, CHANGE);

pinMode(<span style="color: #666666;">2</span>, INPUT);
pinMode(<span style="color: #666666;">3</span>, INPUT);
pinMode(<span style="color: #666666;">4</span>, INPUT);
pinMode(<span style="color: #666666;">5</span>, INPUT);

Serial.begin(<span style="color: #666666;">115200</span>);
delay(<span style="color: #666666;">200</span>);

}

void loop()
{
/*
* Critical Region - Atomic operation must be guarenteed!!!
* 1. Disable interrupts
* 2. Copy ISR counters to working
* 3. Reset ISR counts
* 4. Enable interrupts
*/
noInterrupts();

count[L][<span style="color: #666666;">0</span>] <span style="color: #666666;">=</span> count[L][<span style="color: #666666;">1</span>];    count[L][<span style="color: #666666;">1</span>] <span style="color: #666666;">=</span> <span style="color: #666666;">0</span>;
count[R][<span style="color: #666666;">0</span>] <span style="color: #666666;">=</span> count[R][<span style="color: #666666;">1</span>];    count[R][<span style="color: #666666;">1</span>] <span style="color: #666666;">=</span> <span style="color: #666666;">0</span>;

interrupts();

<span style="color: #408080; font-style: italic;">/*</span>

* Display results using our copy
*/
printByte(count[L][0]);
Serial.print(" ");
printByte(count[R][0]);
Serial.println();

delay(<span style="color: #666666;">200</span>);

}

/*
* Lookup table. For speed purposes I am using 3 bits but bit 1 is always
* 0 so states 2,3,6,7 can never happen. With a little more work in the
* ISR you could reduce the table to 4 entries. It is also possible to
* rework the ISR to use SWITCH or IF constructs.
*/
const int8_t quadDelta[8] = {-1, 1, 0, 0, 1, -1, 0, 0};

/*
* Left Quadratue Decoder ISR
*/
void intrQuadL(void)
{
register uint8_t
i;

i <span style="color: #666666;">=</span> (PIND <span style="color: #666666;">&gt;&gt;</span> <span style="color: #666666;">2</span>) <span style="color: #666666;">&amp;</span> <span style="color: #666666;">0x05</span>;			<span style="color: #408080; font-style: italic;">// isolate bits of interest</span>
count[L][<span style="color: #666666;">1</span>] <span style="color: #666666;">+=</span> quadDelta[i];

}

/*
* Right Quadratue Decoder ISR
*/
void intrQuadR(void)
{
register uint8_t
i;

i <span style="color: #666666;">=</span> (PIND <span style="color: #666666;">&gt;&gt;</span> <span style="color: #666666;">3</span>) <span style="color: #666666;">&amp;</span> <span style="color: #666666;">0x05</span>;
count[R][<span style="color: #666666;">1</span>] <span style="color: #666666;">+=</span> quadDelta[i];

}

void printByte(int8_t n)
{
uint8_t
k;

k <span style="color: #666666;">=</span> abs(n);
<span style="color: #008000; font-weight: bold;">if</span> (k <span style="color: #666666;">&lt;</span>   <span style="color: #666666;">100</span>) Serial.print(<span style="color: #ba2121;">" "</span>);
<span style="color: #008000; font-weight: bold;">if</span> (k <span style="color: #666666;">&lt;</span>    <span style="color: #666666;">10</span>) Serial.print(<span style="color: #ba2121;">" "</span>);
<span style="color: #008000; font-weight: bold;">if</span> (n <span style="color: #666666;">&gt;=</span>    <span style="color: #666666;">0</span>) Serial.print(<span style="color: #ba2121;">" "</span>);
Serial.print(n);

}

Quad decoder
There is a error in the code. The mask should be 0x05 in the right ISR.

With the ATMega8 you must use D2 & D3 for external interrupts. That forces either the A or B signals. The other signal can go to any other input. I happened to chose D4 & D5. You are correct in that I could have removed the shift and used a larger lookup table. The right table would need to be larger.

**Thank you gg.

Now I don’t**
Thank you gg.

Now I don’t have to write this, though I might do it anyway. Due to several things I no longer have any robotics code from before.

I still want to write my own, but I think I blew up my Teensy 3.1 last night. No smoke, but something happened to it. I want to build something now, but the only thing I have in a DFRobot kit that I bought used a while ago. Those motors have encoders, so maybe I do need to do this.

Maybe Arduino Mega’s have more than 2 interrupts. While I’ll admit that an old Comp Sci prof I had once said “The only allowable quatities for something in a processor are 0, 1, 2, and infinity. If you have 0, you’ll work around it. If you have one, it’s a carefully used resourse. If one of something is good, then two is better. After that, you want as much as you’ll ever need, hence infinity.”

I now see a reason for four interrupts. I do wonder why the Arduino folks chose the AVR over the PIC.

**ISR Timing **

I added some I/O pin toggling to the ISR function in an attempt to measure the execution timing of various coding techniques. Rewote the Quadrature ISR to use both SWITCH and nested IF constructs and used a digital scope to measure time.

 

Existing lookup table         1.54 usec

Switch                              6.00 usec

Nexted IF                          6.20 usec

 

The overhead of function call/return pluse the pule code stayed constant at 800 nsec. These result are on a 16MHz Sparkful Redboad.

 

Thanks again. Using the
Thanks again. Using the array seems a clear win.

I’ve thought of using two variable per encoder, countLeft, newLeft, countRight, and newRight. I don’t know if the array reference is done by the compiler or at runtime. It should be done by the compiler, but that’s just an assumption.

But that way I can have newLeft be an int8_t while that count is an int or even a long int.