Rotary Encoder Tutorial port to Arduino Uno

I'm using this tutorial to try to program my Arduino Uno to read a rotary encoder: http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino

The difference is he is on a Atmega168 platform where the Uno is Atmega328. I have the A side of my encoder in A5 and the B side in A4, but have tried them reversed too. I've adjusted the code thusly:

define ENC_A A5

define ENC_B A4

define ENC_PORT PINC

I haven't made any changes in the read_encoder function though. I suspect the hex values should be different?

At this poing I get the following output:

Starting Counter value: 0 Counter value: 1

Then nothing else, no matter how I turn the knob. Any suggestions? Thanks,

David

 old_AB |= ( ENC_PORT & 0x03 );  //add current state

One thing they forgot to mention is that the two pins MUST be the bottom two bits of the port.

Your choices are: PIND 0,1 (Serial port, probably not a good choice) PINB 8,9 (Probably a good choice) PINC A0,A1 (The pins used in the example)

Alternatively you can select other pairs of adjacent pins with a shift before the mask:

For 2,3 change the code to:

 old_AB |= ( (PIND >> 2) & 0x03 );  //add current state

For 3,4 change to

 old_AB |= ( (PIND >> 3) & 0x03 );  //add current state

For A4,A5 change to:

 old_AB |= ( (PINC >> 4) & 0x03 );  //add current state

Thanks. I've still got dramas though. I've decided to go with A0 and A1. At the moment though I can only ever get the encoder to change values if I'm touching the metal housing with my hand. I'm tried it with 5V common and swapping A and B to see if I've got them right.

Should there be pullup resistors in the circuit(I saw them mentioned in the tutorial, but no details). Also should the common be 5V or GND? Thanks,

David

For an optical encoder you would have four pins: +5, Ground, A and B.

Is this a mechanical encoder with only three terminals? In that case you could connect the common to Ground and use pull-up resistors (either the built-in ones or external fro the input pin to +5). Alternatively you can connect the common to +5 and use pull-dowm resistors from the input pins to Ground. Without pull-up or pull-down resistors the switches are switching between +5 and floating or Ground and floating. When left floating the input could read HIGH or LOW randomly.

Yeah it's a mechanical encoder, specifically this one: http://www.sparkfun.com/products/10596

I guess I'm a bit unclear on the idea of pullup and pulldown resistors and the need for them. I don't quite get why I/O lines float, or why it's suggested to use 220k resistors for such purpose in some of the tutorials. Some tutorials don't even seem to use them. Can you point me to a good reference or what I should be looking up? Thanks,

David

http://arduino.cc/it/Tutorial/Button

As per your suggestion, I was trying to use pins 10 &11 with the following change: “old_AB |= ( (PIND >> 2) & 0x03 );”
but had no luck. I am still only able to read pin 1 and 2 on all 3 ports, so I was wondering what other change might need to be made?
THanks in advance, this was really helpful thread

#define ENC_A 10
#define ENC_B 11
#define ENC_PORT PINB

//see the website for changing this
 
void setup()
{
  /* Setup encoder pins as inputs */
  pinMode(ENC_A, INPUT);
  digitalWrite(ENC_A, HIGH);
  pinMode(ENC_B, INPUT);
  digitalWrite(ENC_B, HIGH);
  Serial.begin (115200);
  Serial.println("Start");
}
 
void loop()
{
 static uint8_t counter = 0;      //this variable will be changed by encoder input
 int8_t tmpdata;
 /**/
  tmpdata = read_encoder();
  if( tmpdata ) {
    Serial.print("Counter value: ");
    Serial.println(counter, DEC);
    counter += tmpdata;
  }
}
 
/* returns change in encoder state (-1,0,1) */
int8_t read_encoder()
{
  static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
  static uint8_t old_AB = 0;
  /**/
  old_AB <<= 2;                   //remember previous state
old_AB |= ( (PIND >> 2) & 0x03 );  //add current state
uint8_t new_AB=old_AB >> 4;
  return ( enc_states[( new_AB & 0x0f )]);
}

Here is a excerpt of a program/project that used the sparkfun encoder. Should be easy to convert to your needs.

//Constants
const int encButton = 5;                         //Rotary Encoder button pin (11)
const int encoder0PinA = 6;                      //Rotary Encoder A pin (12)
const int encoder0PinB = 7;                      //Rotary Encoder B pin (13)
const long debounceDelay = 30;                   //Button debounce time


// Button = pin number to read
int checkButton(int Button) {
  int buttonState = digitalRead(Button);          // Read button
  if (buttonState == HIGH) {                      // If button pressed then wait a bit to allow for debounce
    long Time = millis(); 
    while ((millis() - Time) < debounceDelay) {    
    }
    buttonState = digitalRead(Button);            // Read button again
    return buttonState;                           // Return button state
  }
  else {                                          //Button not pressed so no need to wait for bebounce
    return LOW;
  }
}

int readEncoder() { 
  static int encoder0PinALast = LOW;
  int eDir = 0;
  int n = digitalRead(encoder0PinA);
  if ((encoder0PinALast == LOW) && (n == HIGH)) {
    if (digitalRead(encoder0PinB) == LOW) {
      eDir = -1;
      //Serial.print("-1");
    } 
    else {
      eDir = 1;
      //Serial.print("1");
    }
  } 
  encoder0PinALast = n;
  //Serial.println(eDir);
  return eDir;
}

Alternatively…

There are more portable ways to do it, particularly in arduino:

#define PIN_A  5  //encoder pin a
#define PIN_B 6 //encoder pin b

old_AB |= ((digitalRead(PIN_B))?(1<<1):0) | ((digitalRead(PIN_A)?(1<<0):0);  //( ENC_PORT & 0x03 );  //add current state

When you move pins, you just need to redefine them and compile.

gabriella: As per your suggestion, I was trying to use pins 10 &11 with the following change: "old_AB |= ( (PIND >> 2) & 0x03 );" but had no luck.

#define ENC_PORT PINB

old_AB |= ( (PIND >> 2) & 0x03 );  //add current state

You have to use the PINB register for pins 10 and 11. Try it this way:

#define ENC_PORT PINB

old_AB |= ( (ENC_PORT >> 2) & 0x03 );  //add current state