Manipulating Bits in a LONG
JonnyMac
Posts: 9,107
For an RGB LED driver I'd like to keep the colors in the high-level interface as $RR_GG_BB but the device itself wants the order as $GG_RR_BB. In the world of Captain Obvioso (that would be me), I'm doing this:
I know there are some with wild-eyed assembly tricks for bit manipulations -- and I'm always game to learn. Timing is not a problem here, but faster tends to be better.
' adjust colorbits ' -- starts as $RR_GG_BB ' -- ends as $GG_RR_BB mov t1, colorbits ' make copies mov t2, colorbits and t1, RED_BITS ' isolate colors and t2, GRN_BITS and colorbits, BLU_BITS shr t1, #8 ' reposition red shl t2, #8 ' reposition green or colorbits, t2 ' reconstruct or colorbits, t1 ' shift out bits here RED_BITS long $FF_00_00 GRN_BITS long $00_FF_00 BLU_BITS long $00_00_FF
I know there are some with wild-eyed assembly tricks for bit manipulations -- and I'm always game to learn. Timing is not a problem here, but faster tends to be better.
Comments
step1: get the long in to position that the lower byte is correct, use shift or rol
step2: get the byte you want to merge in to position bit 8-1 bit, bit 0 is a do-not-care
step3: use movi
step4: rol the long 8bits
step5: repeat from step2 one more time for 24bit data
if you could adjust so shiftout uses 24bit msb left shiftout , could make the last rol colorbits,#16 and delete the and
It's a lot simpler to write the 9 instructions to shift the data around than it would be to change a board layout or rewire a board/connector.
That is not an option. The device in question is a WS2812 RGB 5050 LED (with built-in driver) and it wants the data as $GG_RR_BB. Why? I have no idea. As I try to keep my code obvious I wan to pass colors as $RR_GG_BB. In the end, the easy code works, the driver works, and I'm going to put a fork in it.
Here is a method that uses 7 instructions
"Mask" is preset to = $00_FF_00_00
"Data" is both your input and output
[Edit] Updated driver in post #25. Will move to ObEx shortly.
I believe the color sequence comes from the video industry where they use a lot of color subtraction and it is from the green channel.
Jim
Now that is the perfect answer when the bits are shifted out on a single pin, and it only adds 2 or 3 shift instructions to the driver.
Good to see you're onto these. The prop is well suited to driving a bunch of these (several "universes")
I've attached some working code based on Gavin T Garner's obex code. The GRB vs RGB swap is a bit of a pain, I did it in the shift out driver. That way we can just change the driver according to the exact led
Btw my kids have had an absolute ball programming these, choosing the color sequences of a little "train" that runs along the strip, as well as how many leds in the "train" etc. Part of the fun is the flexible strip itself, that can be bent around the place to run up walls, loop the loop, weave between toys, etc. It has been good practice spelling colors (including "Chartreuse") and they asked me to add "brown" and "black" to the range of colors (I think we also added marone and crimson too)
One of the next projects we're going to try is a light version of a slot car set, with a red, green and blue "car", that can overtake each other, (you'll get yellow while red is passing green, for instance). Need to 3d print some hand throttles!
Will post some pics/vids of proto board soon
BTW the B version of the WS2812 (WS2812B) is extremely easy to solder, because it has 4 pins rather than 6 (one pin in each corner, spread relatively far apart). Its more like a 0.1" pitch. I have 1000 on order, hope to see them soon. I can send you some when they arrive if you like (and anyone else that wants a few samples)
regards
Lachlan
EDIT: Beware; I messed around with some of the color constants in the attached code - turned down the brightness by editing the constants, most are at "half brightness"
Thanks for the tip on the WS2812B -- I have a couple projects that they will be great for. I've already created a component and pattern for DipTrace. And, yes, I would love samples. Thanks!
Will have a look at the adjusted constants. As I admit in the code, I liberated those from another program. Will do likewise with yours!
That looked intriguing so I plugged it into my code and.... no dice. It seems like it should work, but it doesn't.
ROL D,S Rotate D left by S: C= D[31]
RCL D,S Rotate carry left into D by S: C= D[31]
I updated the code on page1
It would simplify the code a little if you only need to supply two longs for timing, one for total length of 125us and one for 35us
The below code keeps the rising edge at the same interval time, independent of code length.
My final code does in fact use values right out of the WS2812 data sheet. I tried other values but it stops the driver from working correctly. This could be due to 3.3v from the Propeller going into the module (technically, 3.3v is below the Vih spec for the chip). I've attached my final driver in the event it might be useful to you.
As you can see, I've got three Adafruit NeoPixels on an Activity Board and they're doing what I want them to do.
I think the your suspicions are right about the datasheet. However we don't know how they decode the pulses - there's a good chance it involves some kind of voltage averaging (comparing the data in waveform against its own longer term average), may not even involve any type of clock (its a self clocking signal in after all).
I'm in the process of testing the WS2812B one parameter at a time ("WS2812B, the missing manual"). I've been looking at the V-I characteristics and thermal performance first (had Adafruits 40 neopixel shield up at 108 C so far). Next up I want to look at switching thresholds on the Din, and then I'll get onto timing. The prop is plenty fast enough to explore all the timing limits (I'm sure we can make a multi string controller in one cog, in fact).
As john says we all have slightly different code working fine, so happy to look at yours and offer suggestions if you get really stuck. Otherwise keep going!
Here's a vid of a propeller proto board driving a few forms http://youtu.be/z6vhASDaWIw
Let me get to those timing tests. I changed the way the "timing ticks" are calculated from Gavin's original way. I have been able to successfully run faster and slower than recommended, but I didn't record the limits. I think from memory 0.2us and 0.6us worked, but I'll revisit that soon and document it.
As the WS2812 have different timing for a 0,1 bit and also different (supposedly needed) for the total length if the bit was a 0 or 1.
That is why you need multiple waitcnt with different values.
What you have to learn with waitcnt, that you wait for a time to be equal but then you also add a value good towards the next wait (if not needed use #0)
cnt on the destination side (add cnt,cnt) is just a way to use the free shadow register and the real cnt always have to be on the source side.
Below you'll find the latest code from my driver. This uses Tony's register manipulation trick so that the $RR_GG_BB value passed to the routine gets transmitted as $GG_RR_BB.
The high side pulse timing is most critical (it establishes the bit value), so I load the timer, take the TX line high, then add in the cnt register; in my mind this helps the code keep the pulse timing as close as possible. Sure there's a little slop on the low side, but this is well within the tolerances of the spec.
Yes it would as the length of the code would add around 0.6us, in my code in #24 post I do use fixed interval timer.
The length of the bit is more important as the LED will sync itself to the rising edge of each bit it get in.