Go Down

Topic: Rotary Encoder Tutorial port to Arduino Uno (Read 2775 times) previous topic - next topic

redcliffe

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



johnwasser

Code: [Select]

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:
Code: [Select]

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


For 3,4 change to
Code: [Select]

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


For A4,A5 change to:
Code: [Select]

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


Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

redcliffe

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

johnwasser

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.
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

redcliffe

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

johnwasser

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

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. 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

Code: [Select]


#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 )]);
}


Riva

Here is a excerpt of a program/project that used the sparkfun encoder. Should be easy to convert to your needs.
Code: [Select]
//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;
}

dhenry

Quote
Alternatively...


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

Code: [Select]

#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.

johnwasser


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.
Code: [Select]

#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:
Code: [Select]

#define ENC_PORT PINB

old_AB |= ( (ENC_PORT >> 2) & 0x03 );  //add current state
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Go Up