Posted by
| Nick Gammon
Australia (23,057 posts) Bio
Forum Administrator |
Message
| I have been reading about rotary encoders recently, and just assumed that they looked like this:
After all, that is a rotary-dial phone, and presumably as you dial the numbers are encoded.
However I now realize that people are probably talking about these things:
These are rotary switches, which (unlike potentiometers) are not analogue, but digital. As you turn the knob pulses are generated by switching the center (C) pin to either of the outer pins (A and B) in such a way that you can tell which way it is being turned.
To test this, I wired up the switch like this:
The 0.1 uF (ceramic) capacitors shown in the schematic are not in the photo. They were suggested as a means of doing a "hardware debounce". I did not use them initially, and you may find that the encoder works without them. But if you get occasional jumps or wrong readings, the capacitors may very well help.
It was very simple, here is a photo of it being connected to an Arduino Uno:
In the photo a couple of diodes are visible. This was part of a scheme to try to use only one interrupt, but it didn't work perfectly.
Now the center (common, or C) pin is grounded. The outer two pins are connected to pins 2 and 3 of the Arduino, which are then pulled high by setting pull-up resistors on them. In the code that is done like this:
digitalWrite (2, HIGH);
digitalWrite (3, HIGH);
To detect when the knob is being turned we set up an interrupt handler which fires whenever either pin changes:
attachInterrupt (0, isr, CHANGE); // pin 2
attachInterrupt (1, isr, CHANGE); // pin 3
A bit of investigation shows that whenever you turn the encoder by one click you get one of these state changes (where H is high and L is low):
Forward direction: LH then HH, or HL then LL
Reverse direction: HL then HH, or LH then LL
So we can see that both pins the same (HH or LL) is the "resting" position. So we need to remember what preceded them. Thus we remember the previous switch state, and then when we get HH or LL, we look at what we had last time to see which way the switch must have been turned.
An extra test was needed to cater for switch bounce (sometimes we got HH followed by HH). I also added code to alter the "increment" amount. The idea was that if a human is turning the knob, if he or she turns it faster, then we increment by more than one. That is so if you want a big change, you turn the knob quickly, and if you want a small change, you turn it slowly.
The finished test program looks like this:
// Rotary encoder example.
// Author: Nick Gammon
// Date: 24th May 2011
// Wiring: Connect common pin of encoder to ground.
// Connect pins A and B (the outer ones) to pins 2 and 3 (which can generate interrupts)
volatile boolean fired = false;
volatile long rotaryCount = 0;
// Interrupt Service Routine
void isr ()
{
static boolean ready;
static unsigned long lastFiredTime;
static byte pinA, pinB;
// wait for main program to process it
if (fired)
return;
byte newPinA = digitalRead (2);
byte newPinB = digitalRead (3);
// Forward is: LH/HH or HL/LL
// Reverse is: HL/HH or LH/LL
// so we only record a turn on both the same (HH or LL)
if (newPinA == newPinB)
{
if (ready)
{
long increment = 1;
// if they turn the encoder faster, make the count go up more
// (use for humans, not for measuring ticks on a machine)
unsigned long now = millis ();
unsigned long interval = now - lastFiredTime;
lastFiredTime = now;
if (interval < 10)
increment = 5;
else if (interval < 20)
increment = 3;
else if (interval < 50)
increment = 2;
if (newPinA == HIGH) // must be HH now
{
if (pinA == LOW)
rotaryCount += increment;
else
rotaryCount -= increment;
}
else
{ // must be LL now
if (pinA == LOW)
rotaryCount -= increment;
else
rotaryCount += increment;
}
fired = true;
ready = false;
} // end of being ready
} // end of completed click
else
ready = true;
pinA = newPinA;
pinB = newPinB;
} // end of isr
void setup ()
{
digitalWrite (2, HIGH); // activate pull-up resistors
digitalWrite (3, HIGH);
attachInterrupt (0, isr, CHANGE); // pin 2
attachInterrupt (1, isr, CHANGE); // pin 3
Serial.begin (115200);
} // end of setup
void loop ()
{
if (fired)
{
long currentCount;
// protected access to the counter
noInterrupts ();
currentCount = rotaryCount;
interrupts ();
Serial.print ("Count = ");
Serial.println (currentCount);
fired = false;
} // end if fired
} // end of loop
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|