RGB LEDs, but this is easily adjusted with a constant in the
driver object. As with most Propeller objects, the .start()
method sets up parameters used by the driver cog, and
then launches it:
■ FIGURE 5.
WS2812 bit timing.
pub start(pin, leds) | ustix
stop
dira[pin] := 0
ustix := clkfreq / 1_000_000
constants from my original driver.
txp := pin
stringsize := 1 #> leds <# MAX_LEDS
resetticks := ustix 55
t0h := ustix 35 / 100
t0l := ustix 80 / 100
t1h := ustix 70 / 100
t1l := ustix 60 / 100
bufaddr := @rgbbuf
Okay, the truth is that it didn't really matter here; the
driver is tiny and there was no issue with resources. This
won't always be the case, however, and in this project I'm
going to show you techniques used by advanced Propeller
programmers to save cog resources. A big part of the
reason I write this column is to help others learn to
program the Propeller so that they don't have to count on
an object being available; it's much more fun to write
one's own code, anyway.
cog := cognew(@ws2812, @txp) + 1
return cog
This is pretty self-explanatory. We're setting up the pin
used, the number of LEDs to update, and the timing
parameters for the driver. The final parameter is the hub
address; the driver needs to know where we're storing
color values for it to transmit.
Let's have a look at the protocol. The WS2812 wants
the data line to be quiet (low) for at least 50
microseconds to perform a reset. After this, it will read 24
bits — most significant bit (MSB) first — into the green, red,
and blue color registers. Additional bits on the data input
are re-routed to the data output until the next reset period.
If you look carefully, you'll see that there are eight
longs that we need to transfer to the PASM cog that
implement the driver. After initializing everything, we pass
the starting address of the block (@txp) in the cognew
instruction.
With just one line, the data is asynchronous, though it
uses a slot-based RTZ (return to zero) signaling scheme.
The bit value is based on the time the data line is high at
the beginning of the ~1.25 microsecond slot. A zero bit
has a high time of 0.35 microseconds with a low time of
0.8 microseconds; a one bit has a high time of 0.7
Let's have a look at the PASM code that copies these
values from the hub RAM into the cog RAM. In some
objects with just a few parameters, we'll typically do
something like this:
dat
org 0
Figure 5).
If you do, in fact, conduct a Google search for a
WS2812 driver you'll find a lot of consternation over
writing code to meet the somewhat tricky timing specs.
ws2812 mov t1, par
rdlong txpin, t1
add t1, #4
rdlong ledcount, t1
add t1, #4
rdlong resettix, t1
Pardon me for being partisan, but this is where the
Propeller just kicks the stuffing out of the Arduino. By
using a dedicated processor, I was able to write my
original driver in about 20 minutes, and it worked the first
time without tuning. Let me show you what I did and the
final product based on some really great interaction with
other programmers in the Propeller forum.
Remember that the address passed in cognew will be
available to the cog in the par register. We can use this as
the starting point to moving values from the hub to the
cog. This is fine with just a few parameters, but gets long-winded beyond that. With about the same amount of
code as shown above, we can copy as many parameters
as we have from the hub to the cog:
Getting Started with the WS2812
dat
To connect to the WS2812 driver, we need to specify
an output pin and tell the driver how many LEDs we have
in the string. As written, the driver will support up to 64
org 0
ws2812 mov t1, par
movd :read, #txpin
mov t2, #8
52 September 2013