Shop OBEX P1 Docs P2 Docs Learn Events
My First Obex ;) Timer32 — Parallax Forums

My First Obex ;) Timer32

jmspaggijmspaggi Posts: 629
edited 2010-10-07 16:22 in Propeller 1
Hey!

I just published my first OBEX! ;)

http://obex.parallax.com/objects/670/

It's a timer object based on the Javelin Timer object idea.

Working that way:
  OBJ
    timer : "Timer32"
  PUB main
   timer.Mark
   repeat
      timer.Tick
      if (timer.TimeOutMS(500))
        timer.Mark
        '' Perform a periodic action.

Few options available too, like using a cog instead of calling tick on the main loop, implementing multiple timers but calling only one time tick for all, etc. ;)

Ok, maybe far from the best Spin code you will have ever see, but at least, it can be useful. It's something which I was really missing on the propeller. Very useful when you want to have one single loop and multiple actions planned in it.

JM

Comments

  • AribaAriba Posts: 2,690
    edited 2010-10-06 19:50
    jmspaggi wrote: »
    ...
    Ok, maybe far from the best Spin code you will have ever see, but at least, it can be useful. It's something which I was really missing on the propeller. Very useful when you want to have one single loop and multiple actions planned in it.

    Sorry, but your object looks overcomplicated to me. And it's hard coded for 80MHz, which is never a good idea.
    Spin and the Propeller can do this much better, simpler and clock independent:
    PUB main | ms, mark1, mark2
       ms := clkfreq/1000
       mark1 := mark2 := cnt
       repeat
         if cnt-mark1 > 500*ms
           mark1 += 500*ms
           'Perform a periodic action every 500ms.
         if cnt-mark2 > 3000*ms
           mark2 += 3000*ms
           'Perform another periodic action every 3 sec.
    

    As long as the clkfreq is a multiple of 1000, the periodes are very accurate (12.5ns resolution). There is a jitter from the loop duration, but this is compensated every time because of the way the marks are increased.

    Andy
  • MarvinjohnMarvinjohn Posts: 1
    edited 2010-10-07 02:14
    Hi
    I appreciate the work you did.The code might be simple but it gave me new ideas to work with.
  • jmspaggijmspaggi Posts: 629
    edited 2010-10-07 03:57
    @Marvinjohn: You're welcome ;)

    @Ariba: Thanks for your comment. The dependency to the frequency is something I was thinking about and I will (try to) address soon in the next "release". ;)

    Regarding your code, let's take 2 situations.

    1) My goal is to monitor the outside temperature every 5 minutes. Will that work with your example?
    2) What will happen when CNT whill be $FFFFFFFF? 2 things. Since spin code integer are signed, are yout not going to wait for a while instead of just 500ms?

    I agree, you can add some test conditions to get rid of that. But the goal of my object is to remove all those test conditions and calculations from the main class to make is lighter, to allow a timer which work on all conditions (even when CNT is $FFFFFFFF), and allow longer delays compared to what CNT alone allow.

    JM
  • mparkmpark Posts: 1,305
    edited 2010-10-07 10:25
    First of all, kudos for putting your code out there.
    But I was looking at the comments in your code and there's something odd about the numbers.
    This timer is using internal CNT propeller counter and is incremented
    every 12.8us only (every 10000 clock cycles at 80Mhz). Which mean accuracy
    will be +/- 12.8us.
    Shouldn't that be 12.5us (every 1000 clock cycles at 80MHz)?
    Therefore:
    1 millisecond = 78
    1 second = 78,125
    I think 1ms should be 80, 1s 80,000, etc.
  • JonnyMacJonnyMac Posts: 9,208
    edited 2010-10-07 10:34
    JM:

    Timer objects are always handy, but if your goal is just to measure something every five minutes and your measurement takes less than a millisecond, you can do it in Spin (I have). One of the many clever programmers in the forum posted this header that is default in my programs:
    con
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000   
    
      CLK_FREQ = ((_clkmode - xtal1) >> 6) * _xinfreq
      MS_001   = CLK_FREQ / 1_000
      US_001   = CLK_FREQ / 1_000_000
    

    Note that I added the MS_001 and US_001 constants. With this you could create a "background" Spin cog like this (launch with cognew):
    pub measuretemp(taddr) | t, ms, temp
    
      ms := 0
      t := cnt
      repeat
        waitcnt(t += MS_001)
        if (++ms == constant(5 * 60 * 1000))
          ' measure temperature (in temp)
          long[taddr] := temp
          ms := 0
    

    For low-resolution events I've been coding like this -- easy to code, update, and modify.
  • jmspaggijmspaggi Posts: 629
    edited 2010-10-07 11:07
    @mpark:
    Hi Mark,
    mpark wrote: »
    Shouldn't that be 12.5us (every 1000 clock cycles at 80MHz)?
    I think 1ms should be 80, 1s 80,000, etc.

    Oups. Comments are wrong. I will fix that too. It's every 1024 clock cycles. This is to have faster tick method. That's why it's 12.8 and not 12.5. I will fix that. And thanks for the kudos ;)


    @JonnyMac:
    JonnyMac wrote:
    waitcnt(t += MS_001)

    Hi Jonny,
    When CNT will move from $FFFFFFFF to 0, will you not have an issue whith this line? I'm not 100% sure, because t is signed.

    Also, if the same loop is used for other purposes, you not necessary want to delay everything by one MS.

    In my application, I already have one main loop for all the recuring processess. Like reading the input devices (switchs, etc.), updating the display if required, etc. Adding a tick in this loop save me a cog, and allow me to add timers to few options (display the time, get the temperature, etc.). This can take more than 1ms. Wich mean I will loose the CNT target and have to wait for a full circle.

    In my timer, I can call the tick only once every 2 seconds and I should still be correct. Since tick take about 5us, I can call tick between 5us and 2 seconds. Quit flexible.

    I don't say all the options proposed on this thread are bad, I'm just saying that they are differents. Me, I need multiple timers (One to get the temperature, one to display the time, one to log the values, one to ping the distance, etc.). The purpose of this object is to simplify and clear the main loop.

    I'm not very good with signed numbers, but I think the behaviour of t += MS_001 might not be what's exected to be when we reach the limits.

    JM
  • max72max72 Posts: 1,155
    edited 2010-10-07 14:22
    You have no sign issue, because the variable you use to monitor the time wraps around, and the counter behaves in the same manner.
    Same thing applies if you use something like that;
    if (cnt-t)>0
    doyourcode

    being continuous and wrapping around everything is safe.
    If you are unsure test a small counter. When in doubt I use a 4-8 bit test number.
    Mechanics is the same, but it is easier to follow..
    Massimo
  • jmspaggijmspaggi Posts: 629
    edited 2010-10-07 14:31
    Ok. I see.

    Thanks.

    I will try to spend sometime on the Propeller documentation this evening to look at the signed numbers.

    So the only risk with waitcnt(t += MS_001) is to take more than 1ms to execute the code instruction and then miss the target.

    Thanks,

    JM
  • mparkmpark Posts: 1,305
    edited 2010-10-07 15:58
    I can't say I understand exactly what your code is doing, but I'm pretty sure you can simplify this:
    LONG[LONG[@lastCNTs][index]] := currentCNT
         ^^^^^^^^^^^^^^^^^^^^^^
    
    to this:
    LONG[lastCNTs[index]] := currentCNT
    
    Similar simplifications apply throughout.
  • AribaAriba Posts: 2,690
    edited 2010-10-07 16:20
    jmspaggi wrote: »
    @Ariba: Thanks for your comment. The dependency to the frequency is something I was thinking about and I will (try to) address soon in the next "release". ;)

    Regarding your code, let's take 2 situations.

    1) My goal is to monitor the outside temperature every 5 minutes. Will that work with your example?
    2) What will happen when CNT whill be $FFFFFFFF? 2 things. Since spin code integer are signed, are yout not going to wait for a while instead of just 500ms?

    I agree, you can add some test conditions to get rid of that. But the goal of my object is to remove all those test conditions and calculations from the main class to make is lighter, to allow a timer which work on all conditions (even when CNT is $FFFFFFFF), and allow longer delays compared to what CNT alone allow.

    JM

    2) is already answered.

    to 1) My example works up to ~27 seconds at 80MHz (half the rollover time of cnt). So if you need times like 5 minutes your object makes a bit more sense.
    I would just add a prescaler like this:
    PUB main | ms, mark1, mark2, scale
       ms := clkfreq/1000
       scale := 60
       mark1 := mark2 := cnt
       repeat
         if cnt-mark1 > 500*ms
           mark1 += 500*ms
           'Perform a periodic action every 500ms.
         if cnt-mark2 > 5000*ms
           mark2 += 5000*ms
           if --scale == 0
             scale := 60
             'Perform another periodic action every 5 minutes
    
    So you have still the high resolution but also very long intervalls.

    Andy
  • JonnyMacJonnyMac Posts: 9,208
    edited 2010-10-07 16:22
    So the only risk with waitcnt(t += MS_001) is to take more than 1ms to execute the code instruction and then miss the target.

    You can always change your timing resolution. For example, if 5ms resolution works an you need the time for measurement you could change that line to:

    waitcnt(t += constant(5 * MS_001))

    I've done this, too. Of course, you also need to update the line that increments your milliseconds counter that triggers the measurement.
Sign In or Register to comment.