Shop OBEX P1 Docs P2 Docs Learn Events
Any elegant implementations of a timer in SPIN? — Parallax Forums

Any elegant implementations of a timer in SPIN?

agsags Posts: 386
edited 2013-06-04 17:30 in Propeller 1
I've found cases where I'd like a timer in SPIN. Consider the case of waiting for some asynchronous user input. I'd like to set a timer (value should be a configurable parameter) when I first begin waiting for input so that I can detect a timeout condition and exit the input polling loop rather than waiting indefinitely. I am guessing there are more elegant ways of implementing this than what I've done. (In fact, I'm betting there is a simple and obvious solution that I just don't see) I'm currently using a brute-force method that handles each of four separate cases depending on whether the current system clock counter value and "future time" clock counter value are greater than or less than 0 (since the comparison is done using signed 32 bit values) and if the "future time" when the timer will expire constitutes a counter rollover. (Four cases: now>0 and future>0; now<0 and future<0; now>0 and future<0; now<0 and future>0). Or, is there an unsigned compare available in SPIN? [Clarification: for now, the timer value is limited to less than the time for the system counter to cycle once, ~52 seconds]

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2013-04-14 17:04
    It's too simple and straightforward to be called elegant ... Look at any implementation of rxtime, a Spin routine to wait for a character input with a timeout
    PUB rxtime(ms) : rxbyte | t
    
    '' Wait ms milliseconds for a byte to be received
    '' returns -1 if no byte received, $00..$FF if byte
    
      t := cnt
      repeat until (rxbyte := rxcheck) => 0 or (cnt - t) / (clkfreq / 1000) > ms
    
    rxcheck will set rxbyte to -1 the first time it's called if there is no character in the buffer and OR will evaluate both arguments so the return value will be properly set. Note that arithmetic comparisons are signed, so the maximum timeout is 2^31-1 (about 26 seconds at 80MHz).
  • Mike GMike G Posts: 2,702
    edited 2024-02-19 15:56
    Here's an example of a timer using a counter. Originally intended to renew a DHCP lease. More brute force than elegant...
    https://forums.parallax.com/discussion/143731/hostname-expired-ip-stil-working-dhcp-renew-leastime
  • homosapienhomosapien Posts: 147
    edited 2013-04-15 11:39
    Mike,

    Is it a typo having the assignment operator ':=' in the repeat code?
    [COLOR=#3E3E3E][FONT=Parallax]repeat until (rxbyte := rxcheck) => 0 or (cnt - t) / (clkfreq / 1000) > ms[/FONT][/COLOR]
    

    Not sure how that would evaluate...


    Nate
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-04-15 11:50
    That's not a typo. Assignments are allowed inside expressions in Spin.

    -Phil
  • Mike GreenMike Green Posts: 23,101
    edited 2013-04-15 11:53
    An assignment operation evaluates to the value being assigned to the variable (always 32-bit regardless of the size of the variable).
  • lonesocklonesock Posts: 917
    edited 2013-04-15 15:12
    I like to put as much math up-front as possible, to speed up the waiting loop.
    PUB RxTime( ms ) : rxbyte | tout
    {{
      * Waits for a byte to be received or a timeout to occur.
      > ms : the number of milliseconds to wait for an incoming byte
      < returns -1 if no byte received, $00..$FF if byte
    
      e.g. if (c := RxTime( 10 )) < 0
    }}
      tout := clkfreq / 1000 * ms + cnt
      repeat
        rxbyte := RxCheck
      while (rxbyte < 0) and ((cnt - tout) < 0)
    
    Jonathan
  • agsags Posts: 386
    edited 2013-06-03 13:02
    This may be simple and straightforward, but it's not obvious (to me). Intuition told me that with all the positive/negative/overflow combinations this couldn't work as I wanted/expected; experience told me that Mike/Mike/lonesock wouldn't post it if it didn't. So I examined all possible combinations of positive/negative timeout/current times. Of course, it does work. Even though the (unsigned) clock counter progresses from 0 -> POSX, NEGX -> -1, 0 ...

    On the one hand, I could say that it's a lucky artifact of the way subtraction (or more accurately, addition of signed values) works, and overflows. But that seems like saying it's lucky that pants have two legs so they can be used to cover my lower half. I understand ones and twos complement representation. What isn't obvious to me is how subtracting any two values from a discontiguous sequence of numbers always results in a value that is the "shortest distance" between the values (when viewed as if the sequence was not a line but a circle wrapping back on itself). I see that given any two signed values, value1 - value2 will return a positive value if value1 is "closer" to value2 if counting from value2 up to value1 (allowing for overflow/wraparound at -1 -> 0) and a negative value if value1 is "closer" to value1 if counting from value2 backwards to value1 (with overflow/wraparound at 0 -> -1).

    Clearly there is some proof, theorem, or basic property of twos complement representation that can explain this concisely (but I can't articulate it myself).

    Also: I note that each of the examples cited above use "currentTime-timerTime > 0" for the test; it seems more correct to me to use "currentTime-timerTime => 0". I checked all the corner cases I could think of and this still works, but exits the loop as soon as the stop time (timer value) is reached. Any reason to use ">" instead of "=>"?

    Along the same lines, my implementation is much like the example from lonesock (for the same reason - do the calculation of "timerTime" once then have a simple test loop). Any reason why this is not preferred? -
    PUB timerTest(millis) | stopTime
       timerTime := cnt + clkfreq/1000*millis
      repeat until cnt - timerTime => 0
    

    The point being that "timerTime := cnt + clkfreq/1000*millis" captures the begin time (in cnt) earlier than "timerTime := clkfreq/1000*millis + cnt" (assuming left-to-right order of evaluation) and therefore starts the timer sooner.

    Interesting how much detail there is to understand for such a very simple task. Thanks for the replies.
  • Ding-BattyDing-Batty Posts: 302
    edited 2013-06-03 13:33
    If its worth doing, its worth overdoing...
  • max72max72 Posts: 1,155
    edited 2013-06-04 13:02
    When 2's complements are not clicking I usually test it by hand using a custom number of bits... 4 is usually ok, you have 1 sign bit and 3 more bits to play with. Small enough to enumerate everything on paper.
    Massimo
  • agsags Posts: 386
    edited 2013-06-04 15:05
    Yes, I have often done the same thing myself. No need to carry around all the bits, as you say.

    The outstanding questions (simplified - see post #8 for full text) are:

    1) Can anyone explain the fundamentals of why this works? I have proven to myself that it does, but it can't be a coincidence.
    2) Any reason why the comparison uses "> 0" instead of "=> 0" ?

    @Ding-Batty: looks like you spent some time determining precise # clocks...
  • AribaAriba Posts: 2,690
    edited 2013-06-04 17:30
    ags wrote: »
    Yes, I have often done the same thing myself. No need to carry around all the bits, as you say.

    The outstanding questions (simplified - see post #8 for full text) are:

    1) Can anyone explain the fundamentals of why this works? I have proven to myself that it does, but it can't be a coincidence.
    2) Any reason why the comparison uses "> 0" instead of "=> 0" ?

    2) The difference between >0 and =>0 is exactly 12.5 ns with 80MHz clock. It's just a definition: Is it a timeout if you reach the time or if you are over the time?

    1) It works because you subtract two 32bit numbers and get again a 32bit result. It's some kind of subtract with modulo.
    The subtract operation for signed and unsigned numbers is exactly the same, the only difference is how the Carry (Borrow) is handled (and how the result is interpreted). Here we ignore the carry flag anyway.

    Andy
Sign In or Register to comment.