Shop OBEX P1 Docs P2 Docs Learn Events
Time Delay in SPIN — Parallax Forums

Time Delay in SPIN

If I can calculate a time delay required in milliseconds, how do I implement it. I think I have to convert it to clock ticks and use waitcnt(clockticks), but I'm not really sure. The attached code does not work but exemplifies what I'm trying to do. I'm pretty new with spin and am trying to learn from this Parallax book which doesn't have many examples. Any help would be appreciated.
Thanks
«1

Comments

  • The Propeller Education Kit Fundamentals book (chapter 4) has a very good section on time delays in Spin. You can get it from the downloads section of the product page. I think the direct link will work.

    Tom

    https://parallax.com/sites/default/files/downloads/122-32305-PE-Kit-Labs-Fundamentals-Text-v1.2.pdf
  • The Education book, the one I am using, only has very specific examples of how to make a particular delay, but not a generated delay. The generated delay could change on each revolution of the cam.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-10-12 18:30
      timer := -cnt
      ' do stuff to be timed
      timer += cnt
      timer /= clkfreq / 1000
      ' timer now holds the interval in milliseconds
    

    This is a way to do it with one variable.
  • How about the reverse, If I want to do something for 34 milliseconds, or a variable amount time.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-10-12 19:40
    It sounds like you're wanting to run a loop with variable timing. This will work as long as your minimum loop time is longer than the amount of time required to run the code inside the loop,
      t := cnt
      repeat
        ' loop code here; modify millis variable
    
        waitcnt(t += (clkfreq / 1000 * millis))
    
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-10-12 20:30
    Beavis3215 wrote: »
    How about the reverse, If I want to do something for 34 milliseconds, or a variable amount time.

    I'd start by converting ms to clock ticks to reduce the time performing calculations.
      timeOut := 34 ' sent in ms
      timeOut *= clkfreq / 1000 
      startTime := cnt
      repeat while cnt - startTime < timeOut
        ' do stuff until timeout occurs. 
    

    If you want the loop executed at least once.
      timeOut := 34 ' sent in ms
      timeOut *= clkfreq / 1000 
      startTime := cnt
      repeat 
        ' do stuff until timeout occurs. 
      while cnt - startTime < timeOut
    

    Another way to do the same thing. (Well, almost the same thing. You could use "=>" instead of ">" to make it the same. I usually don't worry about a single clock tick.)
      timeOut := 34 ' sent in ms
      timeOut *= clkfreq / 1000 
      startTime := cnt
      repeat 
        ' do stuff until timeout occurs. 
      until cnt - startTime > timeOut
    

    This sort of time comparisons only work for intervals less than half the rollover time or about 26 seconds at 80MHz.

    I usually follow JonnyMac's example and use a constant for milliseconds rather than computing it at run time.
    CON
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
      CLK_FREQ = ((_clkmode - xtal1) >> 6) * _xinfreq
      MICROSECOND = CLK_FREQ / 1_000_000
      MILLISECOND = CLK_FREQ / 1_000
    

    The above section of code is copied from one of Jon's programs. I don't like to use abbreviations so I changed the constant names to fit my coding style. I believe Jon used "MS_001" where I used "MILLISECONDS" and "US_001" where I used "MICROSECOND."

    I copied the line "CLK_FREQ = ((_clkmode - xtal1) >> 6) * _xinfreq" from Jon's code but I've since seen this same line used in the "Clock.spin" object.

    Jon wrote a Spin Zone article about keeping track of time with the Propeller. There are links to Jon's Spin Zone articles in the third post of my "index" (see my signature for a link). If my signature doesn't show up in this post, just an earlier post.
  • Thanks a lot for your help, your examples helped me get it working. Cant download the movie of it working :frown:
    3264 x 2448 - 1M
    2448 x 2448 - 2M
  • This is my current timing header -- the CLK_FREQ line is simplified based on forum suggestions:
    con { timing }
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000                                           ' use 5MHz crystal
    
      CLK_FREQ = (_clkmode >> 6) * _xinfreq                          ' system freq as a constant
      MS_001   = CLK_FREQ / 1_000                                    ' ticks in 1ms
      US_001   = CLK_FREQ / 1_000_000                                ' ticks in 1us
    
  • Is there any reason why this program, counting milliseconds, would act any differently counting microseconds? Substituting waitcnt(clockfreq/1000000+cnt). It does and I'm not sure why. As you add zeros the program becomes more unstable in that it cant get past the first loop to set outa[16]. 10000 will "usually work" but not more. I almost think it is hardware related as some of my complications have been, especially considering the "usually" from above.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-10-18 22:25
    You're doing a lot to hurt yourself there, Beavis

    * Get rid of unnecessary inline calculations (these take time). See my post just above your for a clock-sensitive constant that you can replace clkfreq/1000 with
    * Use waitpne(pinmask, pinmask, 0) instead of ina[pin] == 0; likewise with waitpeq for ina[pin] == 1
    * If you're going to do an inline waitcnt, put cnt as the first element of the expression

    Keep in mind that Spin instructions take a few microseconds each; it would be better to measure a short event like this:
      elapsed := -cnt
      ' code to measure here
      elapsed += cnt - 544
    

    I have used this many times. The elapsed time value is in system ticks; you can divide it by a constant to get microseconds.

    Here's my idea of what your code should look like -- of course, it will still need tuning and, perhaps, conversion to PASM to be as precise as you may want it.
    pub ecu1_jm | ms, td
    
      dira[CAM] := 0
    
      outa[COIL] := 0
      dira[COIL] := 1
    
      waitpeq(1 << CAM, 1 << CAM, 0)                                 ' wait for cam high
      waitpne(1 << CAM, 1 << CAM, 0)                                 ' wait for cam low
    
      repeat
        ms := -cnt
        waitpeq(1 << CAM, 1 << CAM, 0)
        ms += cnt - 544
    
        td := 30 * ms / 360
        waitcnt(cnt + (td * MS_001))
        outa[COIL] := 1
        waitcnt(cnt + (td * 6 * MS_001)) 
        outa[COIL] := 0
    


  • I am really a beginner, trying to learn really fast. I appreciate your help. I need to study your code a while to get a grip on what it really means. I am this close to understanding it but some of the code is unfamiliar to me. My code is about as well as I could get it skimming it out of the propeller textbook. Again, thanks for you help. What is the 544? I assume that I can substitute US_001 to break down elapsed time in finer increments, or how do I do this? What exactly is ms counting?
  • I am thinking ms is in clock ticks and 544 is compensating for time waisted in the code?
  • kwinnkwinn Posts: 8,697
    Beavis3215 wrote: »
    I am thinking ms is in clock ticks and 544 is compensating for time waisted in the code?

    Correct.

    Spin is too slow for single (or even a few) microsecond timing since a single spin instruction can take more than a microsecond to execute. For that you need PASM.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-10-19 05:12
    Beavis3215 wrote: »
    I am really a beginner, trying to learn really fast. I appreciate your help. I need to study your code a while to get a grip on what it really means. I am this close to understanding it but some of the code is unfamiliar to me. My code is about as well as I could get it skimming it out of the propeller textbook. Again, thanks for you help. What is the 544? I assume that I can substitute US_001 to break down elapsed time in finer increments, or how do I do this? What exactly is ms counting?

    The 544 compensates for the cycles required for the two Spin instructions (starting and stopping the event timing. If you do this:
      elapsed := -cnt
      ' do nothing
      elapsed += cnt - 544
    

    If you print elapsed you'll get zero; again, the - 544 compensates for the setup and conclusion of the timer.

    Once you've measured something it ticks you can determine microseconds by dividing by US_001. Note that if you want to do an inline delay, the shortest you can do in Spin is about 45 microseconds.

    I don't care if you're a beginner; I will treat you with the respect you deserve and give you the best possible code that I can. I try to write clean code that others can easily digest and learn from.

  • One of the delays, waitcnt(cnt+(td*MS_001)) is in milliseconds? If I want it in microseconds it would be (td*US_001)?
    Does the 544 have anything to do with "doing something", or how many times you do Nothing or Something? It looks like from one of your examples the answer should be no. I think that I remember you helping me about 6 years ago in my SX days.:) When you say tuning above, what exactly do I adjust. I do have an oscilloscope that I use to monitor the cam rotational period and td.
  • How can I get started learning pasm. Where do I get the assembler? I do have experience in SX asm.
  • If you could summarize in one paragraph the time handling inefficiency of inline calculations, and your method, what would that be? Why doesn't the waitpeq command in the repeat loop above eat up time?
  • The reason why I am asking about the delay above, is that after crunching my first set of actual running data, that td is only about 6 milliseconds at 1050 rpm. Timing control would be way more accurate with td in microseconds. In later versions I will be expanding this system to automate td from a map of data, and control a fuel injector in a similar way. Of course I will be adding more sensors along the way.
  • In my running data I did see evidence of the problems you said I would have.
  • How do cam and coil get tied to pins, or is this an example?
  • Please explain the (state,mask,port) syntax of the waitpeq and waitpne that was used? Please explain <<, bitwise shift left? Please Explain.
  • Beavis3215 wrote: »
    Please explain the (state,mask,port) syntax of the waitpeq and waitpne that was used? Please explain <<, bitwise shift left? Please Explain.

    See:
    https://www.parallax.com/downloads/an001-propeller-p8x23a-counters
    https://www.parallax.com/downloads/an009-code-execution-time-p8x32a

  • How do you print "elapsed" in the above example?
  • This should be a good start less tuning. See if there are any problems.
  • I found an error in the logic in my program, again concerning timing. The steps should go
    1) Wait for cam to go high
    2) Wait for cam to go low, completing cam sync.
    3) Wait for cam to go high, start timer during which cam goes low.
    4) Wait for cam to go high, stop timer and record ms (time = time+ delay + duration)?
    5) Start timer again during which cam goes low
    6) Calculate delay using ms
    7) Execute delay while still timing
    8) Fire for duration while still timing
    9) Reset coil
    10) goto 4)

    I think this can be solved by adding MS = MS + 7 * (waitcnt(cnt + TD * US_001)) after calculating MS. I'm doing 2 delays then starting the timer late by this much. Will delay and duration autoinitialize to zero on the first pass?

    Will this approach work or is there a better way?
  • Or I could let MS = 7 * (waitcnt(cnt +TD * US_001)) at the end, to be carried into the next timer cycle as an initial MS. Worried about unaccounted for cycles I'm adding.
  • Would MS = -cnt + (7*TD), after repeat work, to start the timer with this initial amount? 7 * TD along with some ticks for commands should be the unaccounted for time.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-10-20 17:07
    For most programs you should define your IO pins as constants.
    con
    
      COIL = 16
      CAM  = 15
    

    Also, it takes more work to deal with bytes and words as they need to be extracted from longs (Propeller's normal data size) by the interpreter. Shorter: For general vars, use longs.
  • Ultimately later I think a lot of this will be simplified by making a cog called fire, for firing the coil using TD. This way the delays during the firing sequence can be run in parallel with the cam timing sequence instead of inline with it. Is this line of thinking correct?
Sign In or Register to comment.