Introduction: Arduino Powered Rotary Encoders - I Made It at TechShop

As in some of my past instructables, arduino is all about control. The ability to control certain things like displays, servos, motors, and other electrical mechanical devices in a meaningful way. I've covered using potentiometers to determine position and how to drive 7 segmented led displays. This is continuation of that. This is how to directly control a position and display that position on a led display. A potentiometer might work, but it has large limitation. It has a set range of travel. Using a rotary encoder instead of a potentiometer we have almost no limit to how big a number can be set in the position. This might be helpful if you were trying to determine the position of a motor in rotations.

This also is an intro to interrupts. Interrupts are functions that interrupt the main function when something is triggered. Pressing reset on your computer is an interrupt. It allows for small, fast, or critical functions to work in the background.

This instructable uses shift registers to control the 7 segmented display, but they are covered in detail in another instructable. Visit that one for more info. https://www.instructables.com/id/Arduino-powered-7-seg-LED-display-using-Shift-Regi/

L
ike all my instructables, this one was made at TechShop.
http://www.techshop.ws

Step 1: Materials

You will need all the materials from this instructable:
https://www.instructables.com/id/Arduino-powered-7-seg-LED-display-using-Shift-Regi/

PLUS

- A quadrature rotary encoder. (This means that it has 2 outputs. You need two to determine direction)
Here is an example:
https://www.sparkfun.com/products/10932

Step 2: Schematic

This is exactly the same as before except for the addiction of the rotary encoder. The encoder has four connections; Vcc, ground, OutA, and OutB. 

OutA is the main pulse control and is what will trigger the interrupt. OutB functions as the direction pulse and indicates relative direction when used in conjunction with OutA.

What is going on is that both OutA and OutB are sending regularly spaced pulses but are out of phase with each other. This phase change is what allows the encoders to determine direction.

Look at the diagram of the pulses. The interrupt is set to trigger when OutA sends a Rising signal. The state of OutB is then read. If the state is High, then that means that it is turning clockwise. If the state is low, then the only way that could happen is if the pulses were being sent in the opposite direction or counter-clockwise.

Step 3: Code

The code for the encoders takes the code for the shift registers and replaces the counting loop with a set position between 0 and 99. There are a few important changes. One, the setup for the input of encoder is import. In setup, the pull-up resistors are turned on and an interrupt is attached to OutA.

Whenever a Rising signal is seen, it triggers the encoderPos function. This function reads OutB and either adds or subtracts one interval in the position. NOTE: By only adding and using mod, we can better control the range of the positon, thus limiting it from 0 to 99. AND the global variable must be identified as a volatile variable in order to work with interrupts.

The main loop then translates the number into shift register instructions.

//Shift Registers

#define LATCH 5
#define CLK 4
#define DATA 6
#define ENCA 2
#define ENCB 7

//This is the hex value of each number stored in an array by index num
byte digitOne[10]= {0x6F, 0x09, 0x73, 0x3B, 0x1D, 0x3E, 0x7C, 0x0B, 0x7F, 0x1F};
byte digitTwo[10]= {0x7B, 0x11, 0x67, 0x37, 0x1D, 0x3E, 0x7C, 0x13, 0x7F, 0x1F};

volatile unsigned int pos;

void setup(){

  //Shift Register Setup
  pinMode(LATCH, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(DATA, OUTPUT);

  //Encoder Setup
  pinMode(ENCA, INPUT);
  pinMode(ENCB, INPUT);
  digitalWrite(ENCA, HIGH);
  digitalWrite(ENCB, HIGH);

  attachInterrupt(0, encoderPos, RISING); 
}

void loop(){

  //get the individual digits
  byte ones=pos%10;
  byte tens=pos/10;

  //output to 7 seg
  digitalWrite(LATCH, LOW);
  shiftOut(DATA, CLK, MSBFIRST, ~digitTwo[tens]); // digitTwo
  shiftOut(DATA, CLK, MSBFIRST, ~digitOne[ones]); // digitOne
  digitalWrite(LATCH, HIGH);
}


void encoderPos(void){


  //Check B output for high or low
  //HIGH means clockwise
  //LOW means counter clockwise
  if(digitalRead(ENCB)){
    pos=(pos+1)%100;
  }
  else {
    pos=(pos+99)%100;
  }
}