Shop OBEX P1 Docs P2 Docs Learn Events
Precise Timing — Parallax Forums

Precise Timing

John BoardJohn Board Posts: 371
edited 2015-06-18 01:11 in Propeller 1
G'day,

I'm sure there's information on this somewhere - I just haven't been able to find it in my time looking.

Let's say I have a propeller, running on an external 5MHz Crystal, with the _CLKMODE as XINFREQ+PLL16X, and _XINFREQ set to 5_000_000. Now let's say in SPIN I want to pause the code for 50uS (more or less) precisely.
CON
  _clkmode = XTAL1+PLL16x
  _xinfreq = 5_000_000

PUB Main

  DIRA[0]~~

  repeat
    OUTA[0]~~
    waitcnt(3619+cnt)
    OUTA[0]~
    waitcnt(3619+cnt)

I have a logic analyzer operating at 100MHz hooked up to pin 0, reading the time between high and low.

Now, in my rough calculations I figured 3619 to be around 50uS. I wasn't sure whether cnt is updated at 5MHz, or 80MHz. My first calculation was 5 (clock cycles for 1uS @ 5MHz) * 50 (number of uS) - 381 (overhead of waitcnt(<offset> + cnt) as defined in the Propeller Manual, pp. 221). This caused a very short delay, so lead to my next calculation of 80 (clock cycles for 1uS @ 80MHz) * 50 (number of uS) - 381 (overhead of waitcnt(<offset> + cnt) as defined in the Propeller Manual, pp. 221).

The calculation @ 80MHz produced a much more sensible 57.2uS delay (timing taken between rising and falling edge of the square wave). This error equates to 572 clock cycles, which can undoubtedly be attributed to the OUTA command.

My question is this, how does one calculate the number of clock cycles required for operations in SPIN - or more specifically where do those 572 cycles go to?

Thanks for your expertise and time,

John Board

Comments

  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-06-18 01:02
    I think this will do what you want.
    CON
    
      _clkmode = xtal1 + pll16x                           
      _xinfreq = 5_000_000
    
      CLK_FREQ = ((_clkmode - xtal1) >> 6) * _xinfreq
      MICROSECOND = CLK_FREQ / 1_000_000
      _50US = 50 * MICROSECOND
      
    PUB Main | time
    
      DIRA[0]~~
    
      time := cnt + _50US
      
      repeat
        OUTA[0]~~
        waitcnt(time += _50US)
        OUTA[0]~
        waitcnt(time += _50US)
    

    There's a section in the Propeller Manual about precise time keeping and how one can add a constant about of time to the delay amount. Using this technique you don't have to compensate for the overhead since it's included in the total time.

    BTW, I believe the statement "outa[0] := 1" is executed faster than "outa[0]~~". It doesn't matter in this case but there may be times when you want to save a few clock cycles.
    Oh, waitcnt waits clock cycles. Each clock cycle is 1/80,000,000 of a second (aka 12.5ns).

    Another by the way.

    This:
    repeat
        OUTA[0]~~
        waitcnt(time += _50US)
        OUTA[0]~
        waitcnt(time += _50US)
    

    Could also be written as:
    repeat
        !OUTA[0]
        waitcnt(time += _50US)
    
  • John BoardJohn Board Posts: 371
    edited 2015-06-18 01:11
    Duane Degn wrote: »
    I think this will do what you want.
    CON
    
      _clkmode = xtal1 + pll16x                           
      _xinfreq = 5_000_000
    
      CLK_FREQ = ((_clkmode - xtal1) >> 6) * _xinfreq
      MICROSECOND = CLK_FREQ / 1_000_000
      _50US = 50 * MICROSECOND
      
    PUB Main | time
    
      DIRA[0]~~
    
      time := cnt + _50US
      
      repeat
        OUTA[0]~~
        waitcnt(time += _50US)
        OUTA[0]~
        waitcnt(time += _50US)
    

    There's a section in the Propeller Manual about precise time keeping and how one can add a constant about of time to the delay amount. Using this technique you don't have to compensate for the overhead since it's included in the total time.

    BTW, I believe the statement "outa[0] := 1" is executed faster than "outa[0]~~". It doesn't matter in this case but there may be times when you want to save a few clock cycles.
    Oh, waitcnt waits clock cycles. Each clock cycle is 1/80,000,000 of a second (aka 12.5ns).

    Another by the way.

    This:
    repeat
        OUTA[0]~~
        waitcnt(time += _50US)
        OUTA[0]~
        waitcnt(time += _50US)
    

    Could also be written as:
    repeat
        !OUTA[0]
        waitcnt(time += _50US)
    

    Thanks, I really appreciate the help! [EDIT] Just looking over the code - that's a really clever way to take the OUTA waiting time out of the equation (using the time variable).

    As a side note, which you may be interested in, I've been working on an nRF24L01+ driver - not to replace though, but just so I have a better understanding of how everything fits together. This timing code is just me seeing how fast I can crank the chip, according to the timing specs in the doc. The process of writing the driver was more to help me learn how SPI works than anything else.

    -John
Sign In or Register to comment.