November 22, 2024, 08:08:02 PM

News:

You can now use Vixen to program your Prop-1 and Prop-2 controllers!  Get started quickly and easily, without having to learn PBASIC.  Details in the Library forum.


HC-8+ Cylon (Lesson on Synchronized Loop Timing)

Started by JonnyMac, April 07, 2012, 08:34:24 AM

Previous topic - Next topic

JonnyMac

April 07, 2012, 08:34:24 AM Last Edit: April 07, 2012, 08:39:31 AM by JonnyMac
All of us coming from the BS1 (Prop-1), BS2 (Prop-2), and SX (Prop-SX) are used to the PAUSE instruction which will delay a specific number of milliseconds.  This is great... until we need a code loop to run for a certain period.  Then we have to fine-tune the delay and if we add or subtract an instruction, we get to do it again.

One of the extremely useful features of the Propeller chip is a free-running counter (called cnt in code) that we can use to create synchronized delays.  What do I mean by this?  A synchronized delay allows us to run a code loop at the same rate -- no matter what else is going on inside the loop.  There is a caveat, of course: the loop code must consume a little less time than the intended duration of the loop.

Spin has an instruction called waitcnt that helps us here.  The waitcnt instruction waits for a specific value in the cnt register.  Please read that last line again.  The waitcnt instruction is looking for a specific value to appear in the cnt register before allowing the code to proceed.

So how do we use this to create a specific delay?  We will add the delay time (in system ticks) to the current value of the system counter.  When this new value is reached the waitcnt instruction will drop through.  Note that there is a low end limit of about 400 counts (this is the time required to setup the instruction).  There is a high-end limit, too, of course; with waitcnt you can create a delay of up to 53 seconds (using standard clock setup).  For longer delays we could call our pause() method.

Let's have a look.  In the code sample below there is a variable called t that is set to the system counter before entering the loop.  After an output is set high we use waitcnt by adding the number of ticks in 125ms to the current value of t.  In the full listing you'll see that MS_001 is a constant that defines the number of clock ticks in one millisecond.

pub main | t, ch

  setup_io                                                      ' setup HC-8+ IO pins

  t := cnt                                                      ' synchronize timer
  repeat
    repeat ch from OUT0 to OUT6
      high(ch)                                                  ' light LED
      waitcnt(t += (125 * MS_001))                              ' hold for 125ms
      low(ch)                                                   ' LED off
    repeat ch from OUT7 to OUT1
      high(ch)                 
      waitcnt(t += (125 * MS_001))               
      low(ch)


You may be thinking, "So what, I could have done that with pause()...."  Here's where it counts: what if when I wanted to put a serial command in after making an output go high?  With pause() this would extend the length of the sequence; with waitcnt the timing remains the same! 

Oh! You may be wondering about this line:

    waitcnt(t += (125 * MS_001))

There an operator in that line that you may not have seen (unless you've programmed in C): the += operator is a shortcut.  That line could also have been written:

    waitcnt(t := t + (125 * MS_001))

... and, yes, we can put what looks like a stand-alone line of code in the parameter parenthesis of another instrcution.  In short:

  x += y

is the same as:

  x := x + y

Note that the assignment operator is different, too ( := ). 

You can run the attached program on the HC-8+ and watch the output on the channel LEDs.  That's one of the fun features onf the HC-8+ -- it's like a controller with a trainer board built in!
Jon McPhalen
EFX-TEK Hollywood Office

bsnut

Jon, I love the lesson so far, but can you show a example what you talking about here.
QuoteYou may be thinking, "So what, I could have done that with pause()...."  Here's where it counts: what if when I wanted to put a serial command in after making an output go high?  With pause() this would extend the length of the sequence; with waitcnt the timing remains the same!
William Stefan
The Basic Stamp Nut

JonnyMac

April 09, 2012, 11:01:37 AM #2 Last Edit: April 09, 2012, 11:31:19 AM by JonnyMac
Here's a slightly advanced example.  Let's say you want a timer/rtc in your code.  You can launch this method into its own cog (will show how later) to create a running timer.  The timer variable (in the global section for the program) will be updated every second.

pri rtc_timer | t                                               ' launch with cognew

'' Synthesized RTC/Timer
'' -- uses global vars: killrtc, scs, mns, hrs
'' -- scs, mns, hrs are bytes in

  t := cnt
  bytefill(@scs, 0, 3)                                          ' reset clock/rtc
  repeat
    if (killrtc)
      quit
 
    waitcnt(t += clkfreq)                                       ' wait 1 second
    if (++scs == 60)                                            ' update seconds and check
      scs := 0
      if (++mns == 60)                                          ' update minutes and check
        mns := 0
        if (++hrs == 24)                                        ' update hours and check
          hrs := 0

  cogstop(cogid)                                                ' stop this cog


This is a case where we need the loop to run EXACTLY once per second, and using waitcnt (with clkfreq which is the number of clock ticks in one second) allows us to do that -- no matter what updates were made to the time registers. 

Note, too, that you can unload this timer by setting a global variable called killrtc to true (non-zero).

Final note: Spin allows you to do a lot with a little code.  The ++ symbol in front of variables means "add 1 to this variable before using it in the rest of the code).  I don't do this very often but this is a good place for it.


Edit: I've attached a practical example of the timer code.  This version allows you to use the same code to start multiple timers (though I'm only showing one).  It shows the time on a terminal window and outputs the seconds (as binary) on the HC-8+ outputs
Jon McPhalen
EFX-TEK Hollywood Office