Shop OBEX P1 Docs P2 Docs Learn Events
Simple Math — Parallax Forums

Simple Math

KaosKiddKaosKidd Posts: 296
edited 2013-05-22 16:42 in Propeller 1
This is one of the times I wish I had a scope. I'm working on my first timing sensitive project (The inkJet Printer project) and I need some clarification please.

At the top of my project I have :
_clkmode = xtal1 + pll16x 'Standard clock mode * crystal frequency = 80 MHz
_xinfreq = 5_000_000


There three things which I must "abide by" within my prjoect.
#1: the "shot" must last between 3.5 uS and 6.0 uS. This is how long a single element of the inkjet head must be on, and the max length it can be on.
Playing with the numbers and visually testing the outputs (via the vga term), I come up with:
WAITCNT(((CLKFREQ / 1_000_000) * 5)  + CNT)
which I believe to be a 5uS delay, well within my limits. Part of this is because when I go for the 3.5uS, I get close to a 15~20 second delay, which tells me the calculated wait period is less then the minimum the WAITCNT can use, which is about 384, or some place close to this. If I understand the math (And I'm just grasping this side of it), at this clock speed, 1uS = 100 clock cycles. Please, someone, correct any and every place where this paragraph may be wrong, because it's the logic I use for the next paragraph.

#2: My stepper motor driver states it's fastest drive rate is 250 Khz. If I am understanding things correctly, to get the max speed from things without over driving them, I would do something like this:
StepperWaiTime = (CLKFREQ / 230_000) / 2          'Use a lower number then max to avoid maxing out the driver
REPEAT StepperCount FROM 0 to NumOfSteps     'Counting the number of steps we need for this move
  OUTA[StepPin]                                                   'Turn on the pulse
  WAITCNT(StepperWaitTime + CNT)                      'Wait the minimum time
  OUTA[StepPin]~                                                 'Turn off the pulse
  WAITCNT(StepperWaitTime + CNT)                      'Wait the minimum time (just in case we are NOT done with the loop)

Without a scope, it's hard for me to confirm what I'm looking at in code (and on the screen and a few leds), so if anyone has better suggestions, ideas, comments or crits, please feel free to jump right in and correct me...

Thanks for any and all comments!
Fred

Comments

  • Heater.Heater. Posts: 21,230
    edited 2013-05-14 07:03
    A Cog only executes at 20 million instructions per second.(With an 80MHz clock)
    So 5 us is only time for 100 instructions to execute.
    I suspect you can't do that in Spin which takes about that many instructions per byte code to execute. (Someone might correct me here though).
    If I wanted to do this I would have started out in PASM.

    By the way you do have a scope. If you have a spare cog you can use it read the pin that you are driving and measure the pulse width it sees. Again I'd do that in PASM.
  • Heater.Heater. Posts: 21,230
    edited 2013-05-14 07:08
    What happens when you replace:
    WAITCNT(((CLKFREQ / 1_000_000) * 5)  + CNT)
    
    with
    WAITCNT(t + CNT)
    
    and pre-calculate t.
  • KaosKiddKaosKidd Posts: 296
    edited 2013-05-14 07:21
    ((CLKFREQ / 1_000_000) * 5) evaluates to 500 + CNT
    It takes (as close as I can tell) nearly the same amount of time for each. As long as I don't go over 6uS, which I'm calculating to be a wait of 600 + CNT, I'm good.
    I have read the propeller manual on this and it does say the minimum WAITCNT is like 386 or so due to over head. At this point, the numbers make sense, but I do NOT want to start burning out $15.00 inkjet heads because my math was off, hence the discussion.

    And, is there example code of a using a prop pin to measure the frequency of a pin on the same prop? How would that get wired? (Your idea above). I can always "include" this PASM code, and wire a temp pin setup to do my measurements...
  • Heater.Heater. Posts: 21,230
    edited 2013-05-14 08:00
    I have used a COG to measure the frequency produced on a pin by another COG. However you need to know the pulse width. I'm sure someone can chip in with advice about that.

    The nice thing is you don't have to do any physical wiring. All COGs have access to all pins. So your monitoring COG need only set the same pin to input and then read it.
  • PublisonPublison Posts: 12,366
    edited 2013-05-14 08:29
    KaosKidd wrote: »
    ((CLKFREQ / 1_000_000) * 5) evaluates to 500 + CNT
    It takes (as close as I can tell) nearly the same amount of time for each. As long as I don't go over 6uS, which I'm calculating to be a wait of 600 + CNT, I'm good.
    I have read the propeller manual on this and it does say the minimum WAITCNT is like 386 or so due to over head. At this point, the numbers make sense, but I do NOT want to start burning out $15.00 inkjet heads because my math was off, hence the discussion.

    And, is there example code of a using a prop pin to measure the frequency of a pin on the same prop? How would that get wired? (Your idea above). I can always "include" this PASM code, and wire a temp pin setup to do my measurements...

    Try JonnyMac's object in the OBEX:

    http://obex.parallax.com/object/308


  • JonnyMacJonnyMac Posts: 9,107
    edited 2013-05-14 14:00
    ... the "shot" must last between 3.5 uS and 6.0 uS.

    That's going to require PASM -- Spin is just not fast enough. One of the cool things about Spin, though, is that you can do timing tests without a 'scope -- I use this trick all the time. Let's say, for example, that you want to know how long it takes to take a pin high, then immediately back low. You can do this:
    dira[0] := 1 {output}
      
      elapsed := -cnt
    
      ' test code here
    
      outa[0] := 1 { on  }
      outa[0] := 0 { off }
    
      ' end of test code
    
      elapsed += cnt - 544
    


    The "- 544" at the end removes instruction overhead; the result is the number of ticks to run the test code. In this case it turns out to be 1120 ticks which, at 80MHz, is 14us -- so figure ~7us for the "on" time (which exceeds your requirement). The program is attached so you can experiment.
  • PublisonPublison Posts: 12,366
    edited 2013-05-14 14:12
    JonnyMac wrote: »
    That's going to require PASM -- Spin is just not fast enough. One of the cool things about Spin, though, is that you can do timing tests without a 'scope -- I use this trick all the time. Let's say, for example, that you want to know how long it takes to take a pin high, then immediately back low. You can do this:
    dira[0] := 1 {output}
      
      elapsed := -cnt
    
      ' test code here
    
      outa[0] := 1 { on  }
      outa[0] := 0 { off }
    
      ' end of test code
    
      elapsed += cnt - 544
    


    The "- 544" at the end removes instruction overhead; the result is the number of ticks to run the test code. In this case it turns out to be 1120 ticks which, at 80MHz, is 14us -- so figure ~7us for the "on" time (which exceeds your requirement). The program is attached so you can experiment.
    Nice to know Jon,

    New Avatar is nice too. :)
  • KaosKiddKaosKidd Posts: 296
    edited 2013-05-15 11:01
    JonnyMac wrote: »
    That's going to require PASM -- Spin is just not fast enough. One of the cool things about Spin, though, is that you can do timing tests without a 'scope -- I use this trick all the time. Let's say, for example, that you want to know how long it takes to take a pin high, then immediately back low. You can do this:
    dira[0] := 1 {output}
      
      elapsed := -cnt
    
      ' test code here
    
      outa[0] := 1 { on  }
      outa[0] := 0 { off }
    
      ' end of test code
    
      elapsed += cnt - 544
    


    The "- 544" at the end removes instruction overhead; the result is the number of ticks to run the test code. In this case it turns out to be 1120 ticks which, at 80MHz, is 14us -- so figure ~7us for the "on" time (which exceeds your requirement). The program is attached so you can experiment.

    Thank you John!
    I was playing with your object and the first bits of code and I was coming close to that figure... what I couldn't come up with was the overhead number, and what's funny is your code could be used to come up with exactly that overhead figure as well... Nice... slick and nice..

    So now I guess I have to write a PSAM routine... so it's time for me to start playing...

    Maybe something simple that will monitor a pin... and when it goes high... it waits for {delay} then make it go low...

    Thank you again everyone...
  • KaosKiddKaosKidd Posts: 296
    edited 2013-05-22 07:06
    Ok, I've done a LOT of reading, working on my first PASM routine, and with the help of LOTS of people here, I've come into what I believe is my first good attempt.
    However, I'm not sure of a few things here.

    First off, the Spin code:
    CON
            _clkmode = xtal1 + pll16x                                               'Standard clock mode * crystal frequency = 80 MHz
            _xinfreq = 5_000_000
    
    VAR
      long  COG, Period, tPin1, tPin2, CMD                  'These are the vars needed to start, in this exact order
      
    OBJ                                                     'No external objects for this object
    
    PUB Init (Pin1, Pin2) | Ok                              'Start here, calling with the two pins needed... NOT A RANGE
      Finalize                                              'Stop if it's already running
      Period := (clkfreq / 1_000_000) * 4                   'Calculatethe pause period
      tPin1 := Pin1                                         'Save the pin1 for retrieval in PASM
      tPin2 := Pin2                                         'Save the pin2 for retrieval in pasm
      CMD := 0                                              'Force CMD into a knowen state of waiting before launching
      ok := cog := cognew(@Entry,@Period)                   'launch into a new cog, the assembly below with a pointer to entry
    
    PUB Finalize                                            'Routine to end the assembly and free the cog
      if cog                                                'if cog is non zero (meaning it's already running)
        cogstop(COG)                                        'Stop the cog that is running the assembly
    
    PUB Shoot                                               'Routine that actually shoots the inkjets
      CMD := 1                                              'Set the trigger for assembly to start
      repeat until CMD == 0                                 'wait until assembly is done 
    

    This code was made from the bits and parts scattered throughout the various objects I looked at, trying to find the best way to have an PASM routine that toggled two pins for calculated (not changing) routine. No questions here. However, I'm always open to suggestions about better ways of doing this.

    The PASM code:
    DAT
                    'Load the param's that were passed into this routine
    Entry           org             0                       'We start here
                    mov             Temp,par                'Move the address from the incomming par register into Temp
                    rdlong          Delay, Temp             'Move the period from the incomming par register into Delay
                    add             Temp, #4                'add 4 (long) to Temp to point to the next variable we need
                    rdbyte          PinOne, Temp            'Move the Pin value into PinOne from the passed array
                    add             Temp, #4                'add 4 (long) to Temp to point to the next variable we need
                    rdbyte          PinTwo, Temp            'Move the Pin value into PinTwo from the passed array
    
                    'Create & save Masks, set outputs and preset low's for both pins to be worked with
                    mov             Temp, #1 wz             'load temp with 1
                    shl             Temp, PinOne            'Create mask for Pin 1
                    mov             Mask1, Temp             'Save the mask for pin 1
                    muxz            outa, Temp              'Preset pin to low
                    muxnz           dira, Temp              'Set pin to output
                    mov             Temp, #1 wz             'load temp with 1
                    shl             Temp, PinTwo            'Create mask for pin 2
                    mov             Mask2, Temp             'Save the mask for Pin 2
                    muxz            outa, Temp              'Preset pin to low
                    muxnz           dira, Temp              'Set pin to output
    
                    'Force Cmd to be 0 for a clean start
                    mov             Temp, #0                'Set Temp to 0
                    wrlong          Temp, PCmd              'force current command to be 0
    
    Main
                    rdlong          Temp, PCmd wz           'Check for a command
            if_z    jmp             #Main                   'If the new command is 0 then jump back to the top
                    mov             Time, cnt               'put the value of cnt into time 
                    add             Time, #9                'Add our min wait time
                    xor             outa, PinOne            'Turn on pin 1 (toggle because we set it to 0 above in the setup)
                    xor             outa, PinTwo            'Turn on pin 2 (toggle because we set it to 0 above in the setup)
                    waitcnt         Time, Delay             'Wait until it's time to turn it off
                    xor             outa, PinOne            'Turn off pin 1 (toggle because we know it's on from above)
                    mov             Temp, #0                'Load a 0 into temp
                    rdlong          Temp, PCmd              'Load 0 into tem
                    jmp             #Main                   'Loop for more commands
    
    Delay           long            1                       'Duration to hold the pins high for
    PinOne          long            1                       'Starting Pin to affect
    PinTwo          long            1                       'Ending pin to affect
    PCmd            long            1                       'Holds the current command
    Temp            res             1                       'temp working variable
    Mask1           res             1                       'Mask for pin 1
    Mask2           res             1                       'Mask for pin 2
    Time            res             1       
    

    After doing a LOT of reading, Using Beau's code:
    My personal opinion on this matter is to use a data MASK for the PIN you want to affect via the Propeller MUX commands.
    Example #1 (Set Pin as a OUTPUT - Preset with LOW)
                  mov     t1,             #1        wz      '     Configure Pin
                  shl     t1,             Pin               '          Create Mask with t1   
                  muxz    outa,           t1                '          PreSet DataPin LOW           "0"
                  muxnz   dira,           t1                '          Set DataPin to an OUTPUT     "1"
    
    {{Other code removed }}}
    


    'Pin' holds a value ranging between 0 and 31 corresponding to the I/O pin you want to affect.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 8/21/2006 4:19:52 AM GMT

    My goal was to precalculate and save the masks and delay, thus the loop would be fast as possible.
    My first question is when I move from Temp into Mask1, does temp get emptied?
    My next question is about the main loop. I used Parsko's code as a template for the timing from this post (Edited for content and space)

    parsko wrote: »
    ......
    con
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    pub Toggle_Main
      cognew (@Toggle, 0)
    dat
                            org       0
    Toggle
                  mov     dira,   Pin                'Set Pin to output
                  mov     Time,   cnt                'Place the value of cnt into Time
                  add     Time,   #9                 'Add 9 to time
    :loop
                  waitcnt Time,   Delay              'Set Pin high
                  xor     outa,   Pin                'Toggle Pin
                  waitcnt Time,   Delay              'Set Pin Low
                  xor     outa,   Pin                'Toggle Pin
                  jmp     #:loop
    
    Pin     long            |< 1
    Delay   long            40_000_000
    Time                    res 1
    
    

    {{parts reoved}}
    -Parsko

    My Questions are about the timing loop:
    Why are we adding #9 to the delay?
    And how does it handle when CNT rolls?

    I do plan on testing once I reclaim my desk again, and test with a longer duration, but these questions just beg the asking.

    And again, as always, any comments on how to do it better, please let me know.

    Fred
  • lonesocklonesock Posts: 917
    edited 2013-05-22 08:34
    Just a quick note: you can use a counter to make a precise pulse in Spin. See this thread.

    Jonathan
  • KaosKiddKaosKidd Posts: 296
    edited 2013-05-22 13:42
    lonesock wrote: »
    Just a quick note: you can use a counter to make a precise pulse in Spin. See this thread.

    Jonathan
    Thanks Jonathan, I'm checking into it.
    Fred
  • kuronekokuroneko Posts: 3,623
    edited 2013-05-22 16:42
    KaosKidd wrote: »
    My Questions are about the timing loop:
    Why are we adding #9 to the delay?
    And how does it handle when CNT rolls?
    You can add any number you want, you just end up waiting more or less before the loop starts. For this sequence 9 happens to be the minimal delay so the next (first) waitcnt doesn't in fact wait (just consumes its minimum insn time). Sampling cnt happens in the 3rd cycle of the mov insn IOW you see the result of the 2nd cycle. waitcnt samples/compares first time in its 4th cycle (result of 3rd). That's already a minimum delay of 5 we have to cover. Now we insert another insn between them to take care of that adjustment which gives us 5min + 4adj = 9 cycles. Placing the adjustment insn before the other two lets us get away with 5.

    As for rollover, doesn't really matter here. You're just confined to certain bounderies, e.g. the loop has a minimum execution time so your (minimum) output period is limited by that (you can in fact remove one waitcnt/xor pair).
Sign In or Register to comment.