Resurrecting this thread again, as my recent need for one led me to do some useful stuff…
The main issue with a lot of these routines is that they are susceptible to switch contact bounce, and can give spurious output. I looked for a lot and didn’t come up with much (just a lot of routines with debounce code in them). My application required no bounce, and had to be as small as possible.
So I set about writing a routine from scratch (that also happens to use a state machine), and I’m pretty convinced that it’s quite effective. No contact bounce, very small code footprint, and handles 2 and 4 step encoders. It also supports interrupt driven mode as well as polled.
I have it as an Arduino library too ([see my blog)
Please give it a try:
/* Rotary encoder handler for arduino.
*
* Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3.
* Contact: bb@cactii.net
*
* Quick implementation of rotary encoder routine.
*
* More info: http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html
*
*/
// Half-step mode?
#define HALF_STEP
// Arduino pins the encoder is attached to. Attach the center to ground.
#define ROTARY_PIN1 2
#define ROTARY_PIN2 3
// define to enable weak pullups.
#define ENABLE_PULLUPS
#ifdef HALF_STEP
// Use the half-step state table (emits a code at 00 and 11)
const char ttable[6][4] = {
{0x3 , 0x2, 0x1, 0x0}, {0x83, 0x0, 0x1, 0x0},
{0x43, 0x2, 0x0, 0x0}, {0x3 , 0x5, 0x4, 0x0},
{0x3 , 0x3, 0x4, 0x40}, {0x3 , 0x5, 0x3, 0x80}
};
#else
// Use the full-step state table (emits a code at 00 only)
const char ttable[7][4] = {
{0x0, 0x2, 0x4, 0x0}, {0x3, 0x0, 0x1, 0x40},
{0x3, 0x2, 0x0, 0x0}, {0x3, 0x2, 0x1, 0x0},
{0x6, 0x0, 0x4, 0x0}, {0x6, 0x5, 0x0, 0x80},
{0x6, 0x5, 0x4, 0x0},
};
#endif
volatile char state = 0;
/* Call this once in setup(). */
void rotary_init() {
pinMode(ROTARY_PIN1, INPUT);
pinMode(ROTARY_PIN2, INPUT);
#ifdef ENABLE_PULLUPS
digitalWrite(ROTARY_PIN1, HIGH);
digitalWrite(ROTARY_PIN2, HIGH);
#endif
}
/* Read input pins and process for events. Call this either from a
* loop or an interrupt (eg pin change or timer).
*
* Returns 0 on no event, otherwise 0x80 or 0x40 depending on the direction.
*/
char rotary_process() {
char pinstate = (digitalRead(ROTARY_PIN2) << 1) | digitalRead(ROTARY_PIN1);
state = ttable[state & 0xf][pinstate];
return (state & 0xc0);
}
void setup() {
Serial.begin(9600);
rotary_init();
}
void loop() {
char result = rotary_process();
if (result)
Serial.println(result == 0x40 ? "LEFT" : "RIGHT");
}
](Buxtronix: Rotary encoders, done properly)