Shop OBEX P1 Docs P2 Docs Learn Events
Homebrew EFI Project — Parallax Forums

Homebrew EFI Project

Suppose I have a spinning plastic wheel(camshaft), with a very narrow steel target on its circumference. The target is strategically placed at 30 degrees before top dead center of the cam. There is a Hall effect sensor, sensing the passing of the target. The TTL nature of the Hall sensor is being buffered to a propeller friendly voltage level and connected to pin 15. The goal, time a revolution of the cam, defined as the time between any 2 target passings. Calculate the delay necessary for the cam at its present speed to move 30 degrees. Make pin 16 high for a certain duration(fire ignition coil), starting after this delay is over. After my first 100 or so attempts I have had limited success. The code that I'm attaching seems to waist time cog launching or somewhere else, causing the 2 methods to loose sync. I have 2 led's, one led tied to the hall sensor output, and one to pin 16. I have had a 2 channel oscilloscope monitoring the relationship between the two signals. When I say loose sync, I mean that there are certain cam speeds where I get output from pin 16, which is highly delayed, but no output at most speeds. For prototyping purposes I am using an electric motor to turn the cam. A representative cam speed is a cam period of .115 seconds which translates to about 519 RPM. Any ideas of what I'm doing wrong, or a better way of doing this? Look at my comments to see what I'm trying to do. It seems straight forward but its not.
«13

Comments

  • Glad to see someone giving this another go! I made an attempt this for a school project but never followed through. I'm not much of a Spin programmer, but I'll be watching your thread and rooting for you!
  • Beavis3215 wrote: »
    Suppose I have a spinning plastic wheel(camshaft), with a very narrow steel target on its circumference. The target is strategically placed at 30 degrees before top dead center of the cam. There is a Hall effect sensor, sensing the passing of the target. The TTL nature of the Hall sensor is being buffered to a propeller friendly voltage level and connected to pin 15. The goal, time a revolution of the cam, defined as the time between any 2 target passings. Calculate the delay necessary for the cam at its present speed to move 30 degrees. Make pin 16 high for a certain duration(fire ignition coil), starting after this delay is over. After my first 100 or so attempts I have had limited success. The code that I'm attaching seems to waist time cog launching or somewhere else, causing the 2 methods to loose sync. I have 2 led's, one led tied to the hall sensor output, and one to pin 16. I have had a 2 channel oscilloscope monitoring the relationship between the two signals. When I say loose sync, I mean that there are certain cam speeds where I get output from pin 16, which is highly delayed, but no output at most speeds. For prototyping purposes I am using an electric motor to turn the cam. A representative cam speed is a cam period of .115 seconds which translates to about 519 RPM. Any ideas of what I'm doing wrong, or a better way of doing this? Look at my comments to see what I'm trying to do. It seems straight forward but its not.

    Rather than launch the cog when needed, launch at the start of your program once. Then communicate via a shared memory location in hum memory, setting the flag when the other cog needs to fire the coil. This way you will have reduced lag between detection and and firing as you will not have to execute a cognew. It does take a certain time to load a cog.
  • I will try that. I assume that this means that the firing code in cog 1 should be a loop waiting for a flag, instead of stopping at he end.
  • See if this is what you were thinking. Thanks Eric
  • Beavis3215 wrote: »
    I will try that. I assume that this means that the firing code in cog 1 should be a loop waiting for a flag, instead of stopping at he end.

    That is entirely correct. I think you know how to program in spin (I don't much, but I can work out what it's doing), so I'll give you psuedo code for to program.

    In you master cog where it currently launches a new cog, change that to setting the flag, just once, as in launching the cog just one.
    fire_now = 1
    
    In you second cog, which you are now launching at program start, you need to detect the flag and act on it. Also resetting it so you don't continually fire.
    set up pin direction and initial state
    repeat forvever
      repeat 
        while fire_now == 0  do_nothing except check if fire_now is still 0
      end of repeat whilst waiting for fire_now to be set
        ' fire_now must be a non-zero value at this point
        fire_now = 0   ' reset the flag from the master cog
        ' do your existing actions of pin control and waits
    end of repeat forever
    

    That should be a big improvement, as you are now ready an waiting to act on your master cog.

    However, spin is an interpreted language and as such it takes a while to process its' own byte code commands which have to be read from hub before being decoded. A better approach would be do this as pasm, which will very fast, which we can do a little later. With pasm your only delay is waiting for the hub slot to access memory.

  • I have been thinking about experimenting in pasm and I think you are right about it. I have moved the dira[coil] out of the loop. I tried running it and COIL never fires. Pretty typical of what usually happens. If I think about it for a few days I will probably figure out why. Does the SPIN interpreter as I have it now, interpret assembly, or do I need an assembler? How do I get started?
  • Beavis,

    First, the Spin compiler which runs on your computer knows how to compile *.spin file which contains assembly. Here's an example: of basic assembly on the Propeller when using Spin and Prop Tool.
    http://pi.gadgetoid.com/article/starting-with-spin-on-the-parallax-propeller

    Second, are you at all familiar with C or C++? I ask not because I want to start (yet another) Spin vs C/C++ language war, but because there is lots of help with homebrew EFI systems outside the parallax forums and, though Spin is pretty darned legible, you're likely to get even better help if it's a more common language.
    If you're not sure which is right, see this thread for one of the more polite language war threads :)

    Cheers,
    David
  • Beavis3215 wrote: »
    I have been thinking about experimenting in pasm and I think you are right about it. I have moved the dira[coil] out of the loop. I tried running it and COIL never fires. Pretty typical of what usually happens. If I think about it for a few days I will probably figure out why. Does the SPIN interpreter as I have it now, interpret assembly, or do I need an assembler? How do I get started?

    That is because the way SPIN works I think (not entirely sure) it needs to know about, fire_flag explicitly. The way to do that which I can see is to pass it as another parameter in your call to cognew, so that line becomes:
         Cognew(FIRE(T,fire_flag), @stack[0])
    

    Now a second parameter is passed to FIRE, but at present it only accepts one. Therefore the PUB needs to be amended thus:
    PUB  FIRE(Time,fire_flag) 
    
    Now both your Cognew and PUB FIRE parameter lists agree.

    I think you will save a little time too if you move the DIRA setting from within the repeat loop to between the PUB FIRE and repeat. This means it will only be set once on entry. It only needs to be set the once as you don't change direction in you code. Don't do that yet though.

    As you have an oscilloscope, I suggest you don't move the DIRA yet, but try the parameter passing first with DIRA where you have it. Then once we get the fire_flag recognised and firing your coil, move the DIRA to where I suggest and check with the scope to see if there is an improvement in response time of trigger to action.
  • In PUB FIRE(Time,fire_flag). fire_flag has to be "unique" to compile, so I have it as fireflag, for the same reason that T needs to be Time. Making fire_flag explicit does not make any noticeable difference in the way it runs. How does everyone highlight all their code yellow on the forum?
  • Beavis3215 wrote: »
    In PUB FIRE(Time,fire_flag). fire_flag has to be "unique" to compile, so I have it as fireflag, for the same reason that T needs to be Time. Making fire_flag explicit does not make any noticeable difference in the way it runs. How does everyone highlight all their code yellow on the forum?

    First off, the highlighting. If you look above the yellowish block you type your text into when replying, you will see a C as the fifth item in on a ribbon. Clicking on that inserts a couple of marker blocks to signify start of a code block and its end. I normally type [ code ] and [ /code ] , without the spaces as it save scrolling up and back down again. Tabs and spaces are handled differently within these markers. You do not see it whilst composing the message, but if you click the Preview button to the left of Post Comment, you will see a preview of what you have written and how it will be displayed on this message board.

    Ah, my lack of SPIN knowledge is the porblem here. With your Cognew and the added @ for address of the fire_flag symbol, ie a pointer to a data object in memory, its' location, we also need to amend PUB FIRE to use that parameter as a pointer.

    So now in PUB FIRE if you change all occurances of fire_flag to ptr_fire_flag, include on the PUB FIRE line. This is a reminder to us that it is really pointer to the memory location contain the data which is fire_flag.

    Then you need to do the following, on the repeat line change ptr_fire_flag to:
    long[ptr_fire_flag ]
    
    and also where it is set to 0 change to
    long[ptr_fire_flag ] := 0
    
    The long[ ] means to treat the memory adress specified within as a variable of long type, ie 32 bits. It requires an address to act on, this is supplies in the variable ptr_fire_flag as it really does contain the address of the memory location for fire_flag, because you have specified @fire_flag in your Cognew statement.
  • I think that when you say:
    long[ptr_fire_flag]
    

    A variable is being defined and is being used to pass a parameter, and maybe not a pointer.
    It definitely didn't run differently

    I could be totally wrong but that is my first instinct.
  • Beavis3215 wrote: »
    I think that when you say:
    long[ptr_fire_flag]
    

    A variable is being defined and is being used to pass a parameter, and maybe not a pointer.
    It definitely didn't run differently

    I could be totally wrong but that is my first instinct.
    I think we will have to ask someone more familar with SPIN to assist here.

    Would you please post you current code so I can view. It may just be a small typographical problem? You may use the code posting as explained in previous message, you will get a scrolling window if it's over a certain size, but it is a short program at present.

  • Beavis3215 wrote: »
    I think that when you say:
    long[ptr_fire_flag]
    

    A variable is being defined and is being used to pass a parameter, and maybe not a pointer.
    It definitely didn't run differently

    I could be totally wrong but that is my first instinct.

    AH I think I see the problem. I think reading the manual that fire_flag should be in a DAT block. So:
    DAT
    
    fire_flag     long       0
    ptr_fire_flag long      @fire_flag     ' address of fire_flag
     
    

    Then we need to undo passing fire_flag to PUB fIRE so:
       Cognew(FIRE(T), @stack[0])  
    

    Then:
    PUB  FIRE(Time)
    

    That may work.
  • Here's the code, I appreciate your help.
    1632 x 1224 - 540K
    1632 x 1224 - 859K
  • Try this copy and past into a new file, not you current ones.
    '     Spark Control Module
    
    CON
    
        _clkmode = xtal1 + pll16x
    
        _xinfreq = 5_000_000
    
        CLK_FREQ = (_clkmode >> 6) * _xinfreq
    
        MS_001   = CLK_FREQ/1_000
    
        US_001   = CLK_FREQ/1_000_000                  'Clock ticks in one microsecond
    
        CAM      = 15                                  'Camshaft position sensor connects to pin 15
    
        COIL     = 16                                  'Ignition coil driver circuit connects to pin 16
    
    VAR LONG T, Delay, STACK[10]
    DAT
    
    fire_flag     long      0
    ptr_fire_flag long      @fire_flag                  ' Address of fire flag
     
    PUB ECU
    
         dira[CAM] := 0
    
         waitpeq(1 << CAM, 1 << CAM, 0)                'Wait for cam sensor to go high
                                                       '                                 > Cam sync sequence
         waitpne(1 << CAM, 1 << CAM, 0)                'Wait for cam sensor to go low
         
         Cognew(FIRE(T), @stack[0])
         
         repeat
    
             T := -cnt
             
             waitpeq(1 << CAM, 1 << CAM, 0)
             
             T += cnt  - 544                           'Count clock ticks until cam sensor goes high
                                                          
             fire_flag := 1                            'Once cam sensor goes high, fire coil
    
    
    PUB  FIRE(Time)
    
       dira[COIL] := 1
    
       repeat
    
           repeat until long[ptr_fire_flag ]== 1
    
           Delay := 30 * Time/360                      'Calculate delay neccesary for cam to travel from 30 Degrees BTDC to TDC
    
           waitcnt(cnt + (Delay * MS_001))             'Execute delay
    
           outa[COIL] := 1                             'Fire coil
    
           waitcnt(cnt + (2 * Delay* MS_001))          'Fire for this duration
    
           outa[COIL] := 0
    
           long[@ptr_fire_flag ] := 0   
    
    It compiles but I don't have a Propeller board nor oscilloscope here at present.
  • I tried it, still no change. I feel that I should deploy Parallax serial terminal, so that I can verify communication between the cam sensor and Propeller chip is consistent. I can calculate what "time" should be from the oscilloscope and get "time" from the chip. Never used pst yet. I need to learn.

    Question: Should it matter that the delays are calculated to milliseconds, microseconds, or left in ticks?

    I had a faulty but right idea implementation that would run in millisecond format, .1 millisecond format and unstable on .01 millisecond format, but wouldn't ever fire on 1 microsecond format.

    This program runs but the output is faulty due to the time in the firing delay being unaccounted for in the timing loop. Just a lot of bad ideas here I am trying to get away from.
  • You can use time in whatever format you are comfortable with. However, to convert to clock count in the propellor does take time, so best to use the fast method which is clock, and only convert to and from time formats for easier human reading when necessary. So you can use a formula to convert say 100us delay to counter value as part of your initial step befoer the main repeat loop

    That program could be speed up a lot if you were to change all your clkfreq/1000 to clk_cnt_milliseconds and use that throughout as a constant throughout. clkfreq is constant, not dynamic, so put clk_cnt_milliseconds goes in your VAR section and you set it to clkfreq/1000 before any of the repeat loops.

    You don't need to do this at all if you do the above, but clkfreq / 1000 is close to ( clkfreq >> 10 ) which is equivalent to clkfreq / 1024. Then do all your timings, not on millisecond, but on this faster binary method. You will need to analyse your code to make sure you don not mix real milliseconds and the near binary version, and to adjust results accodingly, but it will be a lot faster if you ever need to use a value which changes during the loop.

    Does that make sense?
  • I was just interested in why that old program would care which method was used.
  • Beavis3215 wrote: »
    I was just interested in why that old program would care which method was used.

    What do you mean by "method"
  • The old faulty program would run with different stability depending on how the delays were configured.

  • I added a line of code to see if we ever get out of the loop. We don't, in this example or with my code. For some reason there is still a problem passing the flag. Pin 16 will go high with the added code, but not without.
  • Beavis3215 wrote: »
    The old faulty program would run with different stability depending on how the delays were configured.
    Please make a copy of that program at make changes for clkfreq/1000 as mentioned in my post 2 back from me (just realised my local time might not help). It may speed things up if the SPIN compiler does not look for clkfreq being a constant, thus it would take a long time for the divide within the repeat loop and the various wait instructions.
  • Would run kind of correctly with all 3 delays set as clkfreq/1000, 10000, or 100000, but not with 1000000
  • Beavis3215 wrote: »
    I added a line of code to see if we ever get out of the loop. We don't, in this example or with my code. For some reason there is still a problem passing the flag. Pin 16 will go high with the added code, but not without.

    Paste this in for pub fire. I've removed the @ as ptr_fireflag already contains an address.
    PUB  FIRE(Time)
    
       dira[COIL] := 1
    
       repeat
    
           repeat until long[ ptr_fire_flag ]== 1      
               outa[COIL] := 1                         '***************added this
           Delay := 30 * Time/360                      'Calculate delay neccesary for cam to travel from 30 Degrees BTDC to TDC
    
           waitcnt(cnt + (Delay * MS_001))             'Execute delay
    
           outa[COIL] := 1                             'Fire coil
    
           waitcnt(cnt + (2 * Delay* MS_001))          'Fire for this duration
    
           outa[COIL] := 0
    
           long[ptr_fire_flag ] := 0
    
  • That's where we were before when I made the comment about
    Long[ptr_fire_flag]
    

    being just a variable.
    We tried that already, and shouldn't be different from
    Long[fire_flag]
    

    or the parameter
    fire_flag
    

    In the Propeller education manual, they show parameter passing during the starting of cognew, not after the cog has started. This may be the problem.
  • Beavis3215 wrote: »
    That's where we were before when I made the comment about
    Long[ptr_fire_flag]
    

    being just a variable.
    We tried that already, and shouldn't be different from
    Long[fire_flag]
    

    or the parameter
    fire_flag
    

    In the Propeller education manual, they show parameter passing during the starting of cognew, not after the cog has started. This may be the problem.

    You may be right but I'm sure it can be done in some manner.

    However, I am WRONG about the DAT block because that is local to the cog you run in I think, or maybe they both get local copies, not sure yet, too much manual to read and take in in a single sitting.

    Please try this bit of code it should be quicker. It is your very original version 1 with the clkfreq / 1000 outside the loop.
    '     Spark Control Module
    
    
    
    Con
      _xinfreq = 5000000
      _clkmode = xtal1 + PLL16x
         ' this is a constant value, so only needs calculating once
    
    Var
       Long milliseconds,TD, clk_cnt_milliseconds
    
    PUB ECU1
       dira[15] := 0
       dira[16] := 1
       outa[16] := 0
       clk_cnt_milliseconds := clkfreq/1000
       
         Repeat until ina[15] == 1              'Wait for cam sensor to go high}
                                                    '                                'cam sync sequence
         Repeat until ina[15] == 0              'Wait for cam sensor to go low}
       Repeat  
         Repeat until ina[15] == 1              'Wait for cam sensor to go high
            waitcnt(clk_cnt_milliseconds+cnt)           'Time one complete revolution of cam in milliseconds
            milliseconds ++
         TD := 30 * milliseconds / 360          'Calculate a time delay for cam to move from 30 degrees BTDC to TDC
         waitcnt(TD*clk_cnt_milliseconds+cnt)         'Wait for calculated time
         outa[16] := 1                          'Fire spark plug
         waitcnt(6*TD*clk_cnt_milliseconds+cnt)       'Fire spark plug for this duration
         outa[16] := 0                          'Reset ignition coil
         milliseconds := 0                      'Clear timer
    

  • This mindset I had in this program was incomplete, because the cam timer stops then executes Fire. Fire time is not accounted for so the cam timer is off by 7*TD plus code delays, which would accumulate error quickly.
  • Beavis3215 wrote: »
    This mindset I had in this program was incomplete, because the cam timer stops then executes Fire. Fire time is not accounted for so the cam timer is off by 7*TD plus code delays, which would accumulate error quickly.
    Agreed, I can see that now you've pointed it out, which is obviously why you moved to a second cog.

    I have dug out my Propeller Starter Kit, hopefully still functional, and even a bag of resistors and leds to hook up on the breadboard to monitor output pins.

    It's late-ish here, currently 11PM, and I am getting rather tired, are you able to continue this tomorrow? That way we can try and learn SPIN togther, and I'll realise where I have been going wrong?
  • pjvpjv Posts: 1,903
    Hello Beavis;

    Just a word of caution here:

    Spin might work as a proof of concept at slow speeds, but on a practical scale you will need to use PASM for realistic RPMs.

    If one were to take a cam speed of 3600 RPM (high, but not outrageous) that is 60 revolutions every second. That translates to 21600 cam degrees per second, or 46 usec per degree.

    There is no way a SPIN loop can reliably deal with those higher speeds with any accuracy of, say, one degree. If there was only a single speed, then possibly offsets could be made to fake out the correct timings, but I assume you will want the speeds to be variable. And in that case the overhead of the loop will itself be a serious and varying proportion of the desired time elements. A non linear proportion for each speed, leading you down a nightmare of frustration.

    PASM, however, can do this all very easily..... time to jump in!

    Cheers,

    Peter (pjv)
Sign In or Register to comment.