Shop OBEX P1 Docs P2 Docs Learn Events
Need help getting a encoder index working — Parallax Forums

Need help getting a encoder index working

krazyideaskrazyideas Posts: 119
edited 2012-06-21 11:34 in Propeller 1
So first off just so you know how much of a beginner I am, I just picked up the reference manul and started programing.

Ok so I can reason through Spin sufficently for my little programs and they work :) But when it comes to Assembly, I can't make heads or tails of it :(. I spend about 4 hours today trying with no success.

So now My problem.

I am using a E2 encoder with an index from US digital to run a home made electric motor with my Dad. I got the program running and working, BUT it can only operate at about 1600 RPM. I poked and proded at my program untill I found the part that was causeing the problem.
Which is that my counter (In Spin) can't keep up. So I tryed to convert it to assembly but because assembly is so complicated I ended up looking on the object exchange and found a encoder counter Called "IdxquadDecoder." It's overview says

"Modified Quadrature Decoder object by Luke Hays to use a third pin to preset encoder count to any value allowing the calculation of absolute encoder angle. See Luke Hays' example for reference. The same except uses third pin for index and allows preset by setting the value of BigValue in the asm section of the program."

I got it counting beautifuly, much cleaner then my Spin attempt. But I need the counter or "count register" to be reset to "0" each time the index is triggered, which is once a rotation. I understand in the overview that this is what it does but I can't make it do it.

Can someone point out where in the code I need to change something and please remember I'm clueless.

Here's the Code.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% 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.
VAR

  byte  cog


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                 mov     IndexMask, #%100        'Mask for Index Pulse ***

                        shl     IndexMask, epin1             'Shift to proper pin  ***

                                      

                        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

                        mov     MarkIndex, newstate                 ' Get unmasked index pin ***

                        and     MarkIndex, IndexMask  wz         ' Mask pins other than index ***

              if_z     jmp     #over                                        ' Skip accum 0 if index is 0***

                        mov     accum, #0

over                  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

IndexMask     res 1

MarkIndex     res 1



                        FIT

Comments

  • krazyideaskrazyideas Posts: 119
    edited 2012-06-19 22:18
    I tryed to space out the comments so it was easyer to read but then they undid them selves when I posted it. Sorry
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-06-19 23:01
    The forum software removes extra spaces unless you use code tags. Phil has posted a tutorial on how to use them.

    attachment.php?attachmentid=78421&d=1297987572
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-06-19 23:34
    I'm not clear on what you want the program to do.

    Do you not want to use the pin to clear the accumulator but to have the accumulator zero itself once a complete revolution has been made?

    If you want to zero the accumulalator once it has reached a certain value, you could add a couple of "cmpsub" statements to the code right before the accumulator is written to the hub.
    turnedneg               add     accum, #%1
                            cmpsub  accum, pulsesPerRev
                            wrlong  accum, par
                            jmp     #updatestates ' exit
     
    turnedpos               sub     accum, #%1
                            cmpsub  accum, pulsesPerRevNeg
    
                            wrlong  accum, par
    

    You'd want to add "pulsesPerRev" as one of your PASM variables. You could preset the value of "pulsesPerRev". For example, if your encoder produced 300 pulses per revolution, you'd change the variable section to:
    epin1                   long 12
    pulsesPerRev            long 300            
    pulsesPerRevNeg         long -300            
     
    

    This is the quick way to set a variable value. There are better ways of setting variables than this.

    Edit: I realized you'd need to test for the negative value of pulsesPerRev if the wheel is turning backward. I'm not sure why "turn" labels appear to be opposite in the code.

    BTW, When I added encoder support to my Mecanum wheeled robot, I modified JonnyMac's code from his Spin Zone #6 article. I personally think JonnyMac's code was easier to follow and modify than the code you posted.
  • krazyideaskrazyideas Posts: 119
    edited 2012-06-20 10:26
    Awsome thanks, for both the code help and for showing me how to display code in the post properly.

    I'll let you know if I get it working today

    And what I really want the to program to do IS to reset the accumulator to Zero each time the index pin is triggered.
    But I think just resetting the accumulator at a certen value will work, not as quite as well because if it was reset each time the index was triggered it would eliminate any possible accumulation errors at the start of each rotation of the encoder.

    I couldn't make the accumulator reset to Zero and I tryed toggleing each pin individualy, but none of the pins reset it.

    Thanks again
  • msrobotsmsrobots Posts: 3,709
    edited 2012-06-20 11:37
    Hi krazyideas,

    carefully looking thru the code I was not able to find any obvious mistakes.

    you are using 3 pins to the propeller ? how did you connect your encoder?

    the PURE MAGIC you found there is actually pretty neat. a quadrature encoder uses 2 signals (the first two pins) to count and decide in which direction to count. So it compares two readouts to find out which signal came first.

    and it checks the third pin and resets the counter to 0 when that third pin is low. But it is adding/subtracting the movment after resetting so you will actually never SEE zero but 1 or -1.

    just put some buttons on a breadboard and test it by hand. IMHO it should work like coded.

    Enjoy!

    Mike
  • krazyideaskrazyideas Posts: 119
    edited 2012-06-20 17:35
    Hi Mike.

    So I have the index connected to pin 0. Then the other two pins connected to pin 1 and 2.

    Where it reads
    PUB start(pin, varaddr)
    

    I am passing pin 1, so in effect it reads
    PUB start(1, varaddr)
    

    So as I understand it that passes pin 1 and pin 2 to be the inputs from the encoder. But what pin do I connect the index pin to ???? I think I tryed all of them but mabe I missed one.
    Any thoughts?

    Thanks
  • krazyideaskrazyideas Posts: 119
    edited 2012-06-20 17:36
    Hi Mike.

    So I have the index connected to pin 0. Then the other two pins connected to pin 1 and 2.

    Where it reads
    PUB start(pin, varaddr)
    

    I am passing pin 1, so in effect it reads
    PUB start(1, varaddr)
    

    So as I understand it that passes pin 1 and pin 2 to be the inputs from the encoder. But what pin do I connect the index pin to ???? I think I tryed all of them but mabe I missed one.
    Any thoughts?

    Thanks
  • AribaAriba Posts: 2,690
    edited 2012-06-20 18:54
    looks like the index pin is hardcoded in the PASM code to pin 12. But you can change that to another pin, just modify the number in epin1

    Andy
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-06-20 19:10
    Ariba wrote: »
    looks like the index pin is hardcoded in the PASM code to pin 12. But you can change that to another pin, just modify the number in epin1

    Andy

    I noticed the "12" also. I don't think it matters though since the start method overwrites the "epin1" value before starting the cog.
  • kuronekokuroneko Posts: 3,623
    edited 2012-06-20 19:25
    Based on the code in the first post, the index pin is always last in a group of 3.
    quad                 mov     IndexMask, #[COLOR="orange"]%100[/COLOR]        ' Mask for Index Pulse ***
                         shl     IndexMask, epin1        ' Shift to proper pin  ***
    
                         rdlong  accum, par              ' get initial value of accumulator
                         mov     quadmask, #[COLOR="orange"]%11[/COLOR]          ' initialize encoder mask
                         shl     quadmask, epin1         ' bitshift it to encoder pins
    
  • krazyideaskrazyideas Posts: 119
    edited 2012-06-20 23:21
    Well I tryed manualy driveing every pin low on the prop other then the two pins (1 and 2) that were connected to the encoders channal A and B. Then after driveing each pin ground I turned the encoder to make sure the count didn't need a step to reset, but it didn't do anything. It just continued to count up or down depending on the direction I turned the encoder.

    I also wrote another little program to make sure that the prop was feeling the index when it went low and high, by turning a LED on and off through the prop based on the condition of the index pin. It worked fine.

    This is the demo program I have been useing to read the value of the count


    % Quadrature Decoder DEMO %

    % Author: Luke Haywas %

    % %

    % %

    % Copyright (c) <2010> <Luke Haywas> %

    % See end of file for terms of use. %

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



    Description:

    Demonstrates how to use my object QuadDecoder.

    Quadrature encoder is connected to pins 1 and 2.

    (If connected to different pins, simply change the

    value of ENCODER_PIN below)



    Displays value of the accumulated variable in

    the serial debug terminal.



    }}
    CON
    
      _clkmode = xtal1 + pll16x
    
      _xinfreq = 5_000_000
    
      '_xinfreq = 6_250_000
    
    
    
      LCD_PIN       = 27
    
      LCD_BAUD      = 19_200
    
      LCD_LINES     = 4
    
      LCD_COLS      = 20
    
    
    
      ENCODER_PIN   = 1
    
    
    
    OBJ
    
      quad  :       "IdxQuadDecoder"
    
      db    :       "FullDuplexSerial"
    
    
    
    VAR
    
      long  count                                          ' example variable that will be accumulated to
    
    
    
    PUB main
    
    
    
      db.start(31,30,0,115_200)                             ' crank up the debug terminal
    
    
    
      count := 25                                          ' initialize the accumulator
    
                                                            ' You can set it to any desired value
    
    
    
      quad.start(ENCODER_PIN, @count)                      ' start the encoder reader
    
    
    
      ' output to debug terminal:
    
          
    
      repeat
    
        db.str(string(1, "Quadrature Decoder Demo", 13))
    
        db.str(string("-----------------------", 13, 13))
    
        db.str(string("Value = "))
    
        db.dec(offset)
    
        db.tx(32)
    
        waitcnt(clkfreq/10 + cnt)
    
    

    I've tryed everything I can think of.
    Thanks
  • kuronekokuroneko Posts: 3,623
    edited 2012-06-20 23:43
    krazyideas wrote: »
    Well I tryed manualy driveing every pin low on the prop other then the two pins (1 and 2) that were connected to the encoders channal A and B.
    The code you posted resets the count when the index pin is driven high, not low.
    and     MarkIndex, IndexMask  wz         ' Mask pins other than index ***
    if_z    jmp     #over                            ' Skip accum 0 if index is 0***
            mov     accum, #0
    over    and     newstate, quadmask               ' mask it against the encoder mask
    
  • krazyideaskrazyideas Posts: 119
    edited 2012-06-21 06:41
    Thanks. I will try driveing each pin High and see what happens.

    I can really see the advantage of assembly vs spin but where would a fellow go for a couple of really good tutorals that explained thins well?

    Thanks again
  • krazyideaskrazyideas Posts: 119
    edited 2012-06-21 06:42
    Thanks. I will try driveing each pin High and see what happens.

    I can really see the advantage of assembly vs spin but where would a fellow go for a couple of really good tutorals that explained thins well?

    Thanks again
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-06-21 07:31
    If you're using P1 and P2 for the encoder, then P3 would be the reset pin.

    The line before "over" sets the accumulator to zero. If P3 is low, this line gets skipped (the z flag would be set by the "and" statement, so "if_z" is true), if P3 is high the variable "accum" is set to zero.
  • krazyideaskrazyideas Posts: 119
    edited 2012-06-21 11:34
    IT WORKS, Awsome.
    Thank you guys so much for the help. And the patience with a begginer.

    You guys are great.
    Take care
Sign In or Register to comment.