Shop OBEX P1 Docs P2 Docs Learn Events
Is non contiguous possible on the Rotary Object? — Parallax Forums

Is non contiguous possible on the Rotary Object?

T ChapT Chap Posts: 4,223
edited 2014-07-30 20:24 in Propeller 1
In the rotary object, I have pins 21 and 22 on an encoder. I need to connect a second encoder. 23 and onwards are doing other things and can't be modified. 0/1, 15/16, 13/14 pairs are open for the second encoder. There are no more cogs. Does anyone have an opinion on the PASM as to whether the pins could be hard coded in PASM to reflect noncontiquous inputs? IE Encoder 1 = 21/22 Encoder 2 =0/1. No delta support required.
PUB Start(StartPin, NumEnc, NumDelta, PosAddr, direction): Pass
''Record configuration, clear all encoder positions and launch a continuous encoder-reading cog.
''PARAMETERS: StartPin = (0..63) 1st pin of encoder 1.  2nd pin of encoder 1 is StartPin+1.
''                       Additional pins for other encoders are contiguous starting with StartPin+2 but MUST NOT cross port boundry (31).
''            NumEnc   = Number of encoders (1..16) to monitor.
''            NumDelta = Number of encoders (0..16) needing delta value support (can be less than NumEnc).
''            PosAddr  = Address of a buffer of longs where each encoder's position (and deta position, if any) is to be stored.
''RETURNS:    True if successful, False otherwise.

  Pin := StartPin                                                                                     
  TotEnc := NumEnc                                                                                    
  TotDelta := NumDelta
  Pos := PosAddr
  Stop
  SetDir(direction)
  longfill(Pos, 0, TotEnc+TotDelta)
  Pass := (Cog := cognew(@Update, Pos) + 1) > 0
DAT
'Read all encoders and update encoder positions in main memory.
'See "Theory of Operation," below, for operational explanation.
'Cycle Calculation Equation:
'  Terms:     SU = :Sample to :Update.  UTI = :UpdatePos through :IPos.  MMW = Main Memory Write.
'             AMMN = After MMW to :Next.  NU = :Next to :UpdatePos.  SH = Resync to Hub.  NS = :Next to :Sample.
'  Equation:  SU + UTI + MMW + (AMMN + NU + UTI + SH + MMW) * (TotEnc-1) + AMMN + NS
'             = 92 + 16  +  8  + ( 16  + 4  + 16  + 6  +  8 ) * (TotEnc-1) +  16  + 12
'             = 144 + 50*(TotEnc-1)

                        org     0
                                                                                                
Update                  test    Pin, #$20               wc      'Test for upper or lower port
                        muxc    :PinSrc, #%1                    'Adjust :PinSrc instruction for proper port
                        mov     IPosAddr, #IntPos               'Clear all internal encoder position values
                        movd    :IClear, IPosAddr               '  set starting internal pointer
                        mov     Idx, TotEnc                     '  for all encoders...  
        :IClear         mov     0, #0                           '  clear internal memory
                        add     IPosAddr, #1                    '  increment pointer
                        movd    :IClear, IPosAddr               
                        djnz    Idx, #:IClear                   '  loop for each encoder
                                                                
                        mov     St2, ina                        'Take first sample of encoder pins
                        shr     St2, Pin                
:Sample                 mov     IPosAddr, #IntPos               'Reset encoder position buffer addresses
                        movd    :IPos+0, IPosAddr                               
                        movd    :IPos+1, IPosAddr
                        mov     MPosAddr, PAR                           
                        mov     St1, St2                        'Calc 2-bit signed offsets (St1 = B1:A1)
                        mov     T1,  St2                        '                           T1  = B1:A1 
                        shl     T1, #1                          '                           T1  = A1:x 
        :PinSrc         mov     St2, inb                        '  Sample encoders         (St2 = B2:A2 left shifted by first encoder offset)
                        shr     St2, Pin                        '  Adj for first encoder   (St2 = B2:A2)
                        xor     St1, St2                        '          St1  =              B1^B2:A1^A2
                        xor     T1, St2                         '          T1   =              A1^B2:x
                        and     T1, BMask                       '          T1   =              A1^B2:0
                        or      T1, AMask                       '          T1   =              A1^B2:1
                        mov     T2, St1                         '          T2   =              B1^B2:A1^A2
                        and     T2, AMask                       '          T2   =                  0:A1^A2
                        and     St1, BMask                      '          St1  =              B1^B2:0
                        shr     St1, #1                         '          St1  =                  0:B1^B2
                        xor     T2, St1                         '          T2   =                  0:A1^A2^B1^B2
                        mov     St1, T2                         '          St1  =                  0:A1^B2^B1^A2
                        shl     St1, #1                         '          St1  =        A1^B2^B1^A2:0
                        or      St1, T2                         '          St1  =        A1^B2^B1^A2:A1^B2^B1^A2
                        and     St1, T1                         '          St1  =  A1^B2^B1^A2&A1^B2:A1^B2^B1^A2
                        mov     Idx, TotEnc                     'For all encoders...
:UpdatePos              ror     St1, #2                         'Rotate current bit pair into 31:30
                        mov     Diff, St1                       'Convert 2-bit signed to 32-bit signed Diff
                        sar     Diff, #30
                        '  add or substraction the count to the accumulator
        :IPos           add     0, Diff                         'Add to encoder position value
                        '
                        wrlong  0, MPosAddr                     'Write new position to main memory
                        add     IPosAddr, #1                    'Increment encoder position addresses
                        movd    :IPos+0, IPosAddr
                        movd    :IPos+1, IPosAddr
                        add     MPosAddr, #4                            
:Next                   djnz    Idx, #:UpdatePos                'Loop for each encoder
                        jmp     #:Sample                        'Loop forever

'Define Encoder Reading Cog's constants/variables

AMask                   long    $55555555                       'A bit mask
BMask                   long    $AAAAAAAA                       'B bit mask
MSB                     long    $80000000                       'MSB mask for current bit pair

Pin                     long    0                               'First pin connected to first encoder
TotEnc                  long    0                               'Total number of encoders

Idx                     res     1                               'Encoder index
St1                     res     1                               'Previous state
St2                     res     1                               'Current state
T1                      res     1                               'Temp 1
T2                      res     1                               'Temp 2
Diff                    res     1                               'Difference, ie: -1, 0 or +1
IPosAddr                res     1                               'Address of current encoder position counter (Internal Memory)
MPosAddr                res     1                               'Address of current encoder position counter (Main Memory)
IntPos                  res     16                              'Internal encoder position counter buffer

Comments

  • JonnyMacJonnyMac Posts: 9,105
    edited 2014-07-26 19:36
    That code is optimized for contiguous IO pins, so you'd have to re-write it. I think I have an object that does three encoders on any pins (was for a project that monitored pan/tilt/position and speed). Let me look for it.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-07-26 20:37
    As long as your encoders are not spinning too fast this program should read four quadrature encoders.

    Each pair of pins need to be together but the pairs can be on any set you wish.

    Here's the output of the demo program.
    Encoder Demo
    
    encoderCount[0] = -3958
    encoderSpeed[0] = 0
    encoderCount[1] = -20
    encoderSpeed[1] = 0
    encoderCount[2] = 28
    encoderSpeed[2] = 0
    encoderCount[3] = 16
    encoderSpeed[3] = 0
    

    The speed is no particular units. I'm working on improving the speed section to work better at low speeds. (I don't know if you even need speed feedback.)

    The encoder code is based on JonnyMac's SpinZone article Spinning It Up with Encoders (or something like that).

    The encoder object receives the pin settings as a pointer.
      Encoders.StartEncoders(@encoderPin, @encoderCount, @encoderSpeed)
    
    
    

    As you can see there are a total of three address passed to the encoder object.

    The pins are set as constants.
    FIRST_PIN_0 = 21
      FIRST_PIN_1 = 0
      FIRST_PIN_2 = 15
      FIRST_PIN_3 = 13
    

    But these constants need to be placed in an array so they can be passed to the child object.
    DAT
    
    encoderPin    byte FIRST_PIN_0, FIRST_PIN_1, FIRST_PIN_2, FIRST_PIN_3 
    
    
    
  • T ChapT Chap Posts: 4,223
    edited 2014-07-27 08:57
    Thanks for the help guys. I will test this out. Hopefully -1 can be used for no pins or some other method to only use 2 pairs of pins versus all 4 pairs. One encoder is 400 lines or 1600 counts at max 3000 rpm. The other is the same except it is 12.5 times slower.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-07-27 09:03
    T Chap wrote: »
    Thanks for the help guys. I will test this out. Hopefully -1 can be used for no pins or some other method to only use 2 pairs of pins versus all 4 pairs.

    I'm pretty sure the object I posted doesn't include a provision to turn off a channel. Since it just reads the pin as an input it won't interfere with other parts of your code. You can just set extra channels to the previous channels' pins.
  • T ChapT Chap Posts: 4,223
    edited 2014-07-27 09:05
    Ok that works then. The code looks easily modified though, probably chop out have of it for the unused pins.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-07-27 09:56
    I posted some two encoder code here. I think one version includes PWM and one is just the encoders (IIRC).

    Edit: Both the objects in post #2 of the above linked thread are encoders only.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2014-07-27 11:10
    Give this a try. Fair warning: it's old, and was written for a specific 3-motor project. I made some updates and added notes to help you. The start() method expects a list of pins in a DAT table -- like this:
    dat
    
      EncPins     long      2, 3, 5, 7, -1, -1
    


    Use @EncPins as the parameter in start(). You can use -1 in the 2nd and 3rd pin sets, the first (encoder 0) must be defined.

    I hope this helps.
  • T ChapT Chap Posts: 4,223
    edited 2014-07-27 12:54
    Thanks Jon! Will test it.
  • T ChapT Chap Posts: 4,223
    edited 2014-07-27 21:17
    I am wanting to test the code Jon posted right above and include some self modifying PASM in launch. The purpose is to create a version that counts in reverse without having to reprogram or swap AB wires off the encoder.
    PUB SetDir(direction)
    'Lines that use SUBS  -56   -41   -22
    'Lines that use ADDS  -60   -37   -18
    ' ADDS = 110100
    'SUBS = 110101
      if direction == 1      
        MAX_NEG[-56] |=  |< 26       'ADDS    set bit 26 low for adds    reversed
        MAX_NEG[-41] |=  |< 26       'ADDS    set bit 26 low for adds
        MAX_NEG[-22] |=  |< 26       'ADDS    set bit 26 low for adds
        MAX_NEG[-60] &= !|< 26     'SUBS     set bit 26 high for SUBS
        MAX_NEG[-37] &= !|< 26     'SUBS     set bit 26 high for SUBS
        MAX_NEG[-18] &= !|< 26     'SUBS     set bit 26 high for SUBS
      elseif direction == 0  
        MAX_NEG[-56] &= !|< 26       'SUBS     set bit 26 high for SUBS     normal counting
        MAX_NEG[-41] &= !|< 26       'SUBS     set bit 26 high for SUBS
        MAX_NEG[-22] &= !|< 26       'SUBS     set bit 26 high for SUBS
        MAX_NEG[-60] |=  |< 26        'ADDS    set bit 26 low for adds
        MAX_NEG[-37] |=  |< 26        'ADDS    set bit 26 low for adds
        MAX_NEG[-18] |=  |< 26        'ADDS    set bit 26 low for adds
    

    This should toggle bit 26 and swap ADDS and SUBS. I was hoping someone could confirm that this is correct thinking.

    Below you can see the PASM with the numbers including for counting back from the first declared long in DAT, leaving out some info that is not needed here:
    dat
    
                            org     0
    
    tri_encoders            mov     r1, par                         ' start of structure
                            rdlong  r2, r1                          ' get enc0a pin
                            mov     e0amask, #1                     ' create pin mask
                            shl     e0amask, r2
    
                            add     r1, #4
                            rdlong  r2, r1                          ' get enc0b pin
                            mov     e0bmask, #1
                            shl     e0bmask, r2
    
                            add     r1, #4
                            rdlong  r2, r1                          ' get enc1a pin
                            cmps    r2, #0                  wc, wz  ' used? (< 0 = no)
            if_b            mov     e1amask, #0
            if_ae           mov     e1amask, #1
            if_ae           shl     e1amask, r2
    
                            add     r1, #4
                            rdlong  r2, r1                          ' get enc1b pin
                            cmps    r2, #0                  wc, wz
            if_b            mov     e1bmask, #0
            if_ae           mov     e1bmask, #1
            if_ae           shl     e1bmask, r2
    
                            add     r1, #4
                            rdlong  r2, r1                          ' get enc2a pin
                            cmps    r2, #0                  wc, wz  ' used? (< 0 = no)
            if_b            mov     e2amask, #0
            if_ae           mov     e2amask, #1
            if_ae           shl     e2amask, r2
    
                            add     r1, #4
                            rdlong  r2, r1                          ' get enc2b pin
                            cmps    r2, #0                  wc, wz  ' used? (< 0 = no)
            if_b            mov     e2bmask, #0
            if_ae           mov     e2bmask, #1
            if_ae           shl     e2bmask, r2
    
                            add     r1, #4                          ' read enc0cnt in VAR
                            mov     enc0hub, r1                     ' save hub addr for results
                            mov     enc1hub, enc0hub
                            add     enc1hub, #4                     ' read enc1cnt in VAR
                            mov     enc2hub, enc1hub
                            add     enc2hub, #4                     ' read enc2cnt in VAR
    
                            call    #sample                         ' read starting inputs
                            mov     oldscan, newscan                ' save
    
    
    encmain                 call    #sample                         ' get sample
                            test    newscan, oldscan        wz      ' compare to last
            if_e            jmp     #encmain                        ' wait for change
    
    
    
    check0                  mov     r1, newscan                     ' copy new scan
                            xor     r1, oldscan                     ' compare with old
                            and     r1, #%00_00_11          wz      ' isolate encoder0
            if_z            jmp     #check1                         ' skip if no change
    
                            rdlong  r2, enc0hub                     ' get current encoder value
    
                            mov     r1, newscan
                            shl     r1, #1
                            xor     r1, oldscan
                            test    r1, #%00_00_10          wc      ' check direction
            if_c            jmp     #dec0
    
    inc0                    cmps    r2, MAX_POS             wz      ' at max?
            if_e            jmp     #check1                         ' yes, skip update
                            adds    r2, #1                       '-60
                            jmp     #update0
    
    dec0                    cmps    r2, MAX_NEG             wz      ' at min?
            if_e            jmp     #check1
                            subs    r2, #1                       '-56
    
    update0                 wrlong  r2, enc0hub                     ' write updated value to hub
    
    
    
    check1                  tjz     e1amask, #check2                ' skip if not defined
    
                            mov     r1, newscan
                            xor     r1, oldscan
                            and     r1, #%00_11_00          wz      ' isolate encoder1
            if_z            jmp     #check2
    
                            rdlong  r2, enc1hub
    
                            mov     r1, newscan
                            shl     r1, #1
                            xor     r1, oldscan
                            test    r1, #%00_10_00          wc
            if_c            jmp     #dec1
    
    inc1                    cmps    r2, MAX_POS             wz
            if_e            jmp     #check2
                            adds    r2, #1                      '-41
                            jmp     #update2
    
    dec1                    cmps    r2, MAX_NEG             wz
            if_e            jmp     #check2
                            subs    r2, #1                      '-37
    
    update1                 wrlong  r2, enc1hub
    
    
    
    check2                  tjz     e2amask, #0                     ' skip if not defined
    
                            mov     r1, newscan
                            xor     r1, oldscan
                            and     r1, #%11_00_00          wz      ' isolate encoder2
            if_z            jmp     #done
    
                            rdlong  r2, enc2hub
    
                            mov     r1, newscan
                            shl     r1, #1
                            xor     r1, oldscan
                            test    r1, #%10_00_00          wc
            if_c            jmp     #dec2
    
    inc2                    cmps    r2, MAX_POS             wz
            if_e            jmp     #done
                            adds    r2, #1                        '-22
                            jmp     #update2
    
    dec2                    cmps    r2, MAX_NEG             wz
            if_e            jmp     #done
                            subs    r2, #1                        '-18
    
    update2                 wrlong  r2, enc2hub
    
    
    
    done                    mov     oldscan, newscan
                            jmp     #encmain
    
    
    
    ' sample encoder pins
    ' -- updates 'now' as: e3b_e3a_e2b_e2a_e1b_e1a
    
    sample                  mov     rawpins, ina                    ' snapshot of inputs
                            test    rawpins, e0amask        wc      ' get enc1 bits
                            muxc    newscan, #%00_00_01
                            test    rawpins, e0bmask        wc
                            muxc    newscan, #%00_00_10
                            test    rawpins, e1amask        wc      ' get enc2 bits
                            muxc    newscan, #%00_01_00
                            test    rawpins, e1bmask        wc
                            muxc    newscan, #%00_10_00
                            test    rawpins, e2amask        wc      ' get enc3 bits
                            muxc    newscan, #%01_00_00
                            test    rawpins, e2bmask        wc
                            muxc    newscan, #%10_00_00
    sample_ret              ret
    
    ' ----------------------------------------------------------------------------------------
    
    MAX_NEG                 long    negx   ' start counting  backwards here  <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    MAX_POS                 long    posx
    
  • T ChapT Chap Posts: 4,223
    edited 2014-07-30 20:24
    Duane you gave me a good idea that I didn't consider when you mentioned to just ignore unused encoder reads. It was taking too much work to try to backwards engineer some of the posted ideas. So I went back to the Rotary object, set it up for 4 encoders( I only need 2) and fortunately had pins 15 and 16 available as a pair. So I started the contiquous Pin declaration in the Rotary object at 15 for the first encoder pair, then ignored the readings on 17-18, 19-20, and used the 4th encoder read on 21-22. The object gives the formula for max RPM for any given number of encoders used based on system speed, and I am way under so. This worked out perfectly and I have the 2 encoders working as needed.
Sign In or Register to comment.