Shop OBEX P1 Docs P2 Docs Learn Events
Consistent Loop Delay with Counter Wrap-Around — Parallax Forums

Consistent Loop Delay with Counter Wrap-Around

coryco2coryco2 Posts: 107
edited 2014-02-07 10:42 in Propeller 1
I am looking for the best way to insert a pause into a repeat loop of code that will cause each loop to take "ms" before the "next command" is executed. (Other not shown commands in the loop vary in their execution time and may include loopstart:= cnt to reset the loopstart time.) I came up with with the following two approaches, and would appreciate any comments or suggestions. I'm looking to get an accurate, consistent delay, and avoid problems with system counter wrap-around.
ms:=50

REPEAT
  loopstart:= cnt
  
  'code with variable execution time

  REPEAT UNTIL ||(cnt - loopstart) => ((clkfreq/1000)*ms)    'Approach 1
  '...next command

  waitcnt (((clkfreq/1000)*ms) + loopstart)       'Approach 2
  '...next command

Comments

  • dbpagedbpage Posts: 217
    edited 2014-02-06 16:05
    I do not like to see 'cnt' on the left side of an equation. I consistently use Approach 2.
  • kuronekokuroneko Posts: 3,623
    edited 2014-02-06 16:18
    dbpage wrote: »
    I do not like to see 'cnt' on the left side of an equation. I consistently use Approach 2.
    The issue with both of them is that they are drifting. This may not be a problem depending on your requirements.
    delay  := clkfreq / 1000 * 50{ms}
      target := cnt
    
      repeat
      
        'code with variable execution time
    
        waitcnt(target += delay)
    
  • coryco2coryco2 Posts: 107
    edited 2014-02-06 18:07
    Thanks for the feedback.
  • JonnyMacJonnyMac Posts: 9,107
    edited 2014-02-06 18:28
    I use fixed-time loops all the time. Generally, they're structured like this:
    pub main | t
    
      t := cnt
      repeat
        waitcnt(t += (clkfreq / 1000 * 50))
    
        ' code here runs every 50ms
    


    You just have to prevent your code from running longer than your intended delay
  • dbpagedbpage Posts: 217
    edited 2014-02-06 19:57
    What would you recommend if my code occasionally runs longer than my intended delay? I'd like the system to recover from occasional overtime executions.
  • jmgjmg Posts: 15,173
    edited 2014-02-06 20:09
    dbpage wrote: »
    What would you recommend if my code occasionally runs longer than my intended delay? I'd like the system to recover from occasional overtime executions.

    It will recover on the cases above, the counter never waits for more than ~53 seconds! ;)

    If you want to avoid that, another test to check if the cnt is already ahead of the wait-point is needed.
  • AribaAriba Posts: 2,690
    edited 2014-02-06 20:18
    dbpage wrote: »
    What would you recommend if my code occasionally runs longer than my intended delay? I'd like the system to recover from occasional overtime executions.
    This code should work with occasionally overtimes (up to ~27s).
    The overtime will be compensated in the next loop(s).
    pub main | t, ms
    
      ms := clkfreq/1000
      t := cnt
      repeat
        t += 50*ms
        repeat until cnt - t > 0
    
        ' code here runs every 50ms
    

    Andy
  • lonesocklonesock Posts: 917
    edited 2014-02-07 10:42
    I would use something like this:
    ' increment and delay
        endt += dt + (cnt - endt) / dt * dt
        repeat
        while (cnt - endt) < 0
    
    Note that endt will advance by one period at least, with optional additional integer multiples of dt if cnt has already overrun the next end time. Also, note that splitting the "repeat while" into 2 lines is both faster, and takes 2 fewer Spin bytecodes...no idea why.

    If you wanted to drop into low-power mode using waitcnt, you would need some extra safety margin, something like:
    waitcnt( endt += (2800 + dt - endt + cnt) / dt * dt ) ' notice I sample cnt as late as possible
    
    I could get it to fail (lock while the counter does a complete roll-over) with a safety margin of 2500. 2800 seems to be working for me, but I would bump that up if using this in production code. Note that you could wrap the safety value and dt into another variable...it would be a tiny bit faster, at the expense of another variable and setup.

    Jonathan
Sign In or Register to comment.