Professional Documents
Culture Documents
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 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:
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
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.
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.
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.
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:
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
@ 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:
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
myISR:
;ISR code here
@ INT_RETURN
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:
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
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
Related Instructables
http://www.instructables.com/id/How-to-interpret-the-direction-of-rotation-from-a-/
Comments
7 comments Add Comment
http://www.instructables.com/id/How-to-interpret-the-direction-of-rotation-from-a-/