Shop OBEX P1 Docs P2 Docs Learn Events
Shift left and add c — Parallax Forums

Shift left and add c

I am working on Jon McPhalen's pixel driver so that I can drive 8 PINs with a single cog. I have it doing that, but only from a single buffer source. I need to have individual buffers for each pin.

What my plan of attack is, is to shift left with wc on each of the buffers that I wish to pull data from,shift left and add c into a byte. Once the byte is populated, write that byte to the pins in parallel to 8 pins after .4us of high, and holding it for another .45us. That will ensue any 1's will be .85us and any 0s will be .4us.

Is there a good shortcut to shifting left and adding c?

BTW, Jon's code is simply gorgeous.

Comments

  • evanhevanh Posts: 15,126

    Yep, in Pasm there is Rotate Carry Left. And there's also a Rotate Carry Right for shifting the opposite direction.

        ...
        testp  #pin  wc
        rcl    buffer, #1
        ...
    
  • JonnyMacJonnyMac Posts: 8,912
    edited 2022-03-06 17:31

    It's going to be interesting to see how that shakes out.
    Since you're starting with my code, I hope you'll share how you solve this.

  • evanhevanh Posts: 15,126

    Okay, where's the original?

  • This is the assembly section of the driver Terry referenced.

    dat { auto-run driver }
    
                    org       0
    
    pix_driver      setq      #10-1                                 ' get 10 longs from hub
                    rdlong    newcon, ptra
    
                    mov       t1, #0
                    wrlong    t1, ptra                              ' tell hub we're connected
    
    rgbx_main       rdlong    newcon, ptra                  wz      ' check for new connection
        if_nz       jmp       #pix_driver
    
                    mov       addr, p_hub                           ' point to rgbbuf[0]
                    mov       npix, pixcount                        ' set # active pixels
    
    frame_loop      rdlong    colorbits, addr                       ' read a channel
                    add       addr, #4                              ' point to next
                    tjz       swapflag, #shift_out                  ' skip fix if swap = 0
    
                    movbyts   colorbits, #%%2310                    ' $RR_GG_BB_WW --> $GG_RR_BB_WW
    
    shift_out       getct     bittimer                              ' start timing frame
    
                    rep       #7, pixelbits                         ' loop through all bits
                     shl      colorbits, #1                 wc      ' get MSB
                     drvh     tx                                    ' pin on
        if_nc        waitx    bit0hi                                ' hold for bit timing
        if_c         waitx    bit1hi
                     drvl     tx                                    ' pin off
                     addct1   bittimer, cycletix                    ' update cycle timer
                     waitct1                                        ' let cycle finish
    
    next_pixel      djnz      npix, #frame_loop                     ' done with all leds?
    
    reset_delay     getct     bittimer                              ' reset delay
                    addct1    bittimer, resettix
                    waitct1
    
                    jmp       #rgbx_main                            ' back to top
    
    ' --------------------------------------------------------------------------------------------------
    
    newcon          res       1                                     ' new connection flag
    p_hub           res       1                                     ' pointer to pixel buffer in use
    pixcount        res       1                                     ' # pixels in buffer
    tx              res       1                                     ' output pin
    pixelbits       res       1                                     ' bits per pixel
    resettix        res       1                                     ' frame reset timing
    swapflag        res       1                                     ' if !0, swap R & G
    bit0hi          res       1                                     ' bit0 high timing
    bit1hi          res       1                                     ' bit1 high timing
    cycletix        res       1                                     ' 1.25us cycle ticks
    
    addr            res       1                                     ' address of current rgbw pixel
    npix            res       1                                     ' # of pixels to process
    colorbits       res       1                                     ' rgbw for current pixel
    
    bittimer        res       1                                     ' timer for reset/bit
    
    t1              res       1                                     ' work vars
    t2              res       1
    t3              res       1
    
                    fit       496
    
  • evanhevanh Posts: 15,126
    edited 2022-03-06 17:31

    Oh, shift out to C ... That penny didn't drop immediately. :)

    EDIT: I'm thinking one of the smartpin modes is going to be helpful here. What's the timing diagram look like? I see it's 1.5 us per bit.

  • This is the start of an idea I toyed with yesterday.

    read_bufs       or        swapflag, swapflag            wz      ' nz == swap r & g
    
                    rdlong    color0, p_buf0                        ' get color from buffer
                    add       p_buf0, #4                            ' point to next         
        if_nz       movbyts   color0, #%%2310                       ' swap r&g if required         
    
                    { repeat for buffers 1..6 }
    
                    rdlong    color7, p_buf7
                    add       p_buf7, #4                            
        if_nz       movbyts   color7, #%%2310                           
    
    
                    mov       bits, #24
                    getct     bittimer    
    
    shift_outs      mov       hitimer, bittimer
                    drvh      pins                                  ' all pins in group high
    
                    shl       color7, #1                    wc
                    rcl       timing
                    shl       color6, #1                    wc
                    rcl       timing
                    shl       color5, #1                    wc
                    rcl       timing
                    shl       color4, #1                    wc
                    rcl       timing
                    shl       color3, #1                    wc
                    rcl       timing
                    shl       color2, #1                    wc
                    rcl       timing
                    shl       color1, #1                    wc
                    rcl       timing
                    shl       color0, #1                    wc
                    rcl       timing
    
                    addct1    hitimer, bit0hi                       ' run 0 bit timing                
                    waitct1
    
                    setq      mask
    wr_outs         muxq      outa, timing                          ' turn off chans with 0 bits
    
                    addct1    hitimer, bit1finish                   ' finish 1 bit timing
                    waitct1
    
                    drvl      pins                                  ' all pins in group low
    
                    addct2    bittimer, cycletix                    ' update cycle timer
                    waitct2                                         ' let cycle finish
    
                    djnz      bits, #shift_outs
    
  • evanhevanh Posts: 15,126

    Is there some other topic covering all this? It feels like I'm in need of a catch up.

  • It works, mostly. Can't figure out why P7 and P8 don't work. Here is the chunk of the relevant code. Timing is still not exact, but the pixels work.
    Currently you really should have the same number of pixels on each port, otherwise, it should just be the longest number of pixel strings on a port. I turned what Jon was doing sideways to parallel it.

    You can track my progress here:

    https://griswoldfx.visualstudio.com/Blitzen 24

    rgbx_main       rdlong    newcon, ptra                  wz      ' check for new connection
        if_nz       jmp       #pix_driver
    
    
    
                    mov       addr, p_hub                           ' point to rgbbuf[0]
                    mov       addr2, p_hub2                           
                    mov       addr3, p_hub3                           
                    mov       addr4, p_hub4                           
                    mov       addr5, p_hub5                           
                    mov       addr6, p_hub6                           
                    mov       addr7, p_hub7                           
                    mov       addr8, p_hub8                           
    
                   mov       npix, pixcount                        ' set # active pixels
    
    frame_loop      rdlong    colorbits, addr                       ' read a channel
                    add       addr, #3                              ' TT changed this from 4 to 3- point to next
                    rdlong    colorbits2, addr2                       ' read a channel
                    add       addr2, #3                              ' TT changed this from 4 to 3- point to next
                    rdlong    colorbits3, addr3                       ' read a channel
                    add       addr3, #3                              ' TT changed this from 4 to 3- point to next
                    rdlong    colorbits4, addr4                       ' read a channel
                    add       addr4, #3                              ' TT changed this from 4 to 3- point to next
                    rdlong    colorbits5, addr5                       ' read a channel
                    add       addr5, #3                              ' TT changed this from 4 to 3- point to next
                    rdlong    colorbits6, addr6                       ' read a channel
                    add       addr6, #3                              ' TT changed this from 4 to 3- point to next
                    rdlong    colorbits7, addr7                       ' read a channel
                    add       addr7, #3                              ' TT changed this from 4 to 3- point to next
                    rdlong    colorbits8, addr8                       ' read a channel
                    add       addr8, #3                              ' TT changed this from 4 to 3- point to next
    
                    movbyts   colorbits,  #%%3120                    ' $RR_GG_BB_WW --> $GG_RR_BB_WW
                    movbyts   colorbits2, #%%3120                    ' $RR_GG_BB_WW --> $GG_RR_BB_WW
                    movbyts   colorbits3, #%%3120                    ' $RR_GG_BB_WW --> $GG_RR_BB_WW
                    movbyts   colorbits4, #%%3120                    ' $RR_GG_BB_WW --> $GG_RR_BB_WW
                    movbyts   colorbits5, #%%3120                    ' $RR_GG_BB_WW --> $GG_RR_BB_WW
                    movbyts   colorbits6, #%%3120                    ' $RR_GG_BB_WW --> $GG_RR_BB_WW
                    movbyts   colorbits7, #%%3120                    ' $RR_GG_BB_WW --> $GG_RR_BB_WW
                    movbyts   colorbits8, #%%3120                    ' $RR_GG_BB_WW --> $GG_RR_BB_WW
    
    shift_out       getct     bittimer                              ' start timing frame
                     rep      #27, pixelbits                        ' loop through all bits
                     mov      pinbyte, #$00                         ' Clear pinbyte
                     shl      colorbits8, #1                 wc     ' get MSB 7
                     rcl      pinbyte, #1                           ' Populate pin 7
                     shl      colorbits7, #1                 wc     ' get MSB 6
                     rcl      pinbyte, #1                           ' Populate pin 6
                     shl      colorbits6, #1                 wc     ' get MSB 5
                     rcl      pinbyte, #1                           ' Populate pin 5
                     shl      colorbits5, #1                 wc     ' get MSB 4
                     rcl      pinbyte, #1                           ' Populate pin 4
                     shl      colorbits4, #1                 wc     ' get MSB 3
                     rcl      pinbyte, #1                           ' Populate pin 3
                     shl      colorbits3, #1                 wc     ' get MSB 2
                     rcl      pinbyte, #1                           ' Populate pin 2
                     shl      colorbits2, #1                 wc     ' get MSB 1
                     rcl      pinbyte, #1                           ' Populate pin 1
                     shl      colorbits, #1                  wc     ' get MSB 0
                     rcl      pinbyte, #1                           ' Populate pin 0
                     altsb     tx, #outa                            ' What is the base PIN?
                     setbyte   #$FF                                 ' Turn them all on, because pixeldata always starts with a 1
                     waitx    bit0hi                                ' Wait for .4us
                     altsb     tx,#outa                             ' What is the base PIN?
                     setbyte pinbyte                                ' Turn off the pins with 0 code
                     waitx    bit0hi                                ' Wait for .4us, therefore 0 code only on for .4us and 1 code on for .8us
                     altsb     tx, #outa                            ' What is the base PIN?
                     setbyte   #$00                                   ' Turn them all off, because pixeldata always end with a 0
                     addct1   bittimer, cycletix                    ' update cycle timer
                     waitct1                                        ' let cycle finish
    
    next_pixel      djnz      npix, #frame_loop                     ' done with all leds?
    
    reset_delay     getct     bittimer                              ' reset delay
                    addct1    bittimer, resettix
                    waitct1
    
                    jmp       #rgbx_main                            ' back to top
    
  • Just saw your post Jon. I will study that! Your code most likely is better and I am sure I will learn something. Thank you sir!

  • evanhevanh Posts: 15,126

    The conversation is somewhat ethereal. It seems there must be other linking topics on the propeller forums.

  • I'm going to try an 8-output version limited to the WS2812b (simplifies code).

  • @evanh said:
    The conversation is somewhat ethereal. It seems there must be other linking topics on the propeller forums.

    This is the only one I know of. This is related to my W6100 Ethernet project. I have E1.31 ethernet controlling the pixels. If the velocity of this project continues, I will be testing driving my 4'x8' 1200 pixel (24x50) matrix this week with it. Fingers and toes crossed!

  • JonnyMacJonnyMac Posts: 8,912
    edited 2022-03-07 00:46

    I think I have the parallel drive working for WS2812Bs. It's not ready for publication yet, so I'll just share some pieces to see what you think.

    Test code

    var
    
      long  pixbuf[16]
    
    
    pub main()
    
      leds.start(32, @pixbuf, 2, 10)
    
      pixbuf[ 0] := $FF_00_00_00
      pixbuf[ 2] := $00_FF_00_00
      pixbuf[ 4] := $00_00_FF_00
      pixbuf[ 6] := $FF_FF_00_00
      pixbuf[ 8] := $00_FF_FF_00
      pixbuf[10] := $FF_00_FF_00
      pixbuf[12] := $FF_FF_FF_00
      pixbuf[14] := $0F_0F_0F_00 
    
      repeat
        waitct(0)
    

    I always start very simple. This creates eight 2-pixel buffers and I set the first channel in each buffer to a color and leave the next channel black (0) -- this makes it easy to examine on a logic analyzer.

    Here's the new object start() method.

    pub start(base, p_first, count, holdoff) : result
    
    '' -- base is the first (lsb) pin of the outputs
    '' -- p_first is pointer to the first [long] array holding pixel data
    ''    * arrays must be end-to-end in RAM
    '' -- count is # of pixels in the arrays
    '' -- holdoff is the delay between data bursts
    ''    * units are 100us (0.1ms) 10 units = 1ms
    ''    * long pixel strings tend to require long hold-off delays (e.g. 10 for 1ms)
    
      stop()                                                        ' stop if running
      pinclear(base addpins 7)                                      ' clear tx pins
    
      ustix := clkfreq / 1_000_000                                  ' ticks in 1us
    
      ' set cog parameters
    
      txpin0     := base
      p_pixels   := p_first
      npixels    := count
      resetticks := ustix * 100 * (1 #> holdoff <# 50)              ' note: 80us min reset timing
      t0h        := ustix * 400 / 1000                              ' pulse widths in ticks
      t1h        := ustix * 800 / 1000
      cycleticks := clkfreq / 800_000                               ' ticks in 1.25us (800kHz)
    
      cog := coginit(COGEXEC_NEW, @pix8_driver, @txpin0) + 1        ' start the cog
    
      return cog
    

    Finally, here's the cog code. It's substantially longer than the single output version -- not a surprise with 8 simultaneous outputs.

    dat { 8-pin WS2812b driver WIP }
    
                    org       0
    
    pix8_driver     setq      #7-1                                 ' get 7 longs from hub
                    rdlong    pins, ptra
    
                    ' configure for parallel output
    
                    testb     pins, #5                      wc      ' outa or outb?
                    bitc      wr_outs, #9                           ' update wr_outs instruction
                    mov       mask, #$FF                            ' create mask for 8 pins
                    shl       mask, pins
                    or        pins, #(%111 << 6)                    ' convert to 8-pin group
    
                    ' setup [other] buffer addresses
    
                    mov       t1, pixcount
                    shl       t1, #2                                ' convert to bytes
    
                    mov       hub1, hub0
                    add       hub1, t1
                    mov       hub2, hub1
                    add       hub2, t1
                    mov       hub3, hub2
                    add       hub3, t1
                    mov       hub4, hub3
                    add       hub4, t1
                    mov       hub5, hub4
                    add       hub5, t1
                    mov       hub6, hub5
                    add       hub6, t1
                    mov       hub7, hub6
                    add       hub7, t1
    
                    sub       bit1hi, bit0hi                        ' adjust for waitctx
    
    
    rgbx_main       mov       addr0, hub0                           ' point to start of buffers
                    mov       addr1, hub1
                    mov       addr2, hub2
                    mov       addr3, hub3
                    mov       addr4, hub4
                    mov       addr5, hub5
                    mov       addr6, hub6
                    mov       addr7, hub7
    
                    mov       npix, pixcount                        ' set # active pixels
    
    next_pixel      rdlong    color0, addr0                         ' get color from buffer
                    add       addr0, #4                             ' point to next
                    movbyts   color0, #%%2310                       ' swap r&g
                    rdlong    color1, addr1
                    add       addr1, #4
                    movbyts   color1, #%%2310
                    rdlong    color2, addr2
                    add       addr2, #4
                    movbyts   color2, #%%2310
                    rdlong    color3, addr3
                    add       addr3, #4
                    movbyts   color3, #%%2310
                    rdlong    color4, addr4
                    add       addr4, #4
                    movbyts   color4, #%%2310
                    rdlong    color5, addr5
                    add       addr5, #4
                    movbyts   color5, #%%2310
                    rdlong    color6, addr6
                    add       addr6, #4
                    movbyts   color6, #%%2310
                    rdlong    color7, addr7
                    add       addr7, #4
                    movbyts   color7, #%%2310
    
                    mov       bits, #24
                    getct     bittimer
    
    shift_outs      mov       hitimer, bittimer
                    drvh      pins                                  ' all pins in group high
    
                    shl       color7, #1                    wc
                    rcl       chouts, #1
                    shl       color6, #1                    wc
                    rcl       chouts, #1
                    shl       color5, #1                    wc
                    rcl       chouts, #1
                    shl       color4, #1                    wc
                    rcl       chouts, #1
                    shl       color3, #1                    wc
                    rcl       chouts, #1
                    shl       color2, #1                    wc
                    rcl       chouts, #1
                    shl       color1, #1                    wc
                    rcl       chouts, #1
                    shl       color0, #1                    wc
                    rcl       chouts, #1
    
                    addct1    hitimer, bit0hi                       ' run 0 bit timing
                    waitct1
    
                    setq      mask
    wr_outs         muxq      outa, chouts                          ' turn off chans with 0 bits
    
                    addct1    hitimer, bit1hi                       ' finish 1 bit timing
                    waitct1
    
                    drvl      pins                                  ' all pins in group low
    
                    addct2    bittimer, cycletix                    ' update cycle timer
                    waitct2                                         ' let cycle finish
    
                    djnz      bits, #shift_outs                     ' done with all bits in the color?
    
                    djnz      npix, #next_pixel                     ' done with all channels?
    
    reset_delay     waitx     resettix                              ' reset delay
    
                    jmp       #rgbx_main                            ' back to top
    
    ' --------------------------------------------------------------------------------------------------
    
    pins            res       1                                     ' pin group
    hub0            res       1                                     ' pointer to first pixel buffer
    pixcount        res       1                                     ' # pixels in each buffer
    resettix        res       1                                     ' frame reset timing
    bit0hi          res       1                                     ' bit0 high timing
    bit1hi          res       1                                     ' bit1 high timing
    cycletix        res       1                                     ' 1.25us cycle ticks
    
    mask            res       1                                     ' mask for pins group
    
    hub1            res       1
    hub2            res       1
    hub3            res       1
    hub4            res       1
    hub5            res       1
    hub6            res       1
    hub7            res       1
    
    addr0           res       1                                     ' working pointers for buffers
    addr1           res       1
    addr2           res       1
    addr3           res       1
    addr4           res       1
    addr5           res       1
    addr6           res       1
    addr7           res       1
    
    color0          res       1                                     ' working color for each output
    color1          res       1
    color2          res       1
    color3          res       1
    color4          res       1
    color5          res       1
    color6          res       1
    color7          res       1
    
    npix            res       1                                     ' # of pixels to process
    bits            res       1                                     ' bit counter (24 for WS2812b)
    bittimer        res       1                                     ' timer for reset/bit
    hitimer         res       1                                     ' timer for high portion of bit
    chouts          res       1                                     ' 8 outputs as byte
    
    t1              res       1                                     ' work vars
    t2              res       1
    t3              res       1
    
                    fit       496
    

    The next step is to update the methods that manipulate a buffer to accept a buffer id -- I will do that tomorrow.

    Here's a screen cap from the logic analyzer running the test code. This is kind of exciting!

  • evanhevanh Posts: 15,126
    edited 2022-03-07 03:21

    @ke4pjw said:
    This is the only one I know of. This is related to my W6100 Ethernet project. I have E1.31 ethernet controlling the pixels. If the velocity of this project continues, I will be testing driving my 4'x8' 1200 pixel (24x50) matrix this week with it. Fingers and toes crossed!

    Well, you both seem to be carrying on about an existing driver, which obviously isn't Ethernet comms. Is there something, on the forums, covering that?

  • @evanh said:
    Well, you both seem to be carrying on about an existing driver, which obviously isn't Ethernet comms. Is there something, on the forums, covering that?

    Oh, this is Jon's pixel driver- jm_rgbx_pixel.spin2

    I think it ships with the PropellerTool.

  • @JonnyMac I found a bug in my code that maps the E1.31 universes to a pin. It was a dumb mistake and I have all 8 pins working with inputs from Jinx!

    Can't wait to see your finished code! I am a super sloppy coder that hacks at things until it works. I know your driver will be superior!

    It is wild to see how many refreshes are occurring with a 100 pixel string vs 8 configured and 4 unconfigured E1.31 universes. When line 0 goes low, there is E1.31 data in the buffer. Line 1 is the P7 output, that is mapped to E1.31 Universe 9. The matrix refresh is set to 25Hz and it looks like the P2 will have absolutely no problem doing this! I knew she was a Pixel Pusher!

  • evanhevanh Posts: 15,126
    edited 2022-03-07 04:50

    @ke4pjw said:
    Oh, this is Jon's pixel driver- jm_rgbx_pixel.spin2

    I think it ships with the PropellerTool.

    Thank you. And I see there is links to PDFs in the driver source too. That's handy.

    Hmm, I guess next problem is I can't test any code without these LEDs ... they look like they could be the fancy lighting strips in the local lighting shops - job for tomorrow ...

  • JonnyMacJonnyMac Posts: 8,912
    edited 2022-03-07 08:19

    Can't wait to see your finished code!

    Well, I stayed up far too late tonight -- but I think this is okay to share with others. I disconnected the LA and attached LEDs. All ports seem to be working properly.

    The matrix refresh is set to 25Hz and it looks like the P2 will have absolutely no problem doing this!

    You should be able to update about 1200+ pixels per port at 25Hz. That said, I have found long pixel strings require long reset periods (otherwise you get weird sparkles -- I had this happen on an art piece).

    Please let me know if you find any issues with the driver. Once it's really wrung out I'll make a multi-type version that is like my original driver, but with 8 simultaneous outputs.

  • I updated my 8-port WS2812b driver so that it can vary between 1 and 8 ports. Now that this is working, I will add the other pixel types back into the mix. For those using WS2812b pixels, this is ready.

Sign In or Register to comment.