Shop OBEX P1 Docs P2 Docs Learn Events
Need help with rotary encoder — Parallax Forums

Need help with rotary encoder

I have an incremental quadrature encoder that I'm trying to use with a BS2. It's the mechanical type and has switch bounce. I've tried using the code I found on the Parallax website but apparently it works only with an optical encoder that's free of switch bounce. My encoder is causing erratic jumps in the counter. Here's the code:
' =========================================================================
'
' File...... Rotary Encoder.bs2
' Purpose... Read A Rotary/Optical Encoder
' Author.... Parallax, Inc.
' E-mail.... support@parallax.com
' Started...
' Updated... 07-15-2005
'
' {$STAMP BS2}
' {$PBASIC 2.5}
'
' =========================================================================


'
[ Program Description ]

' This code demonstrates reading a rotary/optical encoder using a BASIC
' Stamp 2 Module. This code will work on the BS2e, BS2sx, BS2p24, BS2p40,
' BS2pe and BS2px24.


'
[ I/O Definitions ]

' A Output Connects To P0
' B Output Connects To P1


'
[ Variables ]

oldBits VAR Nib ' Previous State Of I/O Pins 0 - 3
newBits VAR Nib ' Current State Of I/O Pins 0 - 3
direction VAR Bit ' Direction Encoder Is Turned
counter VAR Byte ' Counter (0-255)


'
[ Initialization ]

OUTS = %0000000000000000 ' Set All Output Pins Low
DIRS = %1111111111111100 ' I/O Pins 0 - 1 Inputs

newBits = INA

'
[ Program Code ]

Main:
DO
newBits = INA ' Get State Of I/O Pins 0 - 3
IF newBits <> oldBits THEN ' Have Bits Changed?
direction = newBits.BIT0 ^ oldBits.BIT1
counter = counter - 1 + (2 * direction)
DEBUG HOME, DEC3 counter ' Show New Counter On DEBUG Screen
oldBits = newBits ' Update oldBits
ENDIF
LOOP
STOP
I've tried a jillion ways to eliminate the effects of the switch bounce with no good results. I need help, please...

Comments

  • kwinnkwinn Posts: 8,697
    You need to debounce the inputs (have the inputs remain the the same for several reads) before you accept them as a valid input. How many stable reads depends on how bouncy the switches are and how fast the read loop is.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2016-11-05 20:49
    That code should still work with switch bounce. What is the output frequency of your encoder? Does the code only fail at high speeds?

    -Phil
  • Kwinn: I've tried to use the BUTTON command for debouncing but can't figure out how to get the debounced bits into the rest of the code.

    Phil: The encoder fails at ALL speeds. I've tried putting PAUSE commands inside the DO loop to try and slow down the polling of the incoming bits but can't get that to work either.
  • Something doesn't add up here. Properly-written quadrature decoders are immune from switch bounce. And from what I've been able to determine, Parallax's code is properly written. Where it -- or any -- quadrature decoder might fail is when both channels show a change at the same time. You can check this by inserting the following lines after the assignment to newbits:
    IF newbits ^ oldbits & 3 = 3 THEN DEBUG "Error.", CR
    

    -Phil
  • Phil -

    I inserted your line and I do get an occasional error. But only an occasional one. In the meantime the counter changes erratically - sometimes by a single step (which is what I want), but more often in steps of 3-5.

    I've looked at the encoder outputs using a digital storage scope and they look pretty much as expected. I have simple RC filters connected to both outputs that tend to kill some of the switch bounce but certainly not entirely.

    BTW, it might be worth mentioning that the quiescent outputs are both pulled high and that the quadrature pulses pull the outputs to ground (in sequence depending upon rotation direction) and thereafter they go back high. I guess this is normal behavior for this encoder type, but since this is my first experience with them I can't be certain...

    Rod
  • Does your encoder look something like this?

    encoder.jpg

    If not, can you post a photo?

    -Phil
    275 x 312 - 22K
  • I hooked mine up and am getting the same results as you -- mostly four increments/decrements per detent (mine has 24 per rev). Moreover, the scope traces are horrible. Even at low revs, the switch bounces on one channel sometime occur across an edge in the other channel. That's just not acceptable. And it seems to change both channels with each click.

    I don't know what to suggest, except to get a better encoder. I'm glad I didn't try incorporating one of these into a project!

    -Phil
  • OK, well at least I'm glad that I wasn't doing something stupid. Optical encoders that presumably don't have switch bounce are considerably more expensive. I'm still thinking there's a way to use these cheapos and that it's just a matter of firmware. Others are doing it. I'm just a baby programmer and apparently not skilled enough to figure out how to do this from scratch. As mentioned earlier, I tried to figure out how to use the BUTTON command but couldn't bring it off. Ohhh well...

    Thanks for spending your time and for your support..

    Rod
  • jmgjmg Posts: 15,175
    I hooked mine up and am getting the same results as you -- mostly four increments/decrements per detent (mine has 24 per rev). Moreover, the scope traces are horrible. Even at low revs, the switch bounces on one channel sometime occur across an edge in the other channel. That's just not acceptable. And it seems to change both channels with each click.

    I don't know what to suggest, except to get a better encoder. I'm glad I didn't try incorporating one of these into a project!

    -Phil

    Can you post a scope capture.
    Maybe this is an example of serious price engineering, and the classic Quadrature rules may need to be modified.
    If each detent changes both, and they idle HH, maybe the rule needs to be which edge occurs first ?

    As they are made in volumes, there has to be some Software solution to using them.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2016-11-06 04:55
    In my encoder, it goes through a complete cycle (four edges) between detents, hence the count of four -- usually. I have found that really stiff pull-ups (220R) help to minimize bouncing, but that does not eliminate the problem. The A and B edges are not symmetrically arranged, either. Sometimes they almost coincide. If this coincidence interval is shorter in time than the PBASIC loop period, you will get miscounts.

    A microcontroller programmed in machine code would be able to keep up with and track the glitchy signals. In this case the bouncing would not ruin the count except for very short-term up-down or down-up intervals that could easily be filtered out. I suspect the market for these encoders is tilted toward micros or PLDs with faster responses than a BASIC Stamp provides.

    -Phil
  • Attached is a scope shot of my encoder. From Phil's description of his, it sounds like mine has cleaner outputs. Mine look pretty much like in this shot - not all the time - but most of the time. I'm using a simple RC filter to help clean things up....10kohm and 0.02uF.

    Like Phil I'm getting multiple counts for each detent - usually 3-5. I want just one count. Note in the scope shot that the time during which the two output pulses are unequal is about 22 ms. I'm thinking the problem is NOT so much from switch bounce but it's the code I'm using. Depending upon the speed of the DO loop it can read multiple instances of "unequalness" during that 22 ms. It seems to me the problem could be resolved simply by adding an appropriate PAUSE command inside the loop, thereby slowing down the sample period. But I can't get that to work.

    Rod
    1000 x 750 - 250K
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2016-11-06 16:48
    rodbrink wrote:
    Depending upon the speed of the DO loop it can read multiple instances of "unequalness" during that 22 ms.
    No, that's not the case. The unequalness that changes the count applies to the current reading vs. the previous one. At the end of the loop, the previous reading is set to the current one, so you don't get multiple counts.

    The code is designed to change the count on each edge from either channel. Because there's a complete cycle of four edges between detents, you get four counts, nominally.

    -Phil
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2016-11-06 17:10
    Here's some code that works better with mine. Maybe it will be okay with yours:
    ' {$STAMP BS2}
    ' {$PBASIC 2.5}
    
    oldBits   VAR Nib ' Previous State Of I/O Pins 0 - 3
    newBits   VAR Nib ' Current State Of I/O Pins 0 - 3
    counter   VAR Byte ' Counter (0-255)
    dcount    VAR Nib  ' Delta count between detents
    
    oldBits = INA & 3
    DO
      newBits = INA & 3 ' Get State Of I/O Pins 0 - 1
      IF newBits <> oldBits THEN ' Have Bits Changed?
        dcount = dcount - 1 + (2 * (newBits.BIT0 ^ oldBits.BIT1))
        oldBits = newBits ' Update oldBits
        IF newBits = 3 AND dcount THEN
          IF dcount & 8 THEN counter = counter - 1 ELSE counter = counter + 1
          dcount = 0
          DEBUG HOME, DEC3 counter
        ENDIF
      ENDIF
    LOOP
    
    It takes advantage of the fact that on detent, both outputs are high. So it keeps track of the motion between detents. Then, when both outputs go high, it checks to see if any motion occurred, and in which direction. Only then does it increment or decrement the count. So you get just one count change per detent.

    -Phil
  • Phil -

    Your code is working great here. Thanks very much.

    You've taught me something I didn't know about Stamp. I didn't think that Stamp could read edges. I thought it could only poll its inputs and that it samples at some time period after acknowledging the IN command. Responding to edges is almost as good as having interrupts and of course we know that Stamp doesn't support interrupts.

    So, do I have it correct - that an IN command waits for an edge?
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2016-11-06 17:38
    rodbrink wrote:
    So, do I have it correct - that an IN command waits for an edge?
    No, the INA command just reads the state of the pins. Edges are detected by comparing the current state (newBits) to the previous state (saved in oldBits) to see if anything changed.

    -Phil
  • This is how I read encoders using PropBASIC. I don't know if this is of any use in PBASIC

    I use optical encoders and have never needed debounce but I have shown how this could be implemented at label A1B1
    'The encoder signals are referred to as Channel A and Channel B
    'There are four possible legal states:
    'A=1, B=1
    'A=0, B=0
    'A=1, B=0
    'A=0, B=1
    
    'So, on startup....
    'Jump to the appropriate loop
    If Chan_A = 1 And
        Chan_B = 1 Then
        Goto A1B1
    Endif
    
    If Chan_A = 0 And
        Chan_B = 0 Then
        Goto A0B0
    Endif
    
    If Chan_A = 1 And
        Chan_B = 0 Then
        Goto A1B0
    Endif
    
    If Chan_A = 0 And
        Chan_B = 1 Then
        Goto A0B1
    Endif
    
    'Keep looping until a channel changes state and then jump to the loop that 
    'caters to the new state.
      
     A1B1:
     If Chan_A = 0 Then
       'For debounce purposes, a delay could be inserted here, followed by another read of the input
       'If Chan_A = 0 Then  
       	Dec Counter
       	Wrlong HubCTR,Counter
       	Goto A0B1
        'Endif
     Elseif Chan_B = 0 Then
       'For debounce purposes, a delay could be inserted here, followed by another read of the input
       'If Chan_B = 0 Then   
    	Inc Counter
       	Wrlong HubCTR,Counter
       	Goto A1B0
        'Endif
     Endif
     Goto A1B1
     
     A0B0:
     If Chan_A = 1 Then
       Dec Counter
       Wrlong HubCTR,Counter
       Goto A1B0
     Elseif Chan_B = 1 Then
       Inc Counter
       Wrlong HubCTR,Counter
       Goto A0B1
     Endif
     Goto A0B0
     
     A1B0:
     If Chan_A = 0 Then
       Inc Counter
       Wrlong HubCTR,Counter
       Goto A0B0
     Elseif Chan_B = 1 Then
       Dec Counter
       Wrlong HubCTR,Counter
       Goto A1B1
     Endif
     Goto A1B0
     
     A0B1:
     If Chan_A = 1 Then
       Inc Counter
       Wrlong HubCTR,Counter
       Goto A1B1
     Elseif Chan_B = 0 Then
       Dec Counter
       Wrlong HubCTR,Counter
       Goto A0B0
     Endif
     Goto A0B1
    End
    
  • Just an additional note. The algo I posted works by ignoring small errors between detents, using a preponderance of direction info there to decide whether to increment or decrement at the next detent. This method can still fail, however, if you turn the shaft partway between detents, then turn it back. Although it checks for a non-zero dcount before incrementing or decrementing, an error reading going partway then back would cause dcount to be non-zero and lead to an errant change in counter.

    The solution to this would require a more stringent test than simply non-zero. For example, you could make sure it was one of -4 (%1100), -3 (%1101), 3 (%0011), or 4 (%0100) before allowing a change in count.

    -Phil
  • rodbrinkrodbrink Posts: 9
    edited 2016-11-08 17:49
    Phil -

    The code you posted is working fine with my encoder.

Sign In or Register to comment.