You are on page 1of 8

Home Sign Up!

Browse Community Submit


All Art Craft Food Games Green Home Kids Life Music Offbeat Outdoors Pets Photo Ride Science Tech

How to interpret the direction of rotation from a digital rotary switch with a PIC
by hw640 on August 25, 2009

Table of Contents

How to interpret the direction of rotation from a digital rotary switch with a PIC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Intro: How to interpret the direction of rotation from a digital rotary switch with a PIC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

Step 1: Parts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

Step 2: Hardware Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

Step 3: Hardware Theory of Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

Step 4: Software Theory of Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

Step 5: Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

File Downloads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

Related Instructables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

http://www.instructables.com/id/How-to-interpret-the-direction-of-rotation-from-a-/
Intro: How to interpret the direction of rotation from a digital rotary switch with a PIC
The objective for this Instructable is to illustrate how to interface a digital (quadrature coded) rotary switch with a microcontroller. Don't worry, I'll explain what quadrature
coded means for us. This interface and the accompanying software will allow the microcontroller to recognize the direction of rotation for each move from one detent to
another.

I recently used this type of switch in a microcontroller project that required a pressure set point to be entered using a knob with 16 detents instead of up/down buttons.
The idea was to allow the user to "dial in" the desired pressure. As a result, we had to develop a software routine to get the position information from the switch and
deduce the rotation direction in order to increment or decrement the pressure set point for the main system.

In this Instructable, I'll cover the physical interface to the microcontroller, the theory of operation for the rotary switch, the theory of operation for the software as well as
the deduction routine. Finally, I'll show you my application of the deduction routine. As we progress, I'll try to keep things somewhat generic so that the idea can be
applied on as many platforms as possible but I'll also share what I did so you can see a specific application.

Image Notes
1. Final project photo: the knob with the rotary switch.

Step 1: Parts
In order to implement this, you'll need:

A rotary switch (quadrature coded)


Pull up resistors
Suitable microcontroller platform

For my project, I used a Grayhill 61C22-01-04-02 optical encoder. The data sheet for the rotary switch calls for 8.2k ohm pull up resistors on the two data lines coming
from the switch. You'll want to check the data sheet for the encoder you opt to use. The rotary switch I used can also be ordered with an axial push button switch. It's a
useful feature for committing selections that have been dialed in, etc. but I will not be discussing its interface here. I have a "suitable microcontroller platform" listed
because (I think) this can be implemented on more than one platform. I have seen a lot of people using other microcontrollers for Instructables so I want to show the
general approach as well. I wrote all the code in PIC Basic Pro for use with a Microchip PIC16F877A. Really, the key thing that you need on the microcontroller is the
ability to interrupt when there is a logic change on either of two pins. On the PIC16F877A, this is called the PORTB change interrupt. There may be other names for it on
other controllers. This microcontroller interrupt feature is part of what makes this implementation so elegant.

http://www.instructables.com/id/How-to-interpret-the-direction-of-rotation-from-a-/
Image Notes
1. Generic photo of Microchip 40 pin DIP package. This is roughly what the PIC16F877A looks like. Photo credit: Digi-Key
2. Optical rotary encoder from Grayhill. Photo credit: Digi-Key

Step 2: Hardware Interface


A "simple" solution would be to have a "single pole-16 throw" switch with 16 connections to the microcontroller. Each switch output would then be tied to a pin on the
microcontroller so that every dial position can be checked by the microcontroller. This is an excessive use of I/O pins. Things get even worse if we want more than 16
positions (detents) available to us on the switch. Each extra position on the switch would require an extra input to the microcontroller. This quickly becomes a very
inefficient use of inputs on a microcontroller.

Enter the beauty of the rotary switch.

The rotary switch has only two outputs to the microcontroller listed as A and B on the data sheet. There are only four possible logic levels that these lines can take: AB =
00, 01, 10 and 11. This greatly reduces the number of input lines you must use in connecting the switch to the microcontroller. So, we've cut the number of input lines
down to just two. Now what? It seems like we really need 16 different states but this new switch has only four. Have we shot ourselves in the foot? Nope. Read on. We'll
cover a little bit of the theory behind the rotary switch operation to explain.

Image Notes
1. Don't worry about all this stuff, it's inside the switch.
2. 8.2k ohm pull up resistors
3. Schematic is from Grayhill rotary switch data sheet.
4. These are the A and B outputs from the switch.

Step 3: Hardware Theory of Operation


Rotation direction sensing is possible using the aforementioned "single pole-16 throw" switch but it uses up a lot of inputs on the microcontroller. Using the rotary switch
reduces the number of inputs to the microcontroller but now we need to interpret the signals coming from the switch and translate those to a rotation direction.

I mentioned earlier that the switch was quadrature coded. This is also one of the key elegances in this solution. This means that there is a 2-bit code the switch gives that
corresponds to the position of the switch. You might be thinking: "If there is a two bit input to the microcontroller, how do we represent all 16 positions?" That's a good
question. We don't represent them all. We just need to know the relative positions of the knob so we can determine the direction of rotation. The absolute position of the
knob is irrelevant. For clockwise rotation, the code that the switch gives repeats every four detents and is grey coded. Grey coded means that there is only one bit change
for every position change. Instead of the AB input counting up for clockwise rotation in binary like this: 00, 01, 10, 11, it changes like this: 00, 10, 11, 01. Notice that for
the latter pattern, there is only one input changing between sets. The counterclockwise values for the AB input to the microcontroller will look like this: 00, 01, 11, 10. This
is simply the reverse of the clockwise pattern with AB = 00 listed first.

Take a look at the diagrams for a more visual explanation.

http://www.instructables.com/id/How-to-interpret-the-direction-of-rotation-from-a-/
Image Notes
1. From the Grayhill data sheet for the rotary switch.

Step 4: Software Theory of Operation


The routine that deduces the rotation direction is interrupt driven. The microcontroller that you select needs to be able to interrupt any time there is a change on either of
(at least) two pins when the interrupt is enabled. This is called the PORTB change interrupt on the PIC16F877A. Anytime the switch is rotated, the microcontroller will be
interrupted and the program execution will be sent to the Interrupt Service Routine (ISR). The ISR will quickly figure out which way the switch was rotated, set a flag
appropriately and quickly return to the main program. We need this to happen quickly in case the user rotates the switch very fast.

We know the grey coded AB pattern repeats every four positions so if we make the routine work for transitions between those four positions it'll work for all of the others.
Notice that in one four position cycle, there are four edges. A rising edge and a falling edge for the A input as well as the B input. The microprocessor will be interrupted
each time there is an edge which means that the microcontroller will be interrupted any time the knob is turned. As a result, the ISR needs to figure out which way the
knob was turned. To help us figure out how to do this, we turn to the waveform for clockwise rotation.

Notice that any time A has an edge, its new value is always different from that of B. When the knob goes from position 1 to 2, A transitions from logic-0 to logic-1. B is still
0 for this transition and does not match the new value of A. When the knob goes from position 3 to 4, A has a falling edge while B remains at logic-1. Notice again, that B
and the new value of A are different. Right now, we can see that any time A causes the interrupt during clockwise rotation, its new value is different from that of B. Let's
check B to see what happens. B has a rising edge when the switch transitions from position 2 to 3. Here, the new value of B is the same as A. Looking at the last
remaining edge for clockwise rotation, B has a falling edge moving from position 4 to 5. (Position 5 is the same as position 1.) The new value of B is the same as A here
as well! We can now make some deductions! If A causes the interrupt and the new value of A is different from that of B, the rotation was clockwise. In addition, if B
causes the interrupt and the new value of B is the same as A, then the rotation was clockwise.

Let's quickly examine the case of counterclockwise rotation. Just like clockwise rotation, counterclockwise rotation will cause four interrupts in one cycle: two for input A
and two for input B. Input A has a rising edge when the knob moves from position 4 to 3 and a falling edge moving from position 2 to 1. When the knob moves from
position 4 to 3, the new value of A is the same as the value of B. Notice that when A moves from position 2 to 1 its new value is the same as that of B as well. Now, we
can see that when A causes the interrupt and its new value matches that of B the rotation was counterclockwise . Quickly, we'll look at input B to verify everything. B will
cause an interrupt when the knob moves from position 5 (which is the same as 1) to 4 and when the knob moves from position 3 to 2. In both of these cases, the new

http://www.instructables.com/id/How-to-interpret-the-direction-of-rotation-from-a-/
value of B does not match the existing value of A which is the opposite of the cases when B causes the interrupt for clockwise rotation. This is good news. Everything
checks out like it should.

To summarize, if A causes the interrupt and its new value does not match the value of B or if B causes the interrupt and the new value of B matches the value of A we
know there was clockwise rotation. We can check the other cases for counterclockwise rotation in software or we can assume that because it wasn't clockwise rotation it
was counterclockwise. My routine simply made the assumption.

Image Notes
1. Waveform is from Grayhill data sheet for the rotary switch.
2. A transition on the A input causes an interrupt here.
3. A transition on the B input causes an interrupt here.
4. A transition on the A input also causes an interrupt here.
5. A transition on the B input also causes an interrupt here.

Step 5: Software
I did not use the built in interrupts in PIC Basic Pro. I used a couple of files that I included in my code from Darrel Taylor to drive the routine. This is where a huge credit to
Darrel belongs! The files are free. Just visit his website for more information, other applications and to download the files. You can skip this part if you aren't using a PIC
with Darrel Taylor interrupts. Just set up the interrupts as necessary on the platform you're using.

To get the Darrel Taylor (DT) interrupts set up there are two things to do:

1.) Include the DT_INTS-14.bas and ReEnterPBP.bas files in your code.

2.) Copy and paste this into your code.

ASM
INT_LIST macro ;IntSource, Label, Type, ResetFlag?
INT_Handler RBC_INT, _ISR, PBP, yes
endm
INT_CREATE
ENDASM

Insert tabs and spaces like the graphic at the end of the Instructable so you can see things a little easier in your code. You'll need to modify it slightly to fit your needs.
Under Label, replace ISR with the name of the subroutine that is your ISR. Don't forget the underscore! You need it!

To get the interrupts working, there are two more things to do:

1.) Write the ISR. You'll write this just like you were going to write a PBP subroutine except that you will need to insert @ INT_RETURN at the end of the subroutine
instead of RETURN. This will acknowledge the interrupt and return program execution to where it left off in the main loop.

Inside the ISR, you need to clear the interrupt flag so your program does not get caught in a recursive interrupt. Simply reading PORTB is all that needs to be done to
clear the interrupt flag on the PIC16F877A. Each different microcontroller has a different way of clearing interrupt flags. Check the data sheet for your microcontroller.

2.) When you reach the point in your code that you want to enable the interrupt, use this line of code:

@ INT_ENABLE RBC_INT

When you want to disable the interrupt simply use:

@ INT_DISABLE RBC_INT

There's a lot of stuff packed into what I just covered so I'll summarize quickly. So far, your program should look something like this:

; Any needed set up or code

INCLUDE "DT_INTS-14.bas"
INCLUDE "ReEnterPBP.bas"

ASM
INT_LIST macro ;IntSource, Label, Type, ResetFlag?
INT_Handler RBC_INT, _myISR, PBP, yes
endm
INT_CREATE
ENDASM
http://www.instructables.com/id/How-to-interpret-the-direction-of-rotation-from-a-/
; Any other needed set up or code

@ INT_ENABLE RBC_INT
; Code that needs to know which way the knob is rotating
@ INT_DISABLE RBC_INT

; Other code

END ; End of program

myISR:
;ISR code here
@ INT_RETURN

(Interrupt Handler Set Up Table)

I think this is where anyone who is not using a PIC or DT interrupts can join in again. Now, we need to actually write the ISR so the microcontroller knows which way the
knob is rotating. Recall from the software theory section that we can deduce the direction of rotation if we know the input that caused the interrupt, its new value and the
value of the other input. Here's the pseudocode:

Read PORTB into a scratch variable to clear the interrupt flag


Check if A caused the interrupt.
If true,
Compare A and B.
Check if different, if different,
It was clockwise rotation
Else,
It was counterclockwise
Endif

Check if B caused the interrupt.


If true,
Compare A and B
Check if different, if same,
It was clockwise rotation
Else,
It was counterclockwise
Endif

Return from interrupt

How do we know if a change on A or B caused the interrupt? Discovering the new value of the changed input and the other (unchanged) input is easy because we can
read them inside the ISR. We need to know what the state of each one was before execution gets sent to the ISR. This happens in the main routine. The main routine sits
and waits for a byte variable that we called CWflag to be set to 1 or cleared to 0 by the ISR. After each acknowledged change of the knob or if there is no knob activity,
the variable is set to 5 to indicate an idle state. If the flag gets set or is cleared, the main routine immediately increments or decrements the set point pressure
appropriately based on the rotation and then sets the CWflag variable back to 5 because the knob is now idle again. As the main routine is checking the CWflag, it is also
documenting the state of the A and B rotary switch values. This is really simple and looks like this:

oldA = A
oldB = B

There really is nothing super fancy here. Just include those two lines at the beginning of the loop that checks the CWflag for rotation. We're just updating the logic values
of the inputs from the rotary knob inside the increment/decrement loop in the main routine so that we can see what input caused the interrupt when the ISR is executed.
Here is the ISR code:

ABchange:
scratch = PORTB ' Read PORTB to clear interrupt flag

' If A causes the interrupt, check B for direction of rotation


IF oldA != A THEN
' If A and B are different, it was clockwise rotation
IF A != B THEN
GOTO CW
' Otherwise, it was counter-clockwise rotation
ELSE
GOTO CCW
ENDIF
ENDIF

' If B causes the interrupt, check A for direction of rotation


IF oldB != B THEN
' If A and B are the same, it was clockwise rotation
IF A == B THEN
GOTO CW
' Otherwise, it was counter clockwise rotation
ELSE
GOTO CCW
ENDIF
ENDIF

CW:
CWflag = 1
@ INT_RETURN

CCW:
http://www.instructables.com/id/How-to-interpret-the-direction-of-rotation-from-a-/
CWflag = 0
@ INT_RETURN

I've included the ISR code in a AB_ISR.bas file because the tabs in the code aren't showing up the way they should.

Now, because the ISR has the old values for inputs A and B it can determine which input caused the interrupt, compare it to the other (unchanged) input and determine
the direction of rotation. All the main routine has to do is check the CWflag to see which direction the knob has turned (if it has) and increment or decrement a counter, set
point or whatever you like or need.

I hope this helps and hasn't been too confusing. This type of interface is especially useful if your system is already using interrupts as this is only one more interrupt to
add. Enjoy!

Image Notes
1. Don't change this
2. Your interrupt source. Use RBC_INT
3. This is the name of the subroutine that you want program execution sent to
when an interrupt happens. Use the name of your ISR with an underscore "_" in
front of it.
4. Don't change this.
5. No need to change this either.

File Downloads

AB_ISR.bas (671 bytes)


[NOTE: When saving, if you see .tmp as the file ext, rename it to 'AB_ISR.bas']

Related Instructables

Hack your servo


v2.00 #19; Add Simple manual
Using a Make an 10-bit control of Digital
Quadrature Amazing MIDI incremental / stepper motors Measuring Tape
How to build a Encoder with an Controller by absolute without a PIC or by
self navigating ATtiny 2313 and FuzzyWobble encoder PC by jhoffnun vahid_you2004
Robot by guiott an OLED feedback to
Display by Frank your hobby
Buss servo. by Antonb

http://www.instructables.com/id/How-to-interpret-the-direction-of-rotation-from-a-/
Comments
7 comments Add Comment

dennisjss says: Sep 1, 2009. 10:08 AM REPLY


I was curious about your selection of the encoder. It's obviously a high quality encoder at $25 a pop. Does is have a nicer feel than other encoders? Do you
think it's really worth the $25 as compared to cheaper alternatives?

hw640 says: Sep 2, 2009. 2:56 PM REPLY


This interface was part of a MUCH larger project for one of my system design classes. We had a project sponsor (a licensed PE) who I tasked with
selecting and purchasing the encoder since I had absolutely no experience in that area at the time. He said he had used a similar encoder in a previous
project and would check things out. He came back with that one. That said, I didn't actually select that particular encoder. What I had originally spec'd
was a digital knob with detents, a push button and no stops - unlike a potentiometer. This encoder fit the requirements. Through all the testing and
requirements verification, the encoder was very reliable so the quality does appear to be very high. The feel is just barely too "easy" for me (especially
with a knob attached) but it's not bad. I think there is a "high torque" model available for order if you're looking to put a little more effort into turning the
knob. I would imagine the cheaper encoders would do as good of a job especially for this application as it's relatively slow but I think this switch will shine
more in the long term reliability arena. I'm speculating though. Past this point, I think I may be out of my league. Hope this helps.

frollard says: Aug 27, 2009. 7:51 PM REPLY


Wonderful description - a bit advanced for me (I'm stuck with arduino for now). Good job on getting tagged on hackaday.

hw640 says: Aug 27, 2009. 9:11 PM REPLY


Thanks! I wasn't expecting that at all! If you ever get adventurous and start playing with interrupts, this is a good application for them so don't be too
scared: just apply the concepts.

frollard says: Aug 27, 2009. 11:12 PM REPLY


*to qualify that, microcontrollers are advanced for me - I can learn C, but learning a new language WHILE learning hardware is a bit overwhelming.

frollard says: Aug 27, 2009. 11:09 PM REPLY


I specifically have a job in mind for this - and plan to integrate it with my arduino, as soon as I get a hold of an encoder. Only one I have is one out of
an old ball mouse - with copper contacts!!! I have made a program that creates 4-bit (16 colour) pwm on all 8 pins of a shift register. Huge to
compute, but simple on electronics. Then I'll make a turntable with leds around it - the sine-wave of brightness will match the motion of the wheel -
and the whole thing will act as an input to the computer, like a scroll wheel.

calmconviction says: Aug 27, 2009. 5:30 PM REPLY


very well explained and illustrated. thanks hw

http://www.instructables.com/id/How-to-interpret-the-direction-of-rotation-from-a-/

You might also like