brightness control of the LED. That’s what we’re going to
do with the Propeller. We’ll start simply and build as we
go until we have the full-featured driver.
The hardware for this project is simple: a resistor
and bi-color LED that you probably have in
your parts drawer. You can solder these
together for the Propeller platform or plug them in if
you’re using the Propeller demo board or PPDB.
THREE COLORS FROM TWO
The two-lead, bi-color LED actually has two
LED chips inside (green and red) wired
back-to-back. This arrangement allows current to flow
through just one element at a time. It turns out that if we
reverse current flow quickly through the bi-color LED, it
can appear yellow, giving us a third color and — in total —
four states: off, green, red, and yellow.
Here’s the rub: Green and red LEDs are
constructed from different materials and have different
forward voltages. On my LED, the green chip had a
forward voltage of 1.75V and the red chip had a
forward voltage of 1.56V. With a 220 ohm resistor and
a 3.3V output from the Propeller, the green LED
would get about 7 mA and the red LED would get about
8 mA — it’s enough to make a difference and you can
clearly see it when reading the forward voltage with
So, what does this mean? Well, it means that we
cannot get yellow by simply reversing the current
direction every other cycle. What we’re going to do is
construct the code such that we can easily control the
green to red cycle ratio to get the best approximation
Okay, then, let’s jump into the code. You’ll remember
from last time that Propeller assembly code (PASM)
segments are defined in a DAT section of a Spin program.
We’ll start with the interface which lets us define the pin
to use for the green anode (red will be green plus one)
and the color state for the LED.
mov tmp1, par
rdlong tmp2, tmp1
mov gMask, #1
shl gMask, tmp2
or dira, gMask
The driver starts by copying the cog’s par (parameter)
register to tmp1; what we’re going to pass in par is the
address (using @) of the variable that holds the green
anode pin number (this is the starting address of all the
parameters we will pass to the driver). With rdlong, we
can read the pin number from the hub RAM into the cog
variable called tmp2. What we want to do is create a pin
mask for the green pin; to do that, we’ll load gMask with
one and then shift left by the pin number in tmp2. To
make that pin an output, we’ll or the pin mask onto the
dira register as dira controls the pin’s I/O direction
(one bit = output; zero bit = input).
In the program, we’re going to specify that the red
anode pin is the next pin up from green, so creating the
red mask is easy: We copy the gMask into rMask and
then shift rMask left by one bit. As with the green pin,
we make the red anode pin an output by or-ing the pin
mask onto dira.
At this point, tmp1 is equal to par which is the
address of the green pin variable. The second parameter
for the driver is the color mode. To get the hub address of
the color mode variable, we can add four (four bytes in a
long integer) to tmp1 and copy that into colorAddr; we’ll
use this variable in the program to update the LED color.
With the driver setup [mostly] out of the way, we can
get into the main loop. In this version of the driver, we
don’t need any specific loop timing but it doesn’t hurt to
add it in to support timing functions to come later.
Loop timing in assembly is usually a nightmare of cycle
counting but the Propeller relieves us of that nightmare by
including an instruction called waitcnt which takes care of
the dirty work.
Remember that all cogs are driven by a common
clock and have access to the system counter. In both Spin
and PASM, the waitcnt instructions will wait for the system
counter to reach a specific value — this makes precise
timing very simple. We start by defining a timer variable
and loading it with the number of counter ticks we want
to wait. As the system timer is almost always running at
80,000,000 ticks per second, the delay time will usually be
a significant number. Once we’ve loaded the variable with
the delay, ticks synchronize it by adding the current value
of the system counter.
mov loopTimer, us010
add loopTimer, cnt
Here we’re loading the variable called loop Timer with
the value stored in us010; this is a predefined value that
you’ll see at the end of the assembly listing.
long 80000000 / 1000000 10
At first, this may seem odd because it looks like we’re
doing long division and multiplication in an assembly
program. We’re not. All PASM programs start as a DAT
section in a Spin program and when launched with
cognew are moved to the cog RAM. Since we started in
Spin, the compiler will resolve this expression prior to
July 2009 17