If you look at the gray code that an encoder puts out, you can see an easy way to tell which way it is turning. Start by looking only at one pin. When its state changes, if the other pin is in the same state then the encoder is going one way and if the other pin is in the opposite state then the encoder is going the other way. If you are looking at state changes on the second pin then the same holds true but the directions are reversed. If that doesn't make sense yet then google "encoder gray code" and see if you can learn how the waveform works.
Here's a picture:
Now one more thing about encoders. As you spin them that waveform keeps moving, so you really need to check the state of the other line at the very instant that you notice the state change on one pin. So it is common to use an interrupt for that. If you don't know about those, stop here and google "Arduino Hardware Interrupt" and do some reading to learn what those are. The important thing is that on an UNO those are on pins 2 and 3 so we MUST use those pins.
So here is some code that illustrates the point well, but won't work for a reason I'll explain afterwards.
// Code compiles but is untested
// In reality it would be too slow
// But it is easier to understand the
// concept here with digitalRead than
// it is with diret port reads
// I have used the same code written
// with port reads many times to read
// encoders. It works.
// If your encoder gives four increments per "click"
// and you only want one, then remove one interrupt
// entirely and set the other to RISING or FALLING
// instead of CHANGE.
// The two pins from the encoder
const byte epinA = 2;
const byte epinB = 3;
int printInterval = 500;
volatile int encoderCount = 0;
void setup(){
Serial.begin(115200);
attachInterrupt(digitalPinToInterrupt(epinA), eintA, CHANGE);
attachInterrupt(digitalPinToInterrupt(epinB), eintB, CHANGE);
}
void loop(){
static unsigned long pt = millis();
unsigned long ct = millis();
int encoderRead = 0;
if(ct - pt >= printInterval){
cli();
encoderRead = encoderCount;
sei();
Serial.println(encoderRead);
}
}
/*
This is actually too slow for fast encoders.
Should use direct port reads instead of
digitalRead. But this illustrates the point.
if your encoder ends up going backwards
just switch the ++ and -- in both ISR's.
*/
void eintA(){
// if the pins read the same we're going one direction
// else we're going the other direction.
if(digitalRead(epinA) == digitalRead(epinB)){
encoderCount++;
}
else {
encoderCount--;
}
}
void eintB(){
// Same as above, except the directions are switched for
// the other pin.
if(digitalRead(epinB) == digitalRead(epinA)){
encoderCount--;
}
else {
encoderCount++;
}
}
The loop is just a regular Blink Without Delay style timing code that prints the encoder count to the screen once every so often. The important bit is the cli() and sei() which turn off and then back on the interrupts on either side of copying that reading to another variable. This is important because it takes more than one clock cycle to copy that value and we don't want the interrupt to trigger and change it in the middle of while we are reading it. (google "critical section" for more information on that)
Unfortunately that code will almost work. But I included it because what is about to happen is probably a little over your head and I want you to be able to see what is really going on in the next code. See, the problem is that digitalRead can be too slow to catch the state on an encoder that is moving very fast. We need a faster way to read a digital pin and this comes from direct port reads. Please take a little while to study that page. You don't really need to know all of it, you just need to get the basic idea that I am reading the hardware directly. This is what digitalRead does for you behind the scenes.
So now my code becomes this:
// The two pins from the encoder
const byte epinA = 2;
const byte epinB = 3;
int printInterval = 500;
volatile int encoderCount = 0;
void setup(){
Serial.begin(115200);
attachInterrupt(digitalPinToInterrupt(epinA), eintA, CHANGE);
attachInterrupt(digitalPinToInterrupt(epinB), eintB, CHANGE);
}
void loop(){
static unsigned long pt = millis();
unsigned long ct = millis();
int encoderRead = 0;
if(ct - pt >= printInterval){
cli();
encoderRead = encoderCount;
sei();
Serial.println(encoderRead);
}
}
/*
Port reads are way faster than digitalRead
if your encoder ends up going backwards
just switch the ++ and -- in both ISR's.
*/
void eintA(){
// if the pins read the same we're going one direction
// else we're going the other direction.
if(!!(PIND & 4) == !!(PIND & 8)){
encoderCount++;
}
else {
encoderCount--;
}
}
void eintB(){
// Same as above, except the directions are switched for
// the other pin.
if(!!(PIND & 8) == !!(PIND & 4)){
encoderCount--;
}
else {
encoderCount++;
}
}
The !! looks confusing, but all it is doing really is letting me treat a number as a boolean. !someNumber is true if the number is 0 and false if the number is not 0. An extra ! gets me back to true for numbers that aren't 0 and false for 0. Basically, it converts any number that isn't 0 into a 1. So my bitmath would result in 4 or 8 for the two pins which obviously isn't equal. But by using !! I can say !!8 == !!4. If that's still confusing then just take my word for it that it works.
So now I have a counter that will count up or down depending on which way I turn my encoder.
Look now, I gave you a lot of links here and things to google. That whole thing about you not learning anything is about to change. I expect this might take a while for you to study through, maybe even a day or two. If you will, then you will learn a lot more than just how to work an encoder. Take your time and when you get how this is working then we will put it together with your display to make a menu.
PLEASE, if you don't understand some part of this ask questions, but ask specific questions. I'm not going to like "I just don't get it". If that is the case then you need to keep at the things I told you to read or google until you do start to get it.