P1 PASM clocks, WS2811, and clearing cog ram
Been working on my own WS2811 driver with help from JonnyMac's driver from the P1/P2 demos. Having lots of fun and lots of questions ha. Trying not blow up the forums too much. For now....
1) In the P1 PASM manual, each instruction has a "clocks" number in the Opcode tables. Is that number of cnts it takes to complete that instruction. At 80 Mhz.. the cnts needed for pulse width are 20 cnts, 80 cnts and 48 cnts, 52 cnts for lo and hi bit PWM, respectively. Most instructions in PASM seem to be 4 or 8 clocks, so that is not a lot of room for too many instructions, if I understand the "clocks" correctly.
2) why did JonnyMac "adjust" his pulse width counts with "-9" in his WS2811 pixel driver?
t0h := ustix * ns0h / 1000 - 9 ' pulse widths in ticks (adjusted)
t1h := ustix * ns1h / 1000 - 9
3) I have been getting some random results some times after I reload the same P1 PASM code into RAM (I cleared EEPROM with blank spin code). I checked my code over and over. Of course I could have code wrong, but is there any way I may be reading a location in COG memory that may have some old value? I've confirmed that i'm writting each address before it is used in the code.. but this is PASM and may be different than SPIN. Is there a way to clear COG memory before PASM is loaded when I load my P1 SPIN code?
Thanks again
Comments
1) The shiftout loop is using waitcnt which means we don't have to count cycles as closely as you seem to want to do.
2) Here's the code that creates the high-going pulse in the stream.
I think my logic was that the or and the andn cancel each other out, which leaves the add instruction (4) and the waitcnt (5 + delay). That's where the 9 comes from. I remember doing this in a hotel and then using one of the counters to measure and verify. TBH, the spec has a lot of room in it, so this may not have been necessary.
3) When in doubt, blame your own code. Yes, you do need to initialize cog locations before using them. If you use RES, then you must initialize that variable before its use. In Spin1, only global variables and the return values of methods are initialized to zero.
Post your code -- you'll get lots of good feedback.
Jon, thank you!
1) and 2) yeah ok I see. The spec says +/- 150 nsec for the pulse slop. That is +/- 12 cnts (@80Mhz, 150nsec * cnt/12.5 nsec = 12 cnts). That still seems low to me, but based on your response to question2, the way you do it with waitcnt is fast enough. I do see the slop in the reset time. Spec says >=50 usec which is 4000 cnts (@80Mhz, 50usec * cnt/0.0125usec = 4000 cnts). So that makes sense why you are able to loop back up to #frame_loop without impact (<50 usec).
-ahhhh measure with counters. I have to try that. I was thinking about some type of hardware to see and measure the pulse time shape in voltage.
3) OK great. I've posted my current code. It simply drives 50 pixels one color. I appreciate if anyone can look to point out any bad form. Like I said, I do have some random response after loading the same code to RAM (F10) some of the time.
-Is it best to post code as attachment or as that scroll-able window within the post? How do you do the scroll-able window above?
IMO, your code needs these updates to be general-purpose
-- a start() method that lets you pass the pin and hub address of pixel color buffer
-- start method should also set 0 and 1 bit timing (based on system clkfreq), and the period ticks in 800kHz
-- a stop() method so you can kill that cog
-- methods to operate on the buffer
Notes:
-- You only need to time the high side of the pulse; the low side is the rest of the period which is fixed (800kHz)
-- I think it's bad form to modify the pin that has specific high- and low-times with xor; a setup bug could lead to an error. Just use or (for high) and andn (for low) to change the pin state
-- since it's just for the WS2811, you don't need a variable for the number of bits -- it's always 24
-- the cog does clear the SPRs on entry, so writing 0 to outa is redundant (but does no harm)
thanks, Jon. I have incorporated your comments. It definitely streamlined the code to do the 800kHz cycle. I do see now how the "or" and the "andn" work.
I also figured out the pixel stream reset... the spec says >=50usec, your codes says 80usec min and your range is from 100 usec to 5000 usec. I could only get >= ~250usec holdoff to reset reliably. After your comments and the reset... I'm driving the pixels very reliably.
Yes I definitely plan to expand this to include similar start() method, timing, and stop() method. Now that i have the driver down and working well, I'm going to expand as you suggest. And then onto the light show making!
After this project i'll move onto the P2 going forward. The P1 literature was easier for me to start with as a beginner. I'll have a better understanding of SPIN and PASM to move onto the P2 for the next quick byte.
Latest version of code is attached. Just for records and FYI.
The or instruction with your pin mask will make the output high. The andn with your pin mask will make the output low. You can see this in my P1 driver.
Trust me -- long resets are better. I have been through lots of real-world problems with refreshes on long strings using the "spec" reset period. I spent a couple days troubleshooting a high-end art installation near Beverly Hills because I was using the "spec" reset. Reflections in the long line caused random sparkles in the piece. Extending the reset period fixed that.
Ok understand the "OR" and the "ANDN". Thanks for the clarification.
Interesting. This is the fun stuff... the details. But sure takes a lot of time to figure them out. lol
And the longer reset is still plenty fast enough. I was playing with 250 usec and longer and my eyes could hardly see changes.
One step closer to the application. thanks again!