One click-turn of quad encoder produces increment by 4

The quad encoder I bought is obviously different from the old one that died. Using the below driver, every click-turn results in the decoded value to increment by 4 (or -4). Assembly has always been my weakness, so although I understand that all it takes is to shift right 2 the result, I do not understand how to do it. Guidance would be nice.
"{{

┌──────────────────────────────────────────┐
│ Quadrature Decoder                       │
│ Author: Luke Haywas                      │
│                                          │
│                                          │
│ Copyright (c) <2010> <Luke Haywas>       │               
│ See end of file for terms of use.        │                
└──────────────────────────────────────────┘

Description:
Uses one cog to continuously monitor a rotary quadrature encoder.
As the encoder is turned, each change of position is accumulated
to a variable in hub RAM. This variable then reflects how far
the encoder has been turned to the left or the right.

Wiring: Connect the encoder to two adjacent pins. Pass the number
of the first pin as the first parameter of the Start method.
Pass the ADDRESS of the variable to accumulate to as the second
parameter of the Start method.

}}

CON

          _clkmode = xtal1 + pll16x
          _xinfreq = 5_000_000                                      'use 5MHz crystal
        
          clk_freq = (_clkmode >> 6) * _xinfreq                     'system freq as a constant
          mSec     = clk_freq / 1_000                               'ticks in 1ms
          uSec     = clk_freq / 1_000_000                           'ticks in 1us

          
   PinQuadA = 20
  

VAR
  byte  cog
  LONG  gQuadPos


OBJ
          pst   : "Parallax Serial Terminal"
          

PUB Main | value


   pst.Start(9600)                                                      'Remember to start the terminal at 9600b/s
   WAITCNT(clkfreq + cnt)                                         
  
   pst.Str(String("Test of Quad Encoder, press to start"))
   value := pst.DecIn

   start(PinQuadA, @gQuadPos)
      
   pst.Chars(pst#NL, 2)
   REPEAT
     pst.Dec(gQuadPos)
     WAITCNT(clkfreq/2 + cnt)
     pst.Chars(pst#NL, 2)
     

PUB start(pin, varaddr)                                 ' Start a cog monitoring the encoder.
  stop 
  epin1 := pin
  result := cog := cognew(@quad, varaddr) + 1          

{{
  Parameters:
    pin                         first pin of the encoder input
    varaddr                     address of the hub variable to accumulate to

  Returns:
    1 + number of cog it got started in
    0 if no cogs available.

  Note: You can preset the value of the accumulation variable before calling start.
  
  Example usage:
    variable := 25
    quad.start(12, @variable)
}}

PUB stop                                                ' Stop monitoring the encoder.
  if cog
    cogstop(cog~ - 1)

DAT
                        ORG                        
quad                    rdlong  accum, par              ' get initial value of accumulator
                        mov     quadmask, #%11          ' initialize encoder mask
                        shl     quadmask, epin1         ' bitshift it to encoder pins
                                 
firstread               mov     oldstate, ina           ' read encoder for the first time
                        and     oldstate, quadmask      ' call this the "old state"
                        shr     oldstate, epin1         ' bitshift it into the LSBs
                        
backtostart             mov     turnpos, oldstate       ' PURE VOODOO MAGIC
                        rev     turnpos, #30            ' do some funky bit math
                        xor     turnpos, #%10           ' new state will match one of these
                                                        ' whichever it matches = turn direction                        
                        mov     turnneg, oldstate
                        rev     turnneg, #30
                        xor     turnneg, #%01           
                        
getnewstate             mov     newstate, ina           ' read encoder
                        and     newstate, quadmask      ' mask it against the encoder mask
                        shr     newstate, epin1         ' shift it into the LSBs
                                                        
                        cmp     turnpos, newstate       wz     ' see if it moved cw
              if_z      jmp     #turnedpos

                        cmp     turnneg, newstate       wz     ' see if it moved ccw
              if_z      jmp     #turnedneg
                        
                        jmp     #getnewstate            ' falls through, start over

turnedneg               add     accum, #%1
                        wrlong  accum, par
                        jmp     #updatestates           ' exit

turnedpos               sub     accum, #%1
                        wrlong  accum, par
                        jmp     #updatestates           ' exit

updatestates            mov     oldstate, newstate      ' mov newstate overwrite oldstate
                        jmp     #backtostart
                        
                     

epin1   long  12

accum         res 1
quadmask      res 1
oldstate      res 1
turnpos       res 1
turnneg       res 1
newstate      res 1
time          res 1

                        FIT

{{
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ TERMS OF USE: MIT License │                                                            
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation    │ 
│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,    │
│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│
│is furnished to do so, subject to the following conditions:                                                                   │
│                                                                                                                              │
│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│
│                                                                                                                              │
│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE          │
│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR         │
│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,   │
│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                         │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
}}
"

Comments

  • JonnyMacJonnyMac Posts: 6,960
    edited 2019-12-11 - 01:35:30
    In my encoder drivers I have a setting that accounts for this behavior. When I preset the encoder count I multiply by 4; on reading that could I divide by 4 (using SAR).

    You might add these methods:
    pub preset(value)
    
      qQuadPos := value << 2
    
    
    pub get_encoder
    
      return qQuadPos ~> 2
    

    Looking over the PASM code you posted I see that it doesn't allow encoder updates (setting) after it's started. I've attached a dual-encoder object that let's you set each encoder on-the-fly if needed, and accounts for detented encoders that return 4 counts per click.
  • Thanks JonnyMac, I will switch to your driver, add the two methods you suggest, and change my parent code to call for the encoder value when needed, instead of the present solution where the parent code relies on 'its own' variable being continuously updated by the encoder object.
    Good help!
    Erlend
  • If you switch to my driver you don't have to add anything. The start() method lets you define that the encoder is 4 counts per click and handles everything from there.
  • Ah- I did wonder, but I've learned to never doubt what you say :)
  • JonnyMacJonnyMac Posts: 6,960
    edited 2019-12-11 - 01:40:04
    After looking over my own driver -- which I haven't played with in a while -- I decided to make some changes. The only thing that would effect you is the direction sensing.

    New code is attached to my post above.
  • Thanks.
Sign In or Register to comment.