Shop OBEX P1 Docs P2 Docs Learn Events
Timer — Parallax Forums

Timer

hylee101001hylee101001 Posts: 48
edited 2015-07-25 07:04 in Propeller 1
I have been trying to make a timer that can pause about 30 sec. Half of the trial, my code works, but not the other half of the chance. So far, I figured that cnt is 16 bit number, and in decimal it varies -2^31 ~ 2^32 -1 
So, if the cnt starts from negative region, it works, but if the cnt happens to return the positive region, due to the overflow, it messes up. So, to fight to that, I wrote the following code but still doesn't work well. Please help me. 
Also, the weird thing is that this absolute function below, alwyas seems returning y value no matter what the calculation is. I do not know what I am missing. I thought this is the only way I could bypass the overflow..

What this code does is to measure the difference between current clock and the target cnt value and compare that with clkfreq so it stops when the clock is about 30 second elapsed. 




PUB main  | finish  , check, base
  usb.quickStart
  base := cnt  finish := cnt + clkfreq*30  check := absolute(cnt, finish)  repeat  while (check-clkfreq > 0)    usb.str(String("elasp:"))    usb.decLn((cnt-base)/clkfreq)       usb.str(String("remaining: "))    usb.decLn((check-clkfreq)/clkfreq)           usb.decLn(cnt)    usb.decLn(finish)    check := absolute(cnt, finish)     usb.newline
    usb.newline    PRI absolute(x, y)  
  if (x>=y)    result := x-y  else    result := y-x    

Comments

  • edited 2015-07-25 07:24
    Look up waitcnt in the reference manual.  That should help get your program on track.

    Sandy
  • ChrisGaddChrisGadd Posts: 310
    edited 2015-07-25 11:54
      Cnt, like all registers in the Prop, is 32 bits wide and can range from -2147483648 up to 2147483647, however the precise value of cnt is unimportant for timing.  For a time delay that halts all operation, waitcnt(clkfreq * 30 + cnt).  Clkfreq * 30 + cnt gets evaluated, and no problem if it happens to overflow; the remaining 32 bits are compared against cnt.  For a time delay that doesn't halt the cog, which is what it looks like you're attempting, you need to compare the target count to the current count.
      targetCnt := clkfreq * 30 + cnt  repeat until cnt - targetCnt > 0    usb.decLn(||(cnt - targetCnt))      '  || is the absolute value operator    ...

    The problem in your absolute method is that x>=y is an assignment operator, equivalent to x := x > y. => is the operator for greater than or equal to.
  • hylee101001hylee101001 Posts: 48
    edited 2015-07-25 15:40
    Thanks to all,
    I should have mentiond that I wanted to not use waitcnt because I need to write data in sd card for 30 seconds. Is there a better way?
    If possible more than 30 seconds...
    Perhaps, I just realized that I need to use a cog with waitcnt that changes flag of a loop in the default cog.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-07-25 16:31
    In order to prevent overflow errors, a good way to measuring time is to subtract an earlier cnt reading from the present cnt. and compare it against an interval.
    In your case 30 is longer than you can measure time with a single interval. You can only measure intervals up to about 26 seconds (with a 80MHz clock).
    You'll want to count seconds. Use one of the RTC objects to track seconds. If you have trouble, let us know and we can help.
  • Check Spin Zone #7. It's about keeping time on the Propeller.https://www.parallax.com/downloads/propeller-spin-zone-articles-and-code
  •     For a time delay that doesn't halt the cog, which is what it looks like you're attempting, you need to compare the target count to the current count.


    Chris,I'm pretty sure (since I've been bit by it several times) you can only compare intervals up to half the rollover time. Intervals longer than half the rollover time end up being evaluated as negative.
  • AribaAriba Posts: 2,690
    edited 2015-08-06 21:34
    You can do longer time intervalls with several shorter intervalles and a counter that counts them.
    For example 30 times a 1 second wait:
    PUB Main : finish | oneSecond, scount
    
    usb.quickStart
    
    repeat
    oneSecond := cnt + clkfreq
    scount := 30
    repeat until scount == 0
    if oneSecond - cnt > 0
    OneSecond += clkfreq
    scount--
    usb.str(String(" remaining: "))
    usb.decLn(scount)
    usb.newline
    usb.str(String("30 seconds passed",13))
    

    For sure: the code in the loop must not take longer than 1 second to run here. Otherwise you can do for example 3 times a 10 second wait.

    Andy
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-07-25 19:03
    I have a project that uses all eight cogs and still needs to do timing for a bunch of features that is running in the mainline code. For that I came up with the attached object which takes care of the deltas between calls. The programmer needs to declare and start the object, then call any of the methods that mark the time. 
    pub main | check
      usb.quickStart  timer.start  check := 0
      repeat  while (check < 30_000)    check := timer.millis    usb.str(string("elapsed: "))    usb.decLn(check)       usb.str(string("remaining: "))    usb.decLn(30_000 - check)     usb.newline    usb.newline    timer.pause(5) ' prevent PST overflow
  • kwinnkwinn Posts: 8,697
    Check Spin Zone #7. It's about keeping time on the Propeller.https://www.parallax.com/downloads/propeller-spin-zone-articles-and-code


    I second this suggestion. JonnyMac's timing routines are very simple to use and provide delays in milliseconds, so longer time delays are not a problem.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-07-26 15:23
    Check Spin Zone #7. It's about keeping time on the Propeller.https://www.parallax.com/downloads/propeller-spin-zone-articles-and-code


    I second this suggestion. JonnyMac's timing routines are very simple to use and provide delays in milliseconds, so longer time delays are not a problem.


    Part of the reason that I developed my timer object is to avoid long BLOCKING delays; this is just a waste of bandwidth. This style is nothing new; I simply created an object that helps me implement scheduling type programs very easily. A lot of my programs are looking like this now:
    pub main | t1, t2, t3
      timer.start
      longfill(@t1, 0, 3)
      repeat    if ((timer.millis - t1) => T1_MILLIS)      run_process_1      t1 += T1_MILLIS
        if ((timer.millis - t2) => T2_MILLIS)      run_process_2      t2 += T2_MILLIS
        if ((timer.millis - t3) => T3_MILLIS)      run_process_3      t3 += T3_MILLIS      
  • Jon
    I have been somewhat following this thread, but your last post peeked my interest just a wee bit more.  Any chance I could convince you to provide a more full example of this source being used.
    Bruce
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-07-26 20:14
    I really don't have the time, and you may be making it harder than it is. Basically, this code calls methods on a set schedule. That said, you can at times have one process take a little more time than it should; this is why I use => in the check, but still bump the process timer by the scheduled amount.
    Let's say that a process wants to run every 5 milliseconds but something caused the actual delay to be 6 milliseconds. It will run, and the adjustment made to its timer will cause it to run again 4 milliseconds later (back on schedule).
    Mind you, this style is not for processes that require hyper-critical timing, and you want ensure that the combined time in your called processes is less than the shortest timer in your schedule.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-07-26 21:59
    Okay, I've attached a very simple demo. In this case I make the timer variables global so they can be checked/updated within their associated processes. It's just blinkly LEDs, but illustrates the use of the timer object.
    Another strategy that I uses is to create separate timer objects for different processes. When I do this I use the .adjust methods to back-up a timer for a new cycle.
    Last week I wrote some code for an escape house. In that case the client wanted a count-down time. No problem, I set the timer to a negative value and then use the absolute operator to fix it before sending it to a set of giant clock digits. That app takes advantage of my timer object's hold and release methods.
  • jmgjmg Posts: 15,173

    If possible more than 30 seconds...
    Perhaps, I just realized that I need to use a cog with waitcnt that changes flag of a loop in the default cog.

    You can always lower the SysCLK ?
      2^32/80M   = 53.687 sec
      2^32/5M    = 858.993 sec

  • Jon
    Thank you for providing such a nice example.  Your timer object looks pretty darn impressive.  If you ever have a project that you are willing to share, in which you utilize all the methods of the timer object, I would be very interested in seeing all the various methods being utilized or perhaps an updated timer article.
    Very nice...  Thank you.
    Bruce
  • I think if you'll just spend an hour playing you'll be able to do anything you want. I've already written about the first version, so I'm not going to do that again (well, I will, but not in N&V...).
    The key to understanding the timer is that the internal storage unit is milliseconds -- this would let me time an event of about 24 days. This is an event timer, not an RTC, so I don't see any reason to go beyond that. Any of the methods that deal with seconds are in fact updating the internal milliseconds value.

    I've recently worked on two commercial projects that required countdown timing. The attached demo gives you an idea how I do that. Note that I made a decision this morning to change my timer object to simplify putting it on hold with a specific value. Please overwrite the version you have now.
    George Lucas once said that movies are never finished, they're simply abandoned. That's how I feel about my objects. 

  • Jon
    Thanks for the insight and update.  Truly appreciated.
    I like the fact that you kept updating as needed.  Reusable code...  Got to love it.
    Bruce
Sign In or Register to comment.