rotary encoders mechanical - web viewthe arduino uno has 2 pins that are able to detect interrupt...

9
Rotary Encoders Mechanical Objective DC motors are useful devices. They have significantly more torque than stepper motors. Even when geared down, they are much faster. The only real draw back with DC motors is you never really know just where they are. If you need to turn exactly 2.5 revolutions, then stop, back up a bit then turn forward again, this is very difficult to get right with just timing. The solution is the encoder, a small device that mounts on the shaft of a DC motor and gives an output that corresponds to the number of degrees that the motor has traveled. There are numerous types of these devices, the most accurate are optical and can give angle measurements with 0.1 degree accuracy. These are also the more expensive devices. This document looks at the mechanical encoders with lower accuracy that sell for less than $1. The Mechanical Encoder These simple devices usually have two switches that open and close as the shaft turns. The shaft of the encoder is mechanically connected to the shaft of the motor. Care must be taken when mounting the encoder. The shafts must be collinear and concentric. If not, there will be friction, and usually damage. In the Bourns PEC12R devices there are 3 pins. The center pin is common to both switches and is usually connected to ground. The Bourns PEC12R is a low resolution device. It comes in 2 flavors, 12 steps per revolution and 24 steps per revolution. This means that each switch opens and closes 12 or 24 times in each full turn of the shaft. The switches are mounted to the shaft in such a way that first one switch (the A switch) closes then the other (the B switch closes). By noting which of the switches closes first, one can tell if the shaft is rotating clockwise or counter clockwise.

Upload: trinhkien

Post on 13-Feb-2018

217 views

Category:

Documents


2 download

TRANSCRIPT

Rotary Encoders MechanicalObjectiveDC motors are useful devices. They have significantly more torque than stepper motors. Even when geared down, they are much faster. The only real draw back with DC motors is you never really know just where they are. If you need to turn exactly 2.5 revolutions, then stop, back up a bit then turn forward again, this is very difficult to get right with just timing.

The solution is the encoder, a small device that mounts on the shaft of a DC motor and gives an output that corresponds to the number of degrees that the motor has traveled. There are numerous types of these devices, the most accurate are optical and can give angle measurements with 0.1 degree accuracy. These are also the more expensive devices. This document looks at the mechanical encoders with lower accuracy that sell for less than $1.

The Mechanical EncoderThese simple devices usually have two switches that open and close as the shaft turns. The shaft

of the encoder is mechanically connected to the shaft of the motor. Care must be taken when mounting the encoder. The shafts must be collinear and concentric. If not, there will be friction, and usually damage.

In the Bourns PEC12R devices there are 3 pins. The center pin is common to both switches and is usually connected to ground. The Bourns PEC12R is a low resolution device. It comes in 2 flavors, 12 steps per revolution and 24 steps per revolution. This means that each switch opens and closes 12 or 24 times in each full

turn of the shaft. The switches are mounted to the shaft in such a way that first one switch (the A switch) closes then the other (the B switch closes). By noting which of the switches closes first, one can tell if the shaft is rotating clockwise or counter clockwise.

The Encoder SignalThe encoder is a couple switches. They are either open (∞ resistance) or closed (0 resistance). This is similar to working with push buttons. Remember the Arduino cannot measure changes in resistance. The change in resistance must be converted into a change in voltage.

A series resistor makes this possible. Consider the voltage at Point A in the circuit at the right. When the switch is open, no current can flow in the loop. The current through R1 is 0. Since V = IR for any resistor then the voltage change across R1 is 0 since 0*20K = 0. That means the voltage change across R1 is 0 so if the voltage on the Left of R1 is 5, and there is no change, then the voltage on the Right side of R1, namely the voltage at Point A must also be 5V, digital HIGH.

When the switch closes, the resistance between point A and ground is 0 so the voltage at point A must be the same as ground i.e. 0 Volts, or LOW.

If Point A is connect to an Arduino Pin set to be an input pin, we can read this pin with a digitalRead(Pin #); command and we will get either HIGH or LOW and the program can tell of the switch is closed or open.

Just as with the Push Button, we can use the internal resistors provided by the Arduino Processor to create this circuit. In this picture, we see the encoder connected to the Arduino. Pins 2 and 3 are input pins with their internal pullup resistors active. As the shaft turns, these voltage will go HIGH and LOW. Counting these tranistions will allow the program to know how far the shaft has turned. Noting which pin transitioned first will allow the program to know whether it is rotation clockwise (CW) or counter clockwise (CCW).

Consider the diagram at the Left. This shows how the voltage on the two pins will change as the shaft is rotated. The horizontal axis is time. If Signal A goes HIGH (switch opens) first, then Signal B, the shaft is rotating clockwise (CW). If Signal B goes HIGH then A the shaft is rotating counter clockwise (CCW).

It is not difficult to connect the Arduino to Encoder. Reading the signal and determining how far it has turned, or what the speed is can be a bit more difficult. The next section explores the code that will make it happen.

What is an InterruptAs with most things involving Arduino programs, there are multiple ways to do this. This write up is going to explain using the interrupt to detect the transitions. Interrupts and the Interrupt Service Routines (ISR) area powerful tool that are required in a number of programming operations.

Microprocessors execute programs one line at a time and it follows a sequence through the program. The second program line follows the first and so on. Flow control statements like if, for, while or switch can cause the sequenced to skip or repeat certain sections of the program. Function calls will cause the program to jump to a routine and return when that routine is completed.

Sometimes a program requirement demands that this well order set of operations needs to be interrupted. These interruptions are triggered by events outside the normal operation of the program. When the events that require the normal sequence be interrupted, a programmer might use and ISR.

An ISR is a special subroutine that is executed when some external event demands that normal the program sequence be interrupted and a special set of instructions be executed. The

programmer cannot predict when these events will happen, or what program instruction will be in progress.

This is exactly the situation we have with the encoder. The program needs to know when the A and B pulses occur but it cannot predict when that will happen. It can't just do nothing waiting for a transition, but it has to react when it does. This is an ideal time for an ISR, or more accurately 2 of them.

When an event, like the voltage on a pin transitioning from low to high, happens, the program needs to stop what it's doing and react.

Creating an ISRThe Arduino IDE makes it easy. The Uno processor has two pins that can be connected to external stimuli that will trigger an Interrupt Service Routine - pins 2 and 3. Creating an ISR requires two things -

1. it must be defined using the attachInterrupt() function.2. There must be a subroutine that executed when the event happens

The Arduino Uno has 2 pins that are able to detect interrupt causing events, pins 2 and 3. There are 2 interrupts that can be associated with these 2 pins, int0, and int1 respectively. The program tells the processor that Interrupts are going to be active with the attachInterrupt() function. This function requires 3 parameters inside the parenthesis.

Interrupt number - 0 if the event is going to happen to pin 2 or 1 if it happens on pin 1.

ISR name - the name of the routine that will be executing when the event happens.

Triggering event - What causes the interrupt to happen - this can beRISING - the pin transitions from LOW to HIGH.FALLING - the pin transitions from HIGH to LOWCHANGE - the pin changes state either RISING or FALLINGLOW - The ISR executes whenever the pin is LOW

Let's look at a simple example:In this program the Arduino onboard led blinks as the wheel turns the encoder and the A output generates interrupt events.

In the setup() routine, the program defines pin 13 as an output, pin 2 as an input and activates the internal pull-up resistor. Line 6 in the program activates interrupt Int0 (the one for pin 2) and associates it

with the void ISR_Blink() routine. ISR_Blink() is a very simple routine. It toggles (whatever is now, make it different) pin 13, the on-board led.

In this picture, the encoder (lower right corner) is attached to the motor shaft. The 3 pins are connected to the Arduino pin 2, 3, and ground.

Rotating the wheel rotates the encoder shaft and the LED turn on and off.

The processor has 2 hardware interrupt pins, 2, and 3 and to fully utilize the encoder's information, both would be helpful.

The first program shows how to get the

motor direction. To do this, our configuration needs to read output A and output B. If B is low when a rising edge happens on A, the encoder is turning clockwise (CW). If B is high when a rising edge hits on A, the shaft is turning CCW. This first modification to the program includes a direction variable boolean CW = true. Note if CW is false, then the shaft is turning CCW.

This program initializes pin 3 as well as 2, The program reads pin 3 to determine the direction of rotation.

In the ISR_Blink routine if pin 3 is low, then the program sets the Boolean variable CW to false indicating that the shaft is turning counter Clockwise. If pin 3 is high, then CW is set to true.

Another thing to note in this program is the definition of the Boolean variable CW - Note the word volatile in front of

the type casting. volatile means the variable could change unexpectedly. This is important in the loop program. Normally a variable value only changes when the program changes it an then in

expected ways. Because an interrupt can happen any time, and the variable CW is set inside the ISR, the value of CW might change without the loop function changing it.

Another valuable bit of information is the number of steps that have transpired. This will tell us how far the wheel has turned, that is how many steps have happened. This information can be used to get the distance traveled, and when used in conjunction with a millis() function call, can give us the speed of rotation.

The Only Negative is that It Doesn't WorkThis is very interesting, but it would be remiss not to mention "Bounce". All mechanical switches, when they open or close will bounce. The contact doesn't just "make contact". When a

contact is made it will make and break several times before settling to a secure connection. We are configured so interrupts occur when the switch opens.

This scope picture shows the bounce on a falling edge. This is when the switch closes. Note high voltage on the left indicates the switch is open. When it closes in the center of the screen, we see it bounce several time before setting to the closed state.

We can see bouncing is less on the opening contact, but still some noise happens that can cause the counter to increment more times than appropriate. There are several ways to reduce or eliminate bounce. The simplest is to ignore all interrupts that happen in quick succession.

Consider ISR_Blink() in this program. This routine will be called whenever there is a rising edge (transition from LOW to HIGH) on pin 2. If it has not been more than 6mS since the last such transition, it will be ignored.

Remember, millis() gives the number of milliseconds since the program started. On the last transition, the variable tm was set equal to millis(). At that time millis() - tm was 0, not greater than 5. Only when it has been at least 6 mS since the last transition will cnt increment.

From Experiment, it can be shown that this particular encoder produces 32 pulses per

revolution. In our code, the loop function counts the rising edges for a full 500mS then displays them.

The motor speed is controlled by the pulse width modulator pin 5 and 6. The operator can enter a number to set the pulse width and control the energy to the motor. 128 is the minimum to overcome the inertia to start the motor. At 255, the Serial monitor reads 30 pulses in 500 mS - or 60 pulses per second.

That means 60 pulses/second * 60 Seconds/minute / 32pulses / revolution = 112

rpm. At a 50% duty cycle (128 to pin 5) and we a reading of 13, or 26 pulses per second. Calculate the rpm for this speed

____________________________________

If the motor is running and the operator further reduces the pulse width to 90, the reading is 7, or 14 pulses per minute. Calculate the rpm at this rate.

____________________________________ Note, if the motor is stopped, at this duty cycle there is not enough power overcome the static friction and start is moving, but if it is running, it can be slowed to this value.

ConclusionsA simple encoder attached to the motor can be used to determine the motor speed, or the total angle of rotation using interrupts. External interrupts can trigger the execution of an ISR, in direct response to the event.

ISR are easy to implement with the Arduino and can provide useful information.

If you have any questions, please feel free to contact me

Adrian [email protected] 321 6985