[TriEmbed] Fwd: Quadrature encoder help

Robert Gasiorowski rgresume at gmail.com
Tue Sep 13 17:41:04 CDT 2016


If it's a mechanical encoder, maybe your problems are caused by contact
bounce.

On Tue, Sep 13, 2016 at 6:38 PM, Scott Hall via TriEmbed <
triembed at triembed.org> wrote:

> I found the following snippet from this article:
> http://www.robotoid.com/appnotes/circuits-quad-encoding.html
> Reading the Encoder with an Arduino
>
> Let's assume you've built a quad encoder using the LS7184 IC, and
> connected it to an Arduino microcontroller development board, as shown in
> the schematic above. The clock output of the LS7184 provides a pulse for
> each complete step of the encoder wheel. If the wheel has 32 stripes, for
> example, you'll get 32 pulses per revolution.
>
> *RoboTip!*
> Check out the Mode pin on the LS7184. This sets whether the clock is
> multiplied by 1, 2, or 4. I'm showing it set at 1X, which provides one
> clock pulse per encoder step (if 1 revolution = 32 stripes, then 1
> revolution = 32 pulses). When pin 6 of the LS7184 is brought HIGH, the
> output is multiplied by 2, so you get 64 pulses per revolution. And when
> the pin is left floating the multiplication factor is 4X.
>
> You can use the Mode setting to increase the resolution of the encoder,
> letting you detect smaller changes in rotation. It's useful when using
> encoder discs with relatively few stripe transitions.
>
> This sketch for the Arduino reads the clock changes on pin D2, which
> provides for an external interrupt. Each time a clock pulse is detected on
> this pin, the Arduino immediately branches off to an interrupt routine --
> called *encoderIntA* in the sketch -- to process the pulse.
>
> Code within the interrupt routine examines the instantaneous state of the
> direction output of the LS7184, which is connected to Arduino pin D4. If
> the direction is HIGH, the encoder is assumed to be going forward, so the
> count goes up by one. If the direction is LOW, the encoder is assumed to be
> going in reverse, so the count goes down by one.
>
> For demonstration purposes the current count value is displayed in the
> Serial Monitor window. I use an *int*-size variable to hold the count.
> This variable can store positive and negative values in the range -32,768
> to +32,767. If the count goes above +32,767, it wraps around as a negative
> value.
>
> If you suspect this might ever happen in your application use a *long*-size
> variable instead.
> These store values from -2,147,483,648 to +2,147,483,647. That's a lot of
> wheel rotations!
>
> /*
>   Quadrature encoder example
>   Interface to LSI LS7184 quadrature clock converter
>   (USDigital part #LFLS7184)
>
>   Demonstrates reading quadrature encoder using Arduino interrupts.
>   Clock output is connected to pin 2 (Int0); direction output is
>     connected to pin 4.
>   Current position is displayed in Serial Monitor window.
> */
>
> const int clkPinA = 2;
> const int dirPinA = 4;
> volatile int encoderACount = 0;
> volatile boolean changeFlag = false;
>
> void setup() {
>   Serial.begin(9600);
>   pinMode(clkPinA, INPUT);
>   pinMode(dirPinA, INPUT);
>   attachInterrupt(0, encoderIntA, RISING);
> }
>
> void loop() {
>  if (changeFlag) {
>     changeFlag = false;
>     Serial.println(encoderACount);
>   }
> }
>
> void encoderIntA() {
>   if (digitalRead(dirPinA) == HIGH)
>     encoderACount++;
>   else
>     encoderACount--;
>   changeFlag = true;
> }
>
>
> This article, http://makeatronics.blogspot.com/2013/02/efficiently-
> reading-quadrature-with.html, has the following section in it:
>
> Using Only 1 Interrupt
>
> If you're using an arduino Mega, or the new Due, you have more external
> interrupts to work with, so if you want to hook up multiple encoders or you
> have interrupts dedicated to other hardware you're probably covered. But if
> you're working on an Uno or one of it's predecessors, you are limited to
> only 2 interrupts.
>
> It is possible to use this same look-up technique with only 1 interrupt
> per encoder. The trade off is that you will loose half of your resolution.
> In this case, you would hook up the other channel to a regular digital pin,
> and then rework your look-up table keeping in mind that you can only detect
> when one of the channels is changing.
>
>
> <http://3.bp.blogspot.com/-9WVs1ImbheQ/U9w53Hm10FI/AAAAAAAAOv4/H313k8qFmnw/s1600/quadrature_half_res.png>
>
> You might think I'm missing some information on the B channel, but
> remember that the microcontroller only sees when A changes, and reads B at
> that time. In the CW direction, when state 2 started A was high and B was
> low. When it gets to state 3, A is low and B is high. We have no
> information about when B changed from low to high, only that it is now
> high. That's why we loose half the resolution when using only one interrupt.
>
> Working out the entire look-up table:
>
>
> <http://4.bp.blogspot.com/-9vT8QVtvlug/U9w537vwMsI/AAAAAAAAOwE/fxmM6ao7DyI/s1600/quadrature_table_half_res.png>
>
> If you instead connected channel B to the interrupt, this table would be
> different. One way to tell if you've done the table correctly is that the
> direction column should always be symmetric about the middle. That is,
> entry 1 should equal entry 16, entry 2 = entry 15, entry 3 = entry 14, etc.
> Meeting this condition doesn't guarantee that you've done it right, but if
> you don't meet this condition I guarantee you've done it wrong.
>
> You will need to make some changes to the ISR as well. Line 6 in the ISR
> above assumed that the quadrature channels were hooked up the pins 2 and 3.
> For convenience in the code, I suggest you keep the two channels adjacent
> to each other whenever possible, i.e. pins 3 and 4 or pins 1 and 2. But
> remember the pins 0 and 1 are used for TX and RX.
>
> If you connect channel A to the interrupt on pin 3, and channel B to pin
> 4, the ISR becomes:
>
>
>    1. void encoder_isr() {
>    2.     static int8_t lookup_table[] = {0,0,0,-1,0,0,1,0,0,1,0,0,-1,0,0,0};
>    3.     static uint8_t enc_val = 0;
>    4.
>    5.     enc_val = enc_val << 2;
>    6.     enc_val = enc_val | ((PIND & 0b11000) >> 3)
>    7.
>    8.     enc_count = enc_count + lookup_table[enc_val & 0b1111];
>    9. }
>
>
>
> You can also see what was done here in developing this library:
>
> http://www.mathertel.de/Arduino/RotaryEncoderLibrary.aspx
>
>
> for reference: https://www.google.com/search?num=100&newwindow=1&safe=
> active&q=arduino+quadrature+encoder+code+single+interupt&
> oq=arduino+quadrature+encoder+code+single+interupt&gs_l=
> serp.3..0i71k1l8.0.0.0.959.0.0.0.0.0.0.0.0..0.0....0...1c..
> 64.serp..0.0.0.-xq6S5kokU4
>
> - sgh
>
> On Tue, Sep 13, 2016 at 12:33 AM, Rodney Radford via TriEmbed <
> triembed at triembed.org> wrote:
>
>> The 'best' way to solve any programming problem is to first look at it,
>> hit your head on the wall, post it for others to help, go to bed... and
>> then immediately realize the error and have to get up to correct it.
>>
>> Although I looked at this for over an hour before posting it, it was not
>> until I was trying to sleep that I realized why I was always seeing 0 or 1
>> - each transition would just bump it up, and then back down. In order to
>> count up or down, I was going to need to look at more than just two bits of
>> state.
>>
>> I started to code this, and did another google search - you know, the
>> same thing I did before I asked and came up empty?  Yeah, that thing... and
>> this time I found a great resource. While I did not need everything in the
>> library, I was able to pull out just the interrupt routine and that solved
>> my problem.
>>
>> The nice library I found was at: https://www.pjrc.com/teensy/td
>> _libs_Encoder.html
>>
>> The updated (and working) code is below:
>>
>> /*
>>  * Simple quadrature encoder implementation.  I only have one interrupt
>> available (as I
>>  *    am using the other for another purpose on this project), so I can
>> only watch one
>>  *    of the two encoder pins.  While this reduces the number of
>> transitions per revolution
>>  *    that is okay for this purpose.
>>  *
>>  * When running this, I only see a stream of 0 and 1 values for the
>> encoderValue - what am
>>  *    I doing wrong?
>>  */
>> #include "Arduino.h"
>>
>> int encoderA = 3;   // This one is an interrupt pin
>> int encoderB = 4;   // This is a standard (not interrupt) pin
>>
>> volatile long encoderValue = 0;
>>
>> void updateEncoder(void);
>>
>> /*
>>  * Setup the input pins and attach our interrupt vector
>>  */
>> void setup() {
>>    Serial.begin(9600);
>>
>>    // Set both quadrature channels to input and turn on pullup resistors
>>    pinMode(encoderA, INPUT);
>>    pinMode(encoderB, INPUT);
>>    digitalWrite(encoderA, HIGH);
>>    digitalWrite(encoderB, HIGH);
>>
>>    // invoke interrupt routine on each falling edge of encoderA
>>    attachInterrupt(1, updateEncoder, CHANGE);
>> }
>>
>>
>> /*
>>  * Simply print out the encoder value (at a reasonable rate)
>>  */
>> void loop() {
>>
>>   Serial.println(encoderValue);
>>   delay(100);
>> }
>>
>>
>> int state = 0;
>>
>> /**
>>  *               _______         _______
>>  *   Pin1 ______|       |_______|       |______ Pin1
>>  *           _______         _______         __
>>  *   Pin2 __|       |_______|       |_______|   Pin2
>>  *
>>  *    <-- negative count                    positive count -->
>>  *
>>  *    new     new     old     old
>>  *    pin2    pin1    pin2    pin1    Result
>>  *    ----    ----    ----    ----    ------
>>  *    0       0       0       0       no movement
>>  *    0       0       0       1       +1
>>  *    0       0       1       0       -1
>>  *    0       0       1       1       +2  (assume pin1 edges only)
>>  *    0       1       0       0       -1
>>  *    0       1       0       1       no movement
>>  *    0       1       1       0       -2  (assume pin1 edges only)
>>  *    0       1       1       1       +1
>>  *    1       0       0       0       +1
>>  *    1       0       0       1       -2  (assume pin1 edges only)
>>  *    1       0       1       0       no movement
>>  *    1       0       1       1       -1
>>  *    1       1       0       0       +2  (assume pin1 edges only)
>>  *    1       1       0       1       -1
>>  *    1       1       1       0       +1
>>  *    1       1       1       1       no movement
>>  *
>>  * Implementation based on Encoder library at:
>>  *     https://www.pjrc.com/teensy/td_libs_Encoder.html
>>  */
>> void updateEncoder(void) {
>>    uint8_t s = state & 3;
>>    if (digitalRead(encoderA)) s |= 4;
>>    if (digitalRead(encoderB)) s |= 8;
>>    switch (s) {
>>        case 0: case 5: case 10: case 15:
>>           break;
>>        case 1: case 7: case 8: case 14:
>>           encoderValue++;
>>           break;
>>        case 2: case 4: case 11: case 13:
>>           encoderValue--;
>>           break;
>>        case 3: case 12:
>>           encoderValue += 2;
>>           break;
>>        default:
>>           encoderValue -= 2;
>>           break;
>>        }
>>    state = (s >> 2);
>>
>> }
>>
>>
>> ---------- Forwarded message ----------
>> From: Rodney Radford <ncgadgetry at gmail.com>
>> Date: Mon, Sep 12, 2016 at 11:45 PM
>> Subject: Quadrature encoder help
>> To: TriEmbed Discussion <triembed at triembed.org>
>>
>>
>> I am adding a quadrature encoder to a project I am working on, but having
>> a problem understanding why this is not working.
>>
>> I have used quadrature encoders before, implementing them with 0, 1, and
>> 2 interrupt lines.  In this case, I only have one interrupt line available
>> so I am interrupting on one channel and checking the other.
>>
>> I know this is possible, but I have a bug in the code below and I can't
>> seem to find it.
>>
>> What am I missing?
>>
>>
>> /*
>>  * Simple quadrature encoder implementation.  I only have one interrupt
>> available (as I
>>  *    am using the other for another purpose on this project), so I can
>> only watch one
>>  *    of the two encoder pins.  While this reduces the number of
>> transitions per revolution
>>  *    that is okay for this purpose.
>>  *
>>  * When running this, I only see a stream of 0 and 1 values for the
>> encoderValue - what am
>>  *    I doing wrong?
>>  */
>> #include "Arduino.h"
>>
>> int encoderA = 3;   // This one is an interrupt pin
>> int encoderB = 4;   // This is a standard (not interrupt) pin
>>
>> volatile int  lastB = 0;
>> volatile long encoderValue = 0;
>>
>> void updateEncoder(void);
>>
>> /*
>>  * Setup the input pins and attach our interrupt vector
>>  */
>> void setup() {
>>    Serial.begin(9600);
>>
>>    // Set both quadrature channels to input and turn on pullup resistors
>>    pinMode(encoderA, INPUT);
>>    pinMode(encoderB, INPUT);
>>    digitalWrite(encoderA, HIGH);
>>    digitalWrite(encoderB, HIGH);
>>
>>    // invoke interrupt routine on each falling edge of encoderA
>>    attachInterrupt(1, updateEncoder, FALLING);
>> }
>>
>>
>> /*
>>  * Simply print out the encoder value (at a reasonable rate)
>>  */
>> void loop() {
>>
>>   Serial.println(encoderValue);
>>   delay(100);
>> }
>>
>>
>> /*
>>  * This interrupt routine is called on each falling edge of encoder
>> channel A. We
>>  *   look at the B edge to to determine if we are counting up, down, or
>> staying in
>>  *   the same location.
>>  * Note if we only looked at the current B (and not the previous B), we
>> would get
>>  *   duplicate up/down counts on every transition due to non-debounced
>> quadrature
>>  *   signals.
>>  */
>> void updateEncoder(void) {
>>
>>   // read the current encoderB value
>>   int newB = (digitalRead(encoderB) & 0x1);
>>
>>   // create the 'transition' variable - show both old and new states - if
>> 00 or 11
>>   //    this is a duplicate falling edge (not moved), but if 01 or 10, we
>> have moved
>>   int transition = (lastB << 1) | newB;
>>
>>   // are we counting up, down, or staying the same location?
>>   switch (transition) {
>>       // transitioned from a 0 -> 1 edge, so counting up
>>       case B01:
>>            encoderValue++;
>>            break;
>>       // transitioned from a 1 -> 0 edge, so counting down
>>       case B10:
>>            encoderValue--;
>>            break;
>>   }
>>
>>    // Save current encoderB value for next transition variable
>>    lastB = newB;
>> }
>>
>>
>> _______________________________________________
>> Triangle, NC Embedded Computing mailing list
>> TriEmbed at triembed.org
>> http://mail.triembed.org/mailman/listinfo/triembed_triembed.org
>> TriEmbed web site: http://TriEmbed.org
>>
>>
>
>
> --
> Scott G. Hall
> Raleigh, NC, USA
> scottghall1 at gmail.com
>
> _______________________________________________
> Triangle, NC Embedded Computing mailing list
> TriEmbed at triembed.org
> http://mail.triembed.org/mailman/listinfo/triembed_triembed.org
> TriEmbed web site: http://TriEmbed.org
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.triembed.org/pipermail/triembed_triembed.org/attachments/20160913/eb225728/attachment.htm>


More information about the TriEmbed mailing list