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/

ike all my instructables, this one was made at TechShop.

Step 1: Materials

You will need all the materials from this instructable:


- A quadrature rotary encoder. (This means that it has 2 outputs. You need two to determine direction)
Here is an example:

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
  else {
<p>nice, if I want to add sensors , button up ,button down,and reset can you explain code?</p>
I tried this code, but it seems pretty glitchy and only counts full cycles. It does, however, offer a good example for interrupts and grey code (which I had never used before). <br>This code counts half-cycles AND is much more stable. <br> <br>//rotary encoder example by jduffy54. <br>//this picks up rotations of a standard encoder on pins two and seven, and <br>//transmits the results over serial. <br> int r = 0; <br>float pos = 0; <br>float incr = 1; //the ammount you want it to count per step. <br>void setup(){ <br> Serial.begin(115200);//set a high communication speed. using 9600 baud <br> //will cause the program to freeze if the encoder moves too fast <br> pinMode(2, INPUT); <br> pinMode(7, INPUT); <br> digitalWrite(7, HIGH); <br> digitalWrite(2, HIGH);//sets the pins to HIGH if no signal is present <br> //if it is pulled low, the pins will register it. <br> <br> attachInterrupt(0, encoderPos, CHANGE); //go to the new void if pin two changes <br> //this allows us to double the resolution of the encoder. <br> <br> } <br> <br>void loop(){ <br> delay(0.1);//we don't need the loop to do anything, just the interrupts <br> } <br> <br> <br>void encoderPos(void){ //this is a state machine to determine the direction of the encoder. <br>//every half-cycle it counts, effectively doubling the encoder resolution. <br> r = 1; <br> if(digitalRead(2) == HIGH){ <br> if(digitalRead(7) == LOW){ <br> while(r == 1){ <br> if(digitalRead(2) == LOW){ <br> r = 0; <br> } else { <br> if(digitalRead(7) == HIGH){ <br> pos = pos + incr; <br> r = 0; <br> }}} <br> <br> <br> <br> } <br> else { <br> while(r == 1){ <br> if(digitalRead(2) == LOW){ <br> r = 0; <br> } else { <br> if(digitalRead(7) == LOW){ <br> pos = pos - incr; <br> r = 0; <br> }}} <br> } <br> Serial.print(pos); <br> }else{//same as the above code, but with High-Low reversed. <br> r = 1; <br> if(digitalRead(7) == HIGH){ <br> while(r == 1){ <br> if(digitalRead(2) == HIGH){ <br> r = 0; <br> } else { <br> if(digitalRead(7) == LOW){ <br> pos = pos + incr; <br> r = 0; <br> }}} <br> <br> <br> <br> } <br> else { <br> while(r == 1){ <br> if(digitalRead(2) == HIGH){ <br> r = 0; <br> } else { <br> if(digitalRead(7) == HIGH){ <br> pos = pos - incr; <br> r = 0; <br> }}} <br> } <br> Serial.print(pos); <br> }}
nice use of interrupts. are you planning on using this rotary encoder in any other projects?
Not currently planned for any more instructables, but I am currently using a few in some of my private projects. I particularly like the ones I'm using here though.

About This Instructable




More by madebyjoe:Arduino powered GLCD (Graphic LCD) - I made it at TechShop Arduino Bluetooth Serial Connections! - I made it at TechShop Android Tablet PS3 / Xbox360 Controller Mount - I made it at TechShop 
Add instructable to: