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
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 ...
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.
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
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
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!
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).
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!
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!
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!

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