minutes, or hours — and attempting to calculate the
number of seconds required for the delay. This caused a
couple problems, not the least of which was the
opportunity to generate an overflow value on very long
cycle times.
A simpler method I proposed was to create a software
real-time clock that ran independently of the main
program cog. Here’s the first version; this one works with
values assuming normal clock limits (that is, 60 seconds,
60 minutes, 24 hours):
PRI softrtc | t0
t0 := cnt
repeat
if (Clock[REG_RST] < 0)
longfill(@Clock, 0, 6)
waitcnt(t0 += MS_001)
if (++Clock[REG_MS] == 1_000)
Clock[REG_MS]~
if (++Clock[REG_SC] == 60)
Clock[REG_SC]~
if (++Clock[REG_MN] == 60)
Clock[REG_MN]~
if (Clock[REG_HR] == 24)
Clock[REG_HR]~
if (Clock[REG_DY] < posx)
++Clock[REG_DY]
else
Clock[REG_DY]~
As you can see, this method works with a global array
called Clock; the array includes elements for milliseconds,
seconds, minutes, hours, days, and a reset flag. That last
register is important. Since this method is going to be
running in a separate copy of the interpreter — hence
operating independently of our main program — we do not
want to change the timing registers outside of this method.
Doing so could cause a problem where we try to clear the
registers while it is half way through an update cycle.
To get around this, we’ll use another method. This just
sets the flag register which tells the softrtc method to
clear its timing elements:
PUB reset
Clock[REG_RST]~~
This method sets the last register to -1. As you can
see, the first thing the softrtc method does is check this
value; when it’s below zero, all registers (including the
reset flag) are cleared to zero in one fell swoop using
LONGFILL. As the software RTC uses a synchronized one
millisecond delay, resetting the clock is reliable and
happens within a millisecond.
The code is really quite easy. When we reach 1,000
milliseconds, we clear that register and then add one
second. When seconds reaches 60, we clear that register
and then add a minute; and so on.
To launch this method into its own interpreter, we’ll
do this:
cognew(softrtc, @Stack)
16
July 2010
This form of the COGNEW instruction launches
another Spin interpreter into a separate cog and points
this cog to the softrtc method. Note that the Spin
interpreter needs some stack (RAM) space and, in this
case, we’re telling the cog to use an array of longs called
Stack. Setting the size of the stack array can be a little like
guessing the weather for next week — you can get close,
but until you get there you don’t really know.
That stack is used for the storage of local variables,
parameters when calling other methods, and intermediate
values when evaluating expressions. I tend to set my stack
size to 32 longs and have not run into any problems.
However, I tend to keep those Spin methods that run in a
separate interpreter fairly simple. There is an object
included with the Propeller Tool that can be used to check
stack size but I tend not to use this as its output is serial.
I’d rather do that part myself.
So, I created a really simple object based on an
example from a Propeller guru (and very nice, very helpful
guy), Phil Pilgrim. Phil’s idea is that you can fill your stack
with a known pattern and then check the stack — while
your program is running — for changes from that pattern.
Here are the two methods used in my stack test object:
PUB fill(pntr, len)
longfill(pntr, TEST_VAL, len)
PUB used(pntr, len) | idx
idx := len - 1
repeat len
if (long[pntr][idx] <> TEST_VAL)
quit
else
—————idx
return idx + 1
The first method fills the stack with a known test
value; this must be done prior to using that stack (or else
you will probably crash the program). The second method
determines stack usage working from the top to bottom,
looking for a change from the test value; when a change is
found, QUIT is used to terminate the REPEAT loop. The
syntax of the test line may look a little complicated — it’s
really not bad. We can access any area of memory using
the implicit long[] array; that value in the first set of
brackets (pntr) is the base address of our stack. The value
in the second set of brackets (idx) is the index into the
stack.
I whipped up a little demo (see jm_timer_stack.spin
in the downloads at www.nutsvolts.com) to test my stack
requirements. Both my stack checker and the Parallax
version reported a stack usage of seven longs for the
softrtc method, so the 32 originally allocated was more
than enough.
The softrtc method works great but Wayne pushed
back — what he wanted to do is be able to set timing units
beyond normal clock boundaries. For example, he might