www.nutsvolts.com/index.php?/magazine/article/november2011_SpinZone
var
long cog
long pincount
long basepin
byte dutycycle[8]
pub start(n, p) | ok
ok := false
if ((n => 1) and (n =< 8))
if ((p => 0) and (p =< (28-n)))
stop
pincount := n
basepin := p
ok := cog := cognew(@ezmod8, @pincount) + 1
return ok
The VAR section holds parameters used by the object,
including the cog used for the PASM code, the pin count,
the base pin, and the byte array for output duty cycle.
We're using a byte array to make things easy, especially
with lighting protocols like DMX. These variables are
placed into the global variable area of the object, so we
can pass a pointer to them to the PASM code.
The start() method takes care of qualifying the
parameters; we want to ensure a valid pin count, as well
as the base pin. What this does is prevent us from using
the programming/debug and I2C pins as modulation
outputs. With good values, the global variables are
updated and the PASM cog is launched. The first
parameter required for the PASM code is the address of
the pin count variable, so we pass that in the cognew()
call; this will be passed to the PASM code in the par
register.
Let's jump into the PASM code that handles the
modulation:
ezmod8
mov
rdlong
add
rdlong
add
mov
tmp1, par
chcount, tmp1
tmp1, #4
ch0pin, tmp1
tmp1, #4
hub0, tmp1
mov
mov
mov
sub
shr
shl
mov
outa, #0
tmp1, #%1111_1111
tmp2, #8
tmp2, chcount
tmp1, tmp2
tmp1, ch0pin
dira, tmp1
At the top, we copy par into tmp1 (so it can be
modified — par is read-only). The first thing we read is the
channel count. Most of the hub parameters are longs, so
we add four to tmp1 to point to the next which is the
base (channel 0) pin for the group. After reading the base
pin, we add four to tmp1 again and we have the hub
address of the duty cycle array. This value is saved into
hub0 for use in the main program loop.
The next section sets the required I/O pins to output
mode in prep for the modulation loop. We start by
moving %1111_1111 into tmp1; this is the mask for eight
bits. Since eight is the maximum channel count, we move
that into tmp2. The channel count is subtracted from tmp2
( 8) to create a right shift value for the mask which corrects
the mask for the number of channels used. The corrected
bit mask is then shifted left to align its LSB with the base
pin for the group. This value is written to outa to set the
pins as outputs. Now for the loop that reads the channel
levels and handles the output modulation:
mod_main
mov
mov
mov
mov
shl
hubpntr, hub0
cogpntr, #chacc
count, chcount
chmask, #1
chmask, ch0pin
:loop
movd
movd
movd
:update_acc, cogpntr
:check_acc, cogpntr
:clear_c, cogpntr
if_e
rdbyte
cmp
mov
tmp1, hubpntr
tmp1, #255 wz
tmp1, C_BIT
:update_acc
:check_acc
:clear_c
add
test
and
muxc
0-0, tmp1
0-0, C_BIT wc
0-0, #$FF
outa, chmask
add
add
shl
hubpntr, #1
cogpntr, #1
chmask, #1
djnz
jmp
count, #:loop
#mod_main
At the top, we copy the hub address of the duty cycle
array (saved in hub0) to a working variable called hubpntr.
The address of the local (cog) accumulator array is copied
to cogpntr. Note that the hash symbol (#) is used to
designate a cog address. Both variables will be modified
through the loop. The number of channels to process is
moved into count and, finally, a mask is created for the
LSB pin of the designated outputs.
The next section is where we do a bit of magic. You
see, one way to use an array in PASM is to modify the
code as it runs. This is possible in the Propeller as all code
and data exists in the same RAM space. Note the three
lines at the start of the inner loop; each uses the movd
instruction. This instruction copies the source value from
that instruction into the destination field of the target
register. In this case, we're specifying those registers by
referring to local labels. In the destination field at each of
those labels, you'll see 0-0 which is a programmer's
reminder that this element will be modified by other code.
This is self-modifying code. In each case, we are moving
◗ BILL OF MATERIALS
Item Description
R1-8 220 ohms
LED1-8 Red, 5 mm
Supplier/Part No.
November 2011 15