Reading Rotary Encoders

I've been trying to write code to read the value of a rotary encoder with not much success. This code seems to mostly work but it sometimes skips values or jumps. This is code for the ESP8266 using the Arduino libraries. Any suggestions for improvements? I'm actually likely to switch to using a Propeller for this but it seems like it should be possible to do it on the ESP8266 as well.
int a0 = -1;
int b0 = -1;
volatile int Count = 0;
void setupInterrupt(int pin)
{
attachInterrupt(digitalPinToInterrupt(pin), handleInterrupt, CHANGE);
}
int getCount()
{
return Count;
}
ICACHE_RAM_ATTR void handleInterrupt()
{
int a = digitalRead(ROTARY_CLOCK);
int b = digitalRead(ROTARY_DATA);
if (a != a0) {
a0 = a;
if (a == b) {
if (Count < 99)
++Count;
}
else {
if (Count > 0)
--Count;
}
}
}
Comments
Even on the propeller using straight code and not smart pins it will work but will sometimes go forward and then backwards.
Simple logic states that the first pin to go low is the direction with the second pin not meaning that much but to complete the cycle.
I was trying to built some debounce logic but was not very successful.
I will say that the quadrature smart pin works well. The only problem is I wanted to capture the push button as well.
Mike
/* * code to read a rotary encoder */ #include <propeller.h> #include "encoder.h" // require this many samples to match before accepting a new value #define DEBOUNCE_TARGET 2000 static _COGMEM unsigned int pin; static _COGMEM unsigned int nextValue; static _COGMEM unsigned int nextCount; static _COGMEM unsigned int tempValue; static _COGMEM unsigned int lastValue; static _COGMEM unsigned int thisValue; _NATIVE void main(volatile struct encoder_mailbox *m) { pin = m->pin; nextValue = -1; nextCount = 0; lastValue = 0; m->value = 0; for (;;) { tempValue = (INA >> pin) & 3; tempValue = ((tempValue & 1) << 1) | ((tempValue & 2) >> 1); // not sure why I needed to swap the pins if (tempValue == nextValue) { if (++nextCount >= DEBOUNCE_TARGET) { thisValue = nextValue; nextCount = 0; } } else { nextValue = tempValue; nextCount = 0; } switch ((lastValue << 2) | thisValue) { case 0b0000: // no movement case 0b0101: case 0b1111: case 0b1010: // nothing to do break; case 0b0001: // clockwise case 0b0111: case 0b1110: case 0b1000: if (m->value < m->maxValue) ++m->value; else if (m->wrap) m->value = m->minValue; break; case 0b0010: // counter-clockwise case 0b1011: case 0b1101: case 0b0100: if (m->value > m->minValue) --m->value; else if (m->wrap) m->value = m->maxValue; break; } lastValue = thisValue; } }
For switching it doesn't matter as long as you don't overload the output.
Mike
While running this I found the cheap encoder a lot less noisy; this is probably due to the fact that the P1 is running more slowly than the P2, and this serves as a bit of a digital filter. I ported my jm_ansi code to the P1 the other day, so you can output to an ANSI/VT100 terminal. I tested with Putty on Windows (see image).
I attempted to test with FlexProp but there was an issue. Even though I selected P1, FlexProp wanted to use jm_fullduplexserial.spin2 (P2 version) and this caused compile errors. I have attached an image from my system for @ersmith so he can check on his side.
Happy New Year.
The choice of language is independent of processor -- FlexProp can compile Spin2 code for the P1, as long as it doesn't use any PASM.
Generally speaking if your top level object is a .spin file then it should be checking for .spin files first for objects. I actually see that there are some cases where that doesn't happen correctly, so perhaps that's what you've run into. I'll try to improve that logic.
Thanks,
Eric