Shop OBEX P1 Docs P2 Docs Learn Events
I would like this object critiqued please — Parallax Forums

I would like this object critiqued please

turbosupraturbosupra Posts: 1,088
edited 2012-03-02 21:25 in Propeller 1
Hi,

This is code to simulate a hall effect sensor. I think it's harder to scope this, than a regular/consistent frequency, since it has the [X] amount of teeth missing. I get 3.14khz at 10000rpm and 504hz at 1000rpm?

The purpose of the code is to simulate a 36-2 trigger wheel and hall effect sensor, so for each rotation, there are 34 teeth and then 2 missing (assuming it started on the first tooth). Please let me know if there can be improvements, this is the first object I've tried to write, be it very basic. Attached is a working version to be loaded into the propeller.

Thanks for reading.

PUB simulateCrankWheel(simCrankWheelPinNum, crankTeethSimToothCnt, crankTeethSimMissingToothCnt, crankTeethSimRPM) | localIndex, pstSimulateCrankWheel

  ' Initialize variables here 
  dira[simCrankWheelPinNum]~~
  outa[simCrankWheelPinNum]~
  localIndex := 0
  pstSimulateCrankWheel := 0    ' Set this to 0 if you don't want to see the pst troubleshooting code lines, 1 if you do

  {
    This is for a Hall effect signal/sensor simulation
    For 1000rpms on a 36-2 crank trigger wheel, it'd be (1000(36*2)) = 72000, then 72000/60 = 1200 pulses per clkfreq. 80000000(clkfreq)/1200 =
    a pulse every 133333.3 clk cycles that stayed on for 66666.67 cycles and then was off for every 66666.67 cycles  
    So you'd loop on for 66666.67 cycles and then off for 66666.67 cycles and after 34 times, you'd wait for 66666.67 cycles * 4, to simulate the 2
    missing teeth on and accompanying 2 teeth off (or gaps in between each tooth)
    An example to start the method would be simulateCrankWheel(7, 36, 2, 1000)
  }
  
  repeat
    if pstSimulateCrankWheel == 1
      pst.Str(String("start"))
      pst.Dec(simCrankWheelPinNum)
      pst.Str(String(pst#NL))
      pst.Dec(crankTeethSimToothCnt)
      pst.Str(String(pst#NL))
      pst.Dec(crankTeethSimMissingToothCnt)
      pst.Str(String(pst#NL))
      pst.Dec(crankTeethSimRPM)
      pst.Str(String(pst#NL, pst#NL)) 

    repeat localIndex from 1 to (crankTeethSimToothCnt - crankTeethSimMissingToothCnt)    ' loop number of physical teeth on and off  
      outa[simCrankWheelPinNum]~~
      if pstSimulateCrankWheel == 1
        pst.Dec(localIndex)
        pst.Str(string(pst#NL)) 
        pst.Str(string("pin high "))
        pst.Dec(cnt)
        pst.Str(string(pst#NL))
      waitcnt((clkfreq/((crankTeethSimRPM*(crankTeethSimToothCnt*2))/60))+cnt)            ' on for 1 tooth       

      outa[simCrankWheelPinNum]~
      if pstSimulateCrankWheel == 1
        pst.Str(string("pin low "))
        pst.Dec(cnt)
        pst.Str(string(pst#NL))
      waitcnt((clkfreq/((crankTeethSimRPM*(crankTeethSimToothCnt*2))/60))+cnt)            ' off for 1 tooth

    repeat localIndex from 1 to (crankTeethSimMissingToothCnt)                            ' loop number of times for each missing tooth
      outa[simCrankWheelPinNum]~
      if pstSimulateCrankWheel == 1
        pst.Dec(localIndex)
        pst.Str(string(pst#NL))
        pst.Str(string("pin low "))
        pst.Dec(cnt)
        pst.Str(string(pst#NL))
      waitcnt((clkfreq/((crankTeethSimRPM*(crankTeethSimToothCnt*2))/60))+cnt)            ' on for 1 tooth       
      
      outa[simCrankWheelPinNum]~
      if pstSimulateCrankWheel == 1
        pst.Str(string("pin low again "))
        pst.Dec(cnt)
        pst.Str(string(pst#NL))
      waitcnt((clkfreq/((crankTeethSimRPM*(crankTeethSimToothCnt*2))/60))+cnt)            ' off for 1 space in between teeth 

    if pstSimulateCrankWheel == 1
      pst.Str(string(pst#NL)) 
      pst.Str(string("finish"))
      pst.Str(string(pst#NL, pst#NL))
       


Comments

  • CircuitsoftCircuitsoft Posts: 1,166
    edited 2012-03-01 19:29
    What car has 36-2? I know Ford uses 36-1, and VW uses 60-2.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-03-01 20:14
    If you have the object output debug information, it will slow down the program a lot. Once PST's tx buffer fills up, the program will wait until there is room in the buffer before continuing with the program.
    For 1000rpms on a 36-2 crank trigger wheel, it'd be (1000(36*2)) = 72000, then 72000/60 = 1200 pulses per clkfreq. 80000000(clkfreq)/1200 =
        a pulse every 133333.3 clk cycles that stayed on for 66666.67 cycles and then was off for every 66666.67 cycles
    

    Since your using "waitcnt(time + cnt)" type of statements, your program wont take into account the overhead of running the code. In the waitcnt section of the Propeller manual there are examples of using waitcnt to more accurately make regular pauses in a program.

    It will show you how to add an interval to the time to wait so each section of code takes a pradictable amount of time.

    Here's a quick example:
    timeToWait := cnt + interval
      repeat
        'do stuff every interval
        waitcnt(timeToWait)
        timeToWait += interval
    

    The above example will "do stuff" every interval.

    But, If you use:
    repeat
        'do stuff every interval plus some extra time
        waitcnt(interval + cnt)
    

    Then each loop will take a little longer than "interval".
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-03-01 23:14
    Duane is right, but if you change the code to the "constant intervall" method I guess you will have other problems. The output might take more time than available in the timeToWait, which makes you miss the right counter value. Then the waitcnt will wait for ~53 seconds.

    Play the propeller ace card! The propeller has 8 COGs, so it's easy to move all the debug-instructions into it's own COG. It's then like one COG is watching the other COG and reporting to you what it sees without adding any execution-time to the COG that runs the real code.

    The problem is that the other COG will not see the local variables and function parameters by label, if you leave them as local variables/function parameters. So, for the time being you could make them global variables. Later you can change the debug-code to work with long[ address_of_parameters ][ x ].
    As we learned before: Do one step after the other, so first remove the debug-code easiest way, then switch code to use constant interval, then you can change debugging showing local variables.

    Some general comments:
    (clkfreq/((crankTeethSimRPM*(crankTeethSimToothCnt*2))/60))
    this term is used 4 times without changing any of the involved variables somewhere in between. In this case you should create a local variable which stores the result and use that in all waitcnt. This saves HUB-RAM and runtime. Ok ... you might say that runtime is not that important because you'd wait anyways in this case, but during a waitcnt the COG needs less current and saves energy and in other cases it might give you the right boost ;o)
  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-02 06:38
    Toyota :)
    What car has 36-2? I know Ford uses 36-1, and VW uses 60-2.
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-03-02 06:51
    Easy to guess, when looking at your forum-name
    ;o)
  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-02 06:55
    Thanks Duane, that might explain why I saw 3.14khz, instead of a higher frequency. I read the prop manual about time count and I understand what you are saying. I will implement that in the next version.


    Duane Degn wrote: »
    If you have the object output debug information, it will slow down the program a lot. Once PST's tx buffer fills up, the program will wait until there is room in the buffer before continuing with the program.
    For 1000rpms on a 36-2 crank trigger wheel, it'd be (1000(36*2)) = 72000, then 72000/60 = 1200 pulses per clkfreq. 80000000(clkfreq)/1200 =
        a pulse every 133333.3 clk cycles that stayed on for 66666.67 cycles and then was off for every 66666.67 cycles
    

    Since your using "waitcnt(time + cnt)" type of statements, your program wont take into account the overhead of running the code. In the waitcnt section of the Propeller manual there are examples of using waitcnt to more accurately make regular pauses in a program.

    It will show you how to add an interval to the time to wait so each section of code takes a pradictable amount of time.

    Here's a quick example:
    timeToWait := cnt + interval
      repeat
        'do stuff every interval
        waitcnt(timeToWait)
        timeToWait += interval
    

    The above example will "do stuff" every interval.

    But, If you use:
    repeat
        'do stuff every interval plus some extra time
        waitcnt(interval + cnt)
    

    Then each loop will take a little longer than "interval".
  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-02 06:56
    Exactly :), although technically the USDM turbosupra's have a 12-1 from the factory.
    MagIO2 wrote: »
    Easy to guess, when looking at your forum-name
    ;o)
  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-02 06:58
    Thanks for the reply. This all makes sense, and I am changing the code in the order you have suggested. I do have one question, if I put the debugging code into the main cog, it cannot then see my parameters of my simulateCrankWheel method. Should I be assigning them to global variables each loop (inside of simulateCrankWheel), or doing this part a different way to get the debugging working in a different cog?

    MagIO2 wrote: »
    Duane is right, but if you change the code to the "constant intervall" method I guess you will have other problems. The output might take more time than available in the timeToWait, which makes you miss the right counter value. Then the waitcnt will wait for ~53 seconds.

    Play the propeller ace card! The propeller has 8 COGs, so it's easy to move all the debug-instructions into it's own COG. It's then like one COG is watching the other COG and reporting to you what it sees without adding any execution-time to the COG that runs the real code.

    The problem is that the other COG will not see the local variables and function parameters by label, if you leave them as local variables/function parameters. So, for the time being you could make them global variables. Later you can change the debug-code to work with long[ address_of_parameters ][ x ].
    As we learned before: Do one step after the other, so first remove the debug-code easiest way, then switch code to use constant interval, then you can change debugging showing local variables.

    Some general comments:
    (clkfreq/((crankTeethSimRPM*(crankTeethSimToothCnt*2))/60))
    this term is used 4 times without changing any of the involved variables somewhere in between. In this case you should create a local variable which stores the result and use that in all waitcnt. This saves HUB-RAM and runtime. Ok ... you might say that runtime is not that important because you'd wait anyways in this case, but during a waitcnt the COG needs less current and saves energy and in other cases it might give you the right boost ;o)
  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-02 07:12
    One more question, does the debugging slow the original code down down when the if statement is false?
  • Jim FouchJim Fouch Posts: 395
    edited 2012-03-02 07:27
    turbosupra wrote: »
    One more question, does the debugging slow the original code down down when the if statement is false?

    It will take processr cycles to process the IF even if it's false. The true state will take even more time based on when the True condition executes.
  • Jim FouchJim Fouch Posts: 395
    edited 2012-03-02 07:32
    turbosupra,

    I have a few objects i recently wrote that allow you to simulate a signal from the Crank Shaft and Cam Shaft of a car. Both are in PASM and are pretty fast. You simply layout your signal in data statements and the objects flip your outputs to match the rotation of the shaft.

    You could easily write a small program to create the teeth pattern and even inject missing teeth of you wanted.

    It can easily change between different patterns on the fly if need be.

    Let me know if it's something that you may want to look at.

    Jim Fouch
  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-02 07:45
    Hi Jim,

    I'd love to see your code, although I cannot read PASM, so I don't know how well I'd be able to merge my ambitions with it.
    Jim Fouch wrote: »
    turbosupra,

    I have a few objects i recently wrote that allow you to simulate a signal from the Crank Shaft and Cam Shaft of a car. Both are in PASM and are pretty fast. You simply layout your signal in data statements and the objects flip your outputs to match the rotation of the shaft.

    You could easily write a small program to create the teeth pattern and even inject missing teeth of you wanted.

    It can easily change between different patterns on the fly if need be.

    Let me know if it's something that you may want to look at.

    Jim Fouch
  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-02 08:15
    I see how much it slows it down, it hosed the entire loop at anything above 100rpm, when I used a set internal/interval + delay type of code.

    When I turned it off I was able to go to 10000rpm without issue and the frequency showed 5.98kHz on the propscope, which fits my math much better than 3.14kHz :)
    10000rpm/60sec per min = 166.67 166.67 * 36 pulses per rotation = 6000hz, sometimes it will fluctuate to 5.5x kHz because of the missing two teeth, but I believe it is working well now. Now on to moving the variables to another cog.


    Duane Degn wrote: »
    If you have the object output debug information, it will slow down the program a lot. Once PST's tx buffer fills up, the program will wait until there is room in the buffer before continuing with the program.
  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-02 08:44
    Ok, this code works, please critique :) and thanks to everyone who has chimed in so far


     PUB simulateCrankWheel(simCrankWheelPinNum, crankTeethSimToothCnt, crankTeethSimMissingToothCnt, crankTeethSimRPM) | localIndex, waitTime, startCnt 
       
       ' Initialize variables here 
      dira[simCrankWheelPinNum]~~
      outa[simCrankWheelPinNum]~
      localIndex := 0
      waitTime := (clkfreq/((crankTeethSimRPM*(crankTeethSimToothCnt*2))/60))
      startCnt := cnt 
      
      l_simCrankWheelPinNum := simCrankWheelPinNum          ' Set local parameter to global variable for troubleshooting
      l_crankTeethSimToothCnt := crankTeethSimToothCnt      ' Set local parameter to global variable for troubleshooting
      l_crankTeethSimMissingToothCnt := crankTeethSimMissingToothCnt                ' Set local parameter to global variable for troubleshooting
      l_crankTeethSimRPM := crankTeethSimRPM                ' Set local parameter to global variable for troubleshooting
    
      
      repeat
         
        repeat localIndex from 1 to (crankTeethSimToothCnt - crankTeethSimMissingToothCnt)    ' loop number of physical teeth on and off  
          outa[simCrankWheelPinNum]~~
          l_crankTeethSimOn := startCnt
          waitcnt(startCnt += waitTime)                                                        ' on for 1 tooth
          outa[simCrankWheelPinNum]~
          l_crankTeethSimOff := startCnt
          waitcnt(startCnt += waitTime)                                                        ' off for 1 tooth
     
        repeat localIndex from 1 to (crankTeethSimMissingToothCnt)                             ' loop number of times for each missing tooth
          outa[simCrankWheelPinNum]~
          l_crankTeethSimOff := startCnt
          waitcnt(startCnt += waitTime)                                                        ' on for 1 tooth
          outa[simCrankWheelPinNum]~
          l_crankTeethSimOff := startCnt
          waitcnt(startCnt += waitTime)                                                        ' off for 1 space in between teeth
                    
                    
    
    



    Here is the entire code, as the work firewall blocks the attachment feature of the parallax forum
    
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
      _clkHz = 80_000_000
      _byteLimit = 100
    
    VAR
    
      byte b_delimiter
    
      long pstSimulateCrankWheel
      long l_simCrankWheelPinNum
      long l_crankTeethSimToothCnt
      long l_crankTeethSimMissingToothCnt
      long l_crankTeethSimRPM 
      long l_simulateCrankWheelPin
      long l_crankTeethSimOn
      long l_crankTeethSimOff
       
      long simulateCrankWheelstack[50]
    
    OBJ
      pst : "Parallax Serial Terminal"
    
    
    PUB Main | localIndex, pstMain
    
      ' Initialize variables here 
      localIndex := 0
      pstMain := 0
      b_delimiter := "="
      pstSimulateCrankWheel := 1    ' Set this to 0 if you don't want to see the pst troubleshooting code lines, 1 if you do
    
      pst.Start(115_200)
      waitcnt((clkfreq)+cnt)
      pst.Clear
      cognew(simulateCrankWheel(7, 36, 2, 1000), @simulateCrankWheelstack)          ' PUB simulateCrankWheel(simCrankWheelPinNum, crankTeethSimToothCnt, crankTeethSimMissingToothCnt, crankTeethSimRPM)
    
      repeat
    
        if pstSimulateCrankWheel == 1
          pst.Dec(l_crankTeethSimOn)
          pst.Str(String(pst#NL))
          pst.Dec(l_crankTeethSimOff)
          pst.Str(String(pst#NL, pst#NL))
         
            
        waitcnt((clkfreq/25) + cnt)
        
    
    
    PUB simulateCrankWheel(simCrankWheelPinNum, crankTeethSimToothCnt, crankTeethSimMissingToothCnt, crankTeethSimRPM) | localIndex, waitTime, startCnt 
       
       ' Initialize variables here 
      dira[simCrankWheelPinNum]~~
      outa[simCrankWheelPinNum]~
      localIndex := 0
      waitTime := (clkfreq/((crankTeethSimRPM*(crankTeethSimToothCnt*2))/60))
      startCnt := cnt 
      
      l_simCrankWheelPinNum := simCrankWheelPinNum          ' Set local parameter to global variable for troubleshooting
      l_crankTeethSimToothCnt := crankTeethSimToothCnt      ' Set local parameter to global variable for troubleshooting
      l_crankTeethSimMissingToothCnt := crankTeethSimMissingToothCnt                ' Set local parameter to global variable for troubleshooting
      l_crankTeethSimRPM := crankTeethSimRPM                ' Set local parameter to global variable for troubleshooting
    
      
      repeat
         
        repeat localIndex from 1 to (crankTeethSimToothCnt - crankTeethSimMissingToothCnt)    ' loop number of physical teeth on and off  
          outa[simCrankWheelPinNum]~~
          l_crankTeethSimOn := startCnt
          waitcnt(startCnt += waitTime)                                                        ' on for 1 tooth
          outa[simCrankWheelPinNum]~
          l_crankTeethSimOff := startCnt
          waitcnt(startCnt += waitTime)                                                        ' off for 1 tooth
     
        repeat localIndex from 1 to (crankTeethSimMissingToothCnt)                             ' loop number of times for each missing tooth
          outa[simCrankWheelPinNum]~
          l_crankTeethSimOff := startCnt
          waitcnt(startCnt += waitTime)                                                        ' on for 1 tooth
          outa[simCrankWheelPinNum]~
          l_crankTeethSimOff := startCnt
          waitcnt(startCnt += waitTime)                                                        ' off for 1 space in between teeth
                    
    
              
           
    
    
  • Jim FouchJim Fouch Posts: 395
    edited 2012-03-02 08:47
    turbosupra wrote: »
    Hi Jim,

    I'd love to see your code, although I cannot read PASM, so I don't know how well I'd be able to merge my ambitions with it.

    Here is an example of the code I use.

    To define the pattern you simply enter the location of chages in you pattern like this...
    DAT
    
    Pat4X1
            ' This pattern was entered based on the drawing provided by Mike Noonan
            ' instead of usign 360 degrees of rotation, I simply measured the pulses
            ' with a digital caliper and used those measurments to determine where
            ' the transistions would occur
            ' The total width is 123mm
            
            Word 0000_0<<2 + %11
            Word 0005_0<<2 + %10
            Word 0015_5<<2 + %11
            Word 0020_9<<2 + %10
            Word 0031_1<<2 + %11
            Word 0036_6<<2 + %10
            Word 0047_0<<2 + %11
            Word 0052_6<<2 + %10
            Word 0062_1<<2 + %01
            Word 0067_9<<2 + %00
            Word 0077_8<<2 + %01
            Word 0083_1<<2 + %00
            Word 0093_1<<2 + %01
            Word 0098_1<<2 + %00
            Word 0108_1<<2 + %01
            Word 0113_3<<2 + %00
            Word 99999<<2 + %00 ' End Seq
    
    

    This is what the pattern looked on a scope...

    4xCrank_1xCam.jpg


    The two objects I wrote are here...

    Spinner.spin
    Rotator.spin

    One thing to note is that the rotator has code to write to specific pins and has LEDs to indicate the output. The actual output pins for the signal are INVERTED because I was driving 2N3904's to drive the outputs. You will need to change the pins and maybe the states to match your project.

    Defined in you main spin program...
    Rotator: "Rotator"
      Spinner: "Spinner"
    


    The two cogs are controlled by the values in these VARs that are in your main spin object...
    VAR
      
      LONG Pattern,CurrentAngle, AdvanceRate, IncrementCount, MaxCount ' Used to Control Rotator & Spinner
    
    
    
    

    I created a sub to set these for my pattern...
    Pri SetPat24
      Pattern := @Pat24
      CurrentAngle:=0
      AdvanceRate:=1175 ' Minimum 150,   1,175 = 105.7ms / Rev of CamShaft 
      IncrementCount:=1
      MaxCount:=7199 ' 719.9 degrees
    

    Once you have set the vars then you can start the two objects...
    RotatorCog:=Rotator.Start(@Pattern)
      SpinnerCog:=Spinner.Start(@CurrentAngle)
    

    Once the objects are started, simply changing the VARs show above will allow you to change the pattern. Changing the AdvanceRate and or the IncrementCount will change the RPM. There are limits for how low you can set these values. For example if you set them too low you will actually get too much of a pause between steps. This is just like the WAITCNT method. If you go below 381 you will have to wait for the CNT to roll over.

    The real value in these objects is they are very flexable and could be used to make almost any repeating signal.

    You could also write a VB.Net or C# app to help create the patterns. Not sure I'd want to hand enter some 100 tooth signal.
  • Jim FouchJim Fouch Posts: 395
    edited 2012-03-02 09:16
    Sorry...I listed the wrong SetPat sub.

    For this example it shoud have been....
    Pri SetPat4X1
      Pattern := @Pat4X1
      CurrentAngle:=0
      AdvanceRate:=12000   ' 12,000 = 184.6 ms / rev of CamShaft
      IncrementCount:=1
      MaxCount:=1230 ' 123.0 mm
    

    The example shown on the previous post was for this pattern....
    Dat
    
    Pat24
            ' This pattern is based on a crank case rotation of 360 degrees and
            ' a Camshaft rotaion of 720 degrees
            Word 000_1<<2 + %10    ' CMP High CKP Low
            Word 012_0<<2 + %11
            Word 015_0<<2 + %10
            Word 018_0<<2 + %11
            Word 030_0<<2 + %10
            Word 033_0<<2 + %11
            Word 045_0<<2 + %10
            Word 048_0<<2 + %11
            Word 060_0<<2 + %10
            Word 063_0<<2 + %11
            Word 075_0<<2 + %10
            Word 078_0<<2 + %11
            Word 090_0<<2 + %10
            Word 102_0<<2 + %11
            Word 105_0<<2 + %10
            Word 108_0<<2 + %11
            Word 120_0<<2 + %10
            Word 123_0<<2 + %11
            Word 135_0<<2 + %10
            Word 138_0<<2 + %11
            Word 150_0<<2 + %10
            Word 162_0<<2 + %11
            
            Word 165_0<<2 + %10
            Word 177_0<<2 + %11
            Word 180_0<<2 + %10
            Word 183_0<<2 + %11
            Word 195_0<<2 + %10
            Word 198_0<<2 + %11
            Word 210_0<<2 + %10
            Word 222_0<<2 + %11
            Word 225_0<<2 + %10
            Word 237_0<<2 + %11
            Word 240_0<<2 + %10
            Word 252_0<<2 + %11
            Word 255_0<<2 + %10
            Word 258_0<<2 + %11
            Word 270_0<<2 + %10
            Word 282_0<<2 + %11
            Word 285_0<<2 + %10
            Word 288_0<<2 + %11
            Word 300_0<<2 + %10
            Word 312_0<<2 + %11
            Word 315_0<<2 + %10
            Word 327_0<<2 + %11
            Word 330_0<<2 + %10
            Word 342_0<<2 + %11
            Word 357_0<<2 + %10
            
            Word (360_0+000_0)<<2 + %00    ' Second Rotation with CMP Low
            Word (360_0+012_0)<<2 + %01
            Word (360_0+015_0)<<2 + %00
            Word (360_0+018_0)<<2 + %01
            Word (360_0+030_0)<<2 + %00
            Word (360_0+033_0)<<2 + %01
            Word (360_0+045_0)<<2 + %00
            Word (360_0+048_0)<<2 + %01
            Word (360_0+060_0)<<2 + %00
            Word (360_0+063_0)<<2 + %01
            Word (360_0+075_0)<<2 + %00
            Word (360_0+078_0)<<2 + %01
            Word (360_0+090_0)<<2 + %00
            Word (360_0+102_0)<<2 + %01
            Word (360_0+105_0)<<2 + %00
            Word (360_0+108_0)<<2 + %01
            Word (360_0+120_0)<<2 + %00
            Word (360_0+123_0)<<2 + %01
            Word (360_0+135_0)<<2 + %00
            Word (360_0+138_0)<<2 + %01
            Word (360_0+150_0)<<2 + %00
            Word (360_0+162_0)<<2 + %01
            Word (360_0+165_0)<<2 + %00
            Word (360_0+177_0)<<2 + %01
            Word (360_0+180_0)<<2 + %00
            Word (360_0+183_0)<<2 + %01
            Word (360_0+195_0)<<2 + %00
            Word (360_0+198_0)<<2 + %01
            Word (360_0+210_0)<<2 + %00
            Word (360_0+222_0)<<2 + %01
            Word (360_0+225_0)<<2 + %00
            Word (360_0+237_0)<<2 + %01
            Word (360_0+240_0)<<2 + %00
            Word (360_0+252_0)<<2 + %01
            Word (360_0+255_0)<<2 + %00
            Word (360_0+258_0)<<2 + %01
            Word (360_0+270_0)<<2 + %00
            Word (360_0+282_0)<<2 + %01
            Word (360_0+285_0)<<2 + %00
            Word (360_0+288_0)<<2 + %01
            Word (360_0+300_0)<<2 + %00
            Word (360_0+312_0)<<2 + %01
            Word (360_0+315_0)<<2 + %00
            Word (360_0+327_0)<<2 + %01
            Word (360_0+330_0)<<2 + %00                                           
            Word (360_0+342_0)<<2 + %01
            Word (360_0+357_0)<<2 + %00
            Word 99999<<2 + %00 ' End Seq 
    

    What this shows is how flexable the objects are. Two totally different patterns defined in different ways. One with degrees of rotation and the other simpy by measuring the pattern on paper.

    This solution could easily be modified to have more than two outputs if we moved to a LONG for the pattern and shifted the location portion further left and made room for the outputs. I could see making an eight output one. :smile:
  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-02 09:33
    Jim,

    I love the degrees of rotation, that is brilliant. It is going to take me some time to understand the ins and outs of the code, but I like that. I also have to measure cam position and my cam sensor has 3 triggers on it, I believe they are something like 105/105/150 degrees apart or something offset like that. I have to write code for that next.
  • Jim FouchJim Fouch Posts: 395
    edited 2012-03-02 09:59
    turbosupra wrote: »
    Jim,

    I love the degrees of rotation, that is brilliant. It is going to take me some time to understand the ins and outs of the code, but I like that. I also have to measure cam position and my cam sensor has 3 triggers on it, I believe they are something like 105/105/150 degrees apart or something offset like that. I have to write code for that next.

    I desiged it to work with 360 Degrees of rotation in mind but then later realized I had to account for two rotations of the cam. So, I just modified the code to rotate on any bondry. This way it could account for ANY number of rotation or really any signal that repeats.

    The hardest part is entering the changes in outputs. One downside is ALL outputs are locked and if you want to shift one output you have to recalculate the entire data set. This is where a helper app would be greate to define the data statements.
  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-02 11:06
    Tell me what you think of this Jim. I modified my other object to get this and it appears to work well on the scope

    PUB simulateCamWheel(simCamWheelPinNum, camTeethSimToothCnt, camTeethDegreesInBetween, camTeethSimRPM, crankRotsToCamRots) | localIndex, onTime, offTime, offTime2, startCnt
    
       ' Initialize variables here 
      dira[simCamWheelPinNum]~~
      outa[simCamWheelPinNum]~
      localIndex := 0
      onTime := (clkfreq/(((camTeethSimRPM/crankRotsToCamRots)*360)/60))     ' 26666.6 cycles per degree of crank rotation (or 1/2 cam rotation) at 1000rpm
      offTime := ((clkfreq/(((camTeethSimRPM/crankRotsToCamRots)*360)/60))*(camTeethDegreesInBetween))     ' 28000000 at 1000 crank rpm (or 500 cam rpm) and 120 degrees
      offTime2 := ((clkfreq/(((camTeethSimRPM/crankRotsToCamRots)*360)/60))*((camTeethDegreesInBetween * 142)/100))          '28000000 at 1000 crank rpm (or 500 cam rpm) and 150 degrees
      startCnt := cnt
    
      {
        This is for a Hall effect signal/sensor simulation
        For 1000rpms on a cam trigger wheel, it'd be (1000(360)) = 360000, then 360000/60 = 6000 degrees of rotation per second. 80000000(clkfreq)/6000 =
        a degree of rotation every 13333.3 clk cycles.
        An example to start the method would be simulateCamWheel(18, 3, 150, 1000, 2)
      }
    
    
      repeat
    
        outa[simCamWheelPinNum]~~
        l_camTeethSimOn := startCnt
        waitcnt(startCnt += onTime)                                                        ' on for 1 degree
         
        outa[simCamWheelPinNum]~
        l_camTeethSimOff := startCnt
        waitcnt(startCnt += offTime)                                                       ' off for camTeethDegreesInBetween degrees
         
        outa[simCamWheelPinNum]~~
        l_camTeethSimOn := startCnt
        waitcnt(startCnt += onTime)                                                        ' on for 1 degree
         
        outa[simCamWheelPinNum]~
        l_camTeethSimOff := startCnt
        waitcnt(startCnt += offTime)                                                       ' off for camTeethDegreesInBetween degrees
         
        outa[simCamWheelPinNum]~~
        l_camTeethSimOn := startCnt
        waitcnt(startCnt += onTime)                                                        ' on for 1 degree
         
        outa[simCamWheelPinNum]~
        l_camTeethSimOff := startCnt
        waitcnt(startCnt += offTime2)                                                      ' of for 1.x camTeethDegreesInBetween degrees
    
    
    

    camwheelsimulator1.png
    Entire code since I can't upload .spin attachments
    PUB Main | localIndex, pstMain
    
      ' Initialize variables here 
      localIndex := 0
      pstMain := 0
      pstSimulateCamWheel := 0    ' Set this to 0 if you don't want to see the pst troubleshooting code lines, 1 if you do
    
      pst.Start(115_200)
      waitcnt((clkfreq)+cnt)
      pst.Clear
      cognew(simulateCamWheel(7, 3, 105, 100, 2), @simulateCamWheelstack)             ' simulateCamWheel(simCamWheelPinNum, camTeethSimToothCnt, camTeethDegreesInBetween, camTeethSimRPM)
    
      repeat
    
        if pstSimulateCrankWheel == 1
          pst.Dec(l_crankTeethSimOn)
          pst.Str(String(pst#NL))
          pst.Dec(l_crankTeethSimOff)
          pst.Str(String(pst#NL, pst#NL))
    
        {if pstSimulateCamWheel == 1
          pst.Dec(l_crankTeethSimOn)
          pst.Str(String(pst#NL))
          pst.Dec(l_crankTeethSimOff)
          pst.Str(String(pst#NL, pst#NL)) }
            
        waitcnt((clkfreq/25) + cnt)
        
    
    
    
    PUB simulateCamWheel(simCamWheelPinNum, camTeethSimToothCnt, camTeethDegreesInBetween, camTeethSimRPM, crankRotsToCamRots) | localIndex, onTime, offTime, offTime2, startCnt
    
       ' Initialize variables here 
      dira[simCamWheelPinNum]~~
      outa[simCamWheelPinNum]~
      localIndex := 0
      onTime := (clkfreq/(((camTeethSimRPM/crankRotsToCamRots)*360)/60))     ' 26666.6 cycles per degree of crank rotation (or 1/2 cam rotation) at 1000rpm
      offTime := ((clkfreq/(((camTeethSimRPM/crankRotsToCamRots)*360)/60))*(camTeethDegreesInBetween))     ' 28000000 at 1000 crank rpm (or 500 cam rpm) and 120 degrees
      offTime2 := ((clkfreq/(((camTeethSimRPM/crankRotsToCamRots)*360)/60))*((camTeethDegreesInBetween * 142)/100))          '28000000 at 1000 crank rpm (or 500 cam rpm) and 150 degrees
      startCnt := cnt
    
      {
        This is for a Hall effect signal/sensor simulation
        For 1000rpms on a cam trigger wheel, it'd be (1000(360)) = 360000, then 360000/60 = 6000 degrees of rotation per second. 80000000(clkfreq)/6000 =
        a degree of rotation every 13333.3 clk cycles.
        An example to start the method would be simulateCamWheel(18, 3, 150, 1000, 2)
      }
    
    
      repeat
    
        outa[simCamWheelPinNum]~~
        l_camTeethSimOn := startCnt
        waitcnt(startCnt += onTime)                                                        ' on for 1 degree
         
        outa[simCamWheelPinNum]~
        l_camTeethSimOff := startCnt
        waitcnt(startCnt += offTime)                                                       ' off for camTeethDegreesInBetween degrees
         
        outa[simCamWheelPinNum]~~
        l_camTeethSimOn := startCnt
        waitcnt(startCnt += onTime)                                                        ' on for 1 degree
         
        outa[simCamWheelPinNum]~
        l_camTeethSimOff := startCnt
        waitcnt(startCnt += offTime)                                                       ' off for camTeethDegreesInBetween degrees
         
        outa[simCamWheelPinNum]~~
        l_camTeethSimOn := startCnt
        waitcnt(startCnt += onTime)                                                        ' on for 1 degree
         
        outa[simCamWheelPinNum]~
        l_camTeethSimOff := startCnt
        waitcnt(startCnt += offTime2)                                                      ' of for 1.x camTeethDegreesInBetween degrees
    
    


    Jim Fouch wrote: »
    I desiged it to work with 360 Degrees of rotation in mind but then later realized I had to account for two rotations of the cam. So, I just modified the code to rotate on any bondry. This way it could account for ANY number of rotation or really any signal that repeats.

    The hardest part is entering the changes in outputs. One downside is ALL outputs are locked and if you want to shift one output you have to recalculate the entire data set. This is where a helper app would be greate to define the data statements.
  • Jim FouchJim Fouch Posts: 395
    edited 2012-03-02 11:25
    turbosupra wrote: »
    Tell me what you think of this Jim. I modified my other object to get this and it appears to work well on the scope

    Looks pretty good. Not sure how fast your scope is or how fast you need to simulate the rotation.

    I know in my project, when I had it all in SPIN it would skip updates once in a while even when simulating a ~700 RPM signal. Once I split the Rotator out into PASM the signal became rock solid. I'm looking at the output with a logic analyzer that runs at 500Ms/s.

    Basically as I would siulate the rotation of the shaft, the other object could not keep pace 100% of the time in SPIN. Kind of funny the we're using a language called SPIN to simulate a spinning shaft...lol PASM is up to 200x faster than SPIN in some cases. I was able to simulate the larger pattern at speeds way above what a real motor could ever run at. One thing to increase the simulated RPM is to increase the IncrementCount. In the examples I showed above we advanced 1/10 of a unit with a resolution of 0.1 Degrees. By doubling this to a value of 2 would double the rate or rotation but cut in half the resolution as 0.2 Degrees. So setting it to 10 would spin 10x as fast but only have the resolution of 1.0 Degrees.

    I also have an Arbitrary Waveform Generator ( http://www.tequipment.net/RigolDG1022.html ) that would do the same thing. But to do it with a $20 protoboard is so much more fun.
  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-02 13:15
    I see what you are talking about now with the higher speeds. If the cam is only on for 1 degree out of 360, it is harder to "catch" then the trigger wheel which has a total of "72 degree" markings in a circle.

    The crank works great at 10000 rpm, but the cam is not showing up well on my oscope, and I cannot drag the oscope display screen any larger either.
  • Jim FouchJim Fouch Posts: 395
    edited 2012-03-02 13:30
    turbosupra wrote: »
    I see what you are talking about now with the higher speeds. If the cam is only on for 1 degree out of 360, it is harder to "catch" then the trigger wheel which has a total of "72 degree" markings in a circle.

    The crank works great at 10000 rpm, but the cam is not showing up well on my oscope, and I cannot drag the oscope display screen any larger either.

    This is where a Logic Anaylizer has an advantage. Most can compress the signal and the display it back expanded in realtime. Where as a scope does not compress and has a much more limited view window.

    Here is an exampe scree shot from my Logic Anaylizer (http://www.pctestinstruments.com/ ) It's not a cheap date, but boy does it make troubleshooting a lot easier.
    LA.jpg
    1024 x 116 - 19K
  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-02 13:57
    Yeah, $400 bucks isn't very cheap. Ugh...

    At 10000rpm, I'm at 2677 clk cycles for a single degree of cam rotation or 30000 degrees rotated per second, I guess this is too fast for spin :(

    I don't want to take on assembly, but I guess I have no choice and will try and use your object, thank you for that.
  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-02 14:28
    Ok,

    I think I have something that might be working in spin, would you mind testing it for me with the test environment you have setup already? Sorry I can't attach the .spin files because of the work firewall. It's probably too good to be true, but I'm a little optimistic it might actually be a viable solution.

    I tested this at 15000 engine rpm with my scope and I'm seeing the groups of 3 registering on the scope with a slightly longer delay/space in between each group of 3. I will clean up the parameters for the method later after I verify for sure if this is working or not.

    What do you think? :)
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
      _clkHz = 80_000_000
      _byteLimit = 100
    
    VAR
    
      byte b_delimiter
    
      long pstSimulateCrankWheel
      long l_simCrankWheelPinNum
      long l_crankTeethSimToothCnt
      long l_crankTeethSimMissingToothCnt
      long l_crankTeethSimRPM 
      long l_crankTeethSimOn
      long l_crankTeethSimOff
      long pstSimulateCamWheel
      long l_camTeethSimOn
      long l_camTeethSimOff
       
      long simulateCrankWheelstack[50]
      long simulateCamWheelstack[50]
      long simCam2Stack[50]
    
    OBJ
      pst : "Parallax Serial Terminal"
    
    
    PUB Main | localIndex, pstMain
    
      ' Initialize variables here 
      localIndex := 0
      pstMain := 0
      b_delimiter := "="
      pstSimulateCrankWheel := 0    ' Set this to 0 if you don't want to see the pst troubleshooting code lines, 1 if you do
      pstSimulateCamWheel := 0    ' Set this to 0 if you don't want to see the pst troubleshooting code lines, 1 if you do
    
      pst.Start(115_200)
      waitcnt((clkfreq)+cnt)
      pst.Clear
    
      cognew(simCam2(7, 15000, 2, 1, 106, 216, 5), @simCam2Stack)                                ' simCam2(simCamWheelPinNum, engineRPM, degreeOfTrigger1, degreeOfTrigger2, degreeOfTrigger3) | localIndex 
    
      repeat
    
        if pstSimulateCrankWheel == 1
          pst.Dec(l_crankTeethSimOn)
          pst.Str(String(pst#NL))
          pst.Dec(l_crankTeethSimOff)
          pst.Str(String(pst#NL, pst#NL))
    
        if pstSimulateCamWheel == 1
          pst.Dec(l_camTeethSimOn)
          pst.Str(String(pst#NL))
          pst.Dec(l_camTeethSimOff)
          pst.Str(String(pst#NL, pst#NL)) 
            
        'waitcnt((clkfreq/25) + cnt)
        
    
    
    PUB simCam2(simCamWheelPinNum, engineRPM, crankRotsToCamRots, degreeOfTrigger1, degreeOfTrigger2, degreeOfTrigger3, camTriggerDegreeWidth) | localIndex1, crankRPMPerSecond, camRPMPerSecond, localCnt, trigger1Wait, trigger2Wait, trigger3Wait, emtpySpace1Wait, emtpySpace2Wait, emtpySpace3Wait
    
      
      
      dira[simCamWheelPinNum]~~
      outa[simCamWheelPinNum]~
      localIndex1 := 0
      crankRPMPerSecond := (engineRPM/60)
      camRPMPerSecond := ((engineRPM/60)/2)
    
      trigger1Wait := (((clkfreq/360)*camTriggerDegreeWidth)/(camRPMPerSecond))  ' 222 at 10000rpm / 2222 at 1000rpm
      trigger2Wait := (((clkfreq/360)*camTriggerDegreeWidth)/(camRPMPerSecond))
      trigger3Wait := (((clkfreq/360)*camTriggerDegreeWidth)/(camRPMPerSecond))
      emtpySpace1Wait := (((clkfreq/360)*105)/(camRPMPerSecond))                 ' 4666 at 10000rpm / 46666 at 1000rpm
      emtpySpace2Wait := (((clkfreq/360)*105)/(camRPMPerSecond))                 ' 4666 at 10000rpm / 46666 at 1000rpm
      emtpySpace3Wait := (((clkfreq/360)*145)/(camRPMPerSecond))                 ' 6444 at 10000rpm / 64444 at 1000rpm
        
      pst.Dec(simCamWheelPinNum)
      pst.Str(string(pst#NL))
      pst.Dec(engineRPM)
      pst.Str(string(pst#NL))
      pst.Dec(crankRotsToCamRots)
      pst.Str(string(pst#NL))
      pst.Dec(degreeOfTrigger1)
      pst.Str(string(pst#NL))
      pst.Dec(degreeOfTrigger2)
      pst.Str(string(pst#NL))
      pst.Dec(degreeOfTrigger3)
      pst.Str(string(pst#NL))
      pst.Dec(camTriggerDegreeWidth)
      pst.Str(string(pst#NL))
      pst.Dec(crankRPMPerSecond)
      pst.Str(string(pst#NL))
      pst.Dec(camRPMPerSecond)
      pst.Str(string(pst#NL))
      pst.Dec(trigger1Wait)
      pst.Str(string(pst#NL))
      pst.Dec(emtpySpace1Wait)
      pst.Str(string(pst#NL))
      pst.Dec(emtpySpace2Wait)
      pst.Str(string(pst#NL))
      pst.Dec(emtpySpace3Wait)
      pst.Str(string(pst#NL))
    
      localCnt := cnt                                      
    
    
      repeat
    
        outa[simCamWheelPinNum]~~
        waitcnt(localCnt += trigger1Wait)
        outa[simCamWheelPinNum]~
        waitcnt(localCnt += emtpySpace1Wait)
        outa[simCamWheelPinNum]~~
        waitcnt(localCnt += trigger2Wait)
        outa[simCamWheelPinNum]~
        waitcnt(localCnt += emtpySpace2Wait)
        outa[simCamWheelPinNum]~~
        waitcnt(localCnt += trigger3Wait)
        outa[simCamWheelPinNum]~
        waitcnt(localCnt += emtpySpace3Wait)
    
    


    camwheelsimulator2.png
  • Cluso99Cluso99 Posts: 18,069
    edited 2012-03-02 16:07
    turbosupra: You can use a prop to be a logic analyser. If you have spare pins and cogs, you could do it within the same prop - perhaps you dont even need the pins because you are probably sensing them anyway.
    BTW pasm is not that difficult compared to most other assemblers. Its a nice and regular instruction set.
  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-02 21:16
    Hi,

    I actually wrote the simulator code, so I could feed it back into the prop and write code to read it, rather than sit out in the car for hours testing it and reading the actual sensors themselves.

    Now that I *think* I have the simulators written, I was going to try and write the code to read the simulators/sensors and see how that goes?

    Maybe I'll give PASM a try, I just don't even know where to start with it. Spin was/is such a chore, it really makes me scared to even try PASM.

    Cluso99 wrote: »
    turbosupra: You can use a prop to be a logic analyser. If you have spare pins and cogs, you could do it within the same prop - perhaps you dont even need the pins because you are probably sensing them anyway.
    BTW pasm is not that difficult compared to most other assemblers. Its a nice and regular instruction set.
  • pedwardpedward Posts: 1,642
    edited 2012-03-02 21:25
    Just FYI, Craigslist can be a good resource for logic analyzers, etc. I picked up an HP 1631D 50Mhz Logic analyzer/scope for $100. I also have an HP 1651B 100MHz logic analyzer I picked up for $20 when a local business closed their proto shop. My point is that you can get good used test equipment for cheap if you look around.
Sign In or Register to comment.