Shop OBEX P1 Docs P2 Docs Learn Events
Rotary Encoder Object? — Parallax Forums

Rotary Encoder Object?

Does anyone know of a Spin object that handles a rotary encoder? I tried the one in OBEX but it doesn't seem to track very well. I'm not sure it is handling contact bounce.

Comments

  • What kind of rotary encoder has contact bounce?
    Jim
  • This is the one I'm using: https://www.sparkfun.com/products/10982
  • Have you looked at the output on a scope?
    Jim
  • The spec sheet does show there is contact bounce. It recommends using a 2ms window for de-bouncing. From my calculations you shouldn't need to de-bounce as long as the encoder is turned at a rate less than 5 rotations/second (300 RPM). However, small rotations at a high speed may cause confusion. As long the encoder is slowly rotated it should work OK.
  • Dave Hein wrote: »
    The spec sheet does show there is contact bounce. It recommends using a 2ms window for de-bouncing. From my calculations you shouldn't need to de-bounce as long as the encoder is turned at a rate less than 5 rotations/second (300 RPM). However, small rotations at a high speed may cause confusion. As long the encoder is slowly rotated it should work OK.
    I find that the values returned skip at least one step per click even if I turn it slowly.
  • There is a quadrature encoder object in the Spin library. I have used it on several encoders but don't know how it will respond to lots of contact bounce.

    John Abshier
  • ErNaErNa Posts: 1,742
    The problem with those encoders is, they are not really quadrature encoders, but there is a A / B signal shifted only a few degrees. Therefor you never know, if you have counted odd or even. This is true for some items I tested. The solution I implemented is to have a counter internally and the output is shifted right, so divided by two. The resolution is reduced, but debouncing no longer is a matter
  • T ChapT Chap Posts: 4,198
    edited 2016-10-21 16:24
    The rotary that is found in the examples with propeller is rock solid. You must be using a defective part or bad wiring.

    ENS1J-B28-L00256L-ND try this on digikey
  • David Betz wrote: »
    I find that the values returned skip at least one step per click even if I turn it slowly.

    I've used that exact encoder several times. It's really hard to get the output to change by one. As ErNa suggests, just take this into account in your program.

    I don't know which quadrature encoder object you used but there's an object written by Kye (how usually does great stuff) which doesn't always count encoder transitions correctly.

  • Here is what I ended up with. I'm not sure if my debouncing code is correct but it does seem to work.
    /*
     * code to read a rotary encoder
     */
    
    #include <propeller.h>
    #include "encoder.h"
    
    struct encoder_mailbox {
        int pin;
        int minValue;
        int maxValue;
        volatile int value;
    };
    
    / require this many samples to match before accepting a new value
    #define DEBOUNCE_TARGET 2000
    
    static _COGMEM unsigned int pin;
    static _COGMEM int minValue;
    static _COGMEM int maxValue;
    
    static _COGMEM unsigned int nextValue;
    static _COGMEM unsigned int nextCount;
    static _COGMEM unsigned int tempValue;
    
    static _COGMEM unsigned int lastValue;
    static _COGMEM unsigned int thisValue;
    
    _NATIVE
    void main(volatile struct encoder_mailbox *m)
    {
        pin = m->pin;
        minValue = m->minValue;
        maxValue = m->maxValue;
    
        nextValue = -1;
        nextCount = 0;
    
        lastValue = 0;
        m->value = 0;
    
        for (;;) {
    
            tempValue = (INA >> pin) & 3;
    
            if (tempValue == nextValue) {
                if (++nextCount >= DEBOUNCE_TARGET) {
                    thisValue = nextValue;
                    nextCount = 0;
                }
            }
            else {
                nextValue = tempValue;
                nextCount = 0;
            }
    
            switch ((lastValue << 2) | thisValue) {
            case 0b0000:    // no movement
            case 0b0101:
            case 0b1111:
            case 0b1010:
                // nothing to do
                break;
            case 0b0001:    // clockwise
            case 0b0111:
            case 0b1110:
            case 0b1000:
                if (m->value < maxValue)
                    ++m->value;
                break;
            case 0b0010:    // counter-clockwise
            case 0b1011:
            case 0b1101:
            case 0b0100:
                if (m->value > minValue)
                    --m->value;
                break;
            }
    
            lastValue = thisValue;
        }
    }
    
  • David BetzDavid Betz Posts: 14,511
    edited 2016-10-21 16:32
    Sorry, when I said above that "it does seem to work" I didn't mean to imply that it always steps by exactly one. It still stutters sometimes with my encoder. However, it does seem to work well enough.
  • For this encoder, both terminals are open at the rest positions; turning the shaft causes a complete cycle of the A and B terminals. I'd think the only cases you need to check are 0b1111, 0b1110, and 0b1101.

    It should be a simple matter of waiting until A goes low and checking B. Turned CW if B is still high when A goes low, or CCW if B is already low when A goes low. Then debounce until A and B are steady high.
  • Mark_TMark_T Posts: 1,981
    edited 2016-10-21 17:30
    Debouncing is not an issue if you _correctly_ handle quadrature, it will see bounces and oscillation
    between two neighbouring states which is fine (this can happen with optical shaft encoders at
    low speeds for instance). You should be ignoring small changes (ie have a little hysteresis)
    when interpreting the count value in higher level code, as the switch may be teetering on the
    edge of making contact and oscillate anyway.
  • I had the same issues, the big detents in the movement don't help much either. The only thing I really liked about it was the RGB illumination.
  • ChrisGadd wrote: »
    For this encoder, both terminals are open at the rest positions; turning the shaft causes a complete cycle of the A and B terminals. I'd think the only cases you need to check are 0b1111, 0b1110, and 0b1101.

    It should be a simple matter of waiting until A goes low and checking B. Turned CW if B is still high when A goes low, or CCW if B is already low when A goes low. Then debounce until A and B are steady high.

    This is essentially the X4 encoding scheme that I recently implemented in order to use a cheap (in terms of quality and price) encoder as a tuner knob for an FM radio. Since I also monitored the switch, my pseudo-code looks like:

    WAITPNE EN0ABSW, EN0ABSW

    IF EN0SW = 0 THEN {seek next valid channel}
    ELSE
    WAITPEQ 0, EN0A
    IF EN0B = 1 THEN {INC channel}
    ELSE {DEC channnel}
    ENDIF
    ENDIF

    WAITPEQ EN0ABSW, EN0ABSW
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    I found an old SPIN object I wrote many years ago. It was a test and displays the current value on the PST.
    CON
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
      BAUD  = 115_200                                       ' PST Baud Rate
      
      Ao    = 0                                             ' A output from encoder
      Bo    = 1                                             ' B output from encoder
    
    
    VAR
    
      long    counter
    
    
    OBJ
    
      pst   : "Parallax Serial Terminal"
    
       
    PUB Main | newBits, oldBits, direction
    
      pst.Start(BAUD)                                       ' Set Parallax Serial Terminal to 115200 baud
    
      dira[Bo..Ao]~                                         ' Set encoder pins to inputs
    
      oldBits := ina[Bo..Ao]                                ' Pre-initialize oldBits (prevents non-zero starting count)
    
      pst.Clear
      
      repeat                                                ' Repeat forever
        pst.Home
        pst.Bin(newBits, 2)
        pst.NewLine
        pst.Dec(counter)
        pst.ClearEnd
        newBits := ina[Bo..Ao]                              ' Get current encoder bits
          if newBits <> oldBits                             ' See if bits have changed since last time
            direction := (newBits & %1) ^ oldBits >> 1      ' Calculate direction (Returns 0 or 1)
            case direction                                  ' Evaluate direction
              0 : counter--                                 ' Decrement counter
              1 : counter++                                 ' Increment counter
            oldBits := newBits                              ' Update oldBits
    
  • I've attached an encoder object that I wrote a while back for use in a pan-tilt-trolley camera control system. It worked well in that app. The object is designed for up to three encoders, but you don't have to use the second or third channels.
Sign In or Register to comment.