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.
Add this line to the very end of that modified first version I posted most recently:
milliseconds := 7*TD*clk_cnt_milliseconds ' adjust for how lonw waited in total, but negative timewise
It sets milliseconds to value, which is the time you have spent in those waitcnt periods after the cam sensor goes high. Which means it will adjust for the next pass and be closer to waht you want. That is not as close as you can get. You can make it quicker by changing this line:
TD := 30 * milliseconds / 360 'Calculate a time delay for cam to move from 30 degrees BTDC to TDC
to this:
TD := milliseconds / 12 'Calculate a time delay for cam to move from 30 degrees BTDC to TDC
because 30/360 is 1/12, which is what you are after. Better would be to make a constant in the CON section called, thirty_degrees set to 30/360.
To Execute even faster in SPIN we need to see if we can find a solution for replacing the / 12, because divsion is long winded when performed in software. Could possible use the log and anti-log table in the ROM
How did you calculate $1555_5555 as the 30 will later be variable. Isn't the timer short by the 2 waitcnt delays?
Does the attached method have a chance, why or why not? See attached. Trying to implement parallel processing and have a pure cam time measuring method. It seems to be simple and have clean structure and not work, all at the same time. LOL
As Peter pointed out spin is an interpreted language and therefore a bit on the slow side for doing this. At 539 rpm you have 115mSec to do all your calculations so it may be possible, but at 10,000 rpm you only have a bit over 6mSec. Best to switch to PASM sooner rather than later.
I am also wondering why you are having one cog send a firing signal to a second cog. Why not have the firing signal go out to a pin directly from the first cog?
Finally, the time of one engine revolution does not change much from one revolution to the next so why not base the calculation of the next firing based on the time of the previous revolution. That allows you to do the calculations during 300+ degrees of the cam.
PS, Also, do the calculations with cnt values rather than converting back and forth between time and degrees. Makes the calculations faster and more accurate.
Consulting the manual (hint, hint), one can determine the multiplier for ** by taking the desired fractional value (30 / 360 in this case) and multiplying it by 2 ^ 32.
If that value is going to be a variable within a small range, you may want to pre-calculate multipliers for ** for that range. Multiplication and division take a long time, so removing one of these from that step saves a little time.
Isn't the timer short by the 2 waitcnt delays?
Nope. Look carefully; the code folds back on itself which is why there's a little redundant code outside the loop.
This could all be for naught if the speed of your rig is too fast for Spin.
Beavis
Echoing what pjv said, I have built EFI systems based on a propeller running PASM code.
These systems handled synchronized firing of injectors at engine speed in excess of 10,000 RPM.
No problem at all. Definitely look at PASM.
I will definitely look at pasm. One thing to note is that the engine will run at maximum 3200 rpm crankshaft speed which is 1600 rpm camshaft speed. It may work in spin, for now. I am just getting a handle right now on the science of fixed timing. Pasm will most definitely be required in a little while as features get added to the system. Once I can fire a spark plug whenever I need to, next comes the fuel injector, the throttle position sensor, engine temperature, oxygen sensor and so on. Then comes the map of engine requirements at the mixture of all of these inputs, that I will have to dyno out. It may work with spin now, but even I can see with the addition of one of these features, when working with huge instructions that take 20 assembly instructions, that the management of series instructions get harder and harder. Like I said earlier, I am a beginner, tying to just get a handle on spin right now. Don't think that I havn't spent days in the Propeller manual trying to figure out your code. When I ask a question, and you answer in not just code, but exceptional code, I can make a few assumptions. You at least present yourself as an electrical engineer, which I really respect. If you aren't one, you should have been one. With all the programming I have studied in the 40 years of my life that I have been programming, SPIN is about the least structured mess I have ever seen. To be honest though, I have never attempted such a time sensitive project. When you answer In "The best" spin code to my basic mentality of spin, there are multiple levels of learning for me to catch up, as there is little documentation to go with your code. If someone asked me "how do I evaluate why this 2 cylinder engine runs on one cylinder?" And I answer strictly, "oh I can solve it with an infared thermometer, a spark tester and a cylinder leak down tester", and the askee is a self proclaimed beginner, how many levels of learning are required for him to catch up? I really respect your genius in programming, or I wouldn't be asking for your help, but the patience you pledged to me when I first told you I was a beginner is lacking. I'm not trying to pound free code out of you, but merely asking for help trying to figure something out, that I really don't understand. I'm not going to use code I don't understand on face value. I'm pretty sure you were the one helping me with code in my SX28 days in about 2008. The Propeller Education manual, The Propeller manual, and about 2 months experience only get you so far. I have a long way to go. I picked something really complicated and I know it, but that is how I learn. If you make your own EFI, you will have a good understanding of commercially made EFI, which is one of my goals. I appreciate the patience you have given me, and am hoping you still will be patient with me.
Regarding JonnyMac's multiplier value of $1555_5555 and how it is calculated, I will do an explanation on a theoretical machine on which calculations are in decimal, so easy for humans to understand, and that each long on this machine can hold a value between 0 and 10,000.
The ** operator which was used mutltiplies two longs together, and because of the values they hold, it produces a two long answer, of which the high long is the value which is returned. So if each long that we multiplied was 8,000 decimal, the first long is a real valueof 8000, but the second long is really a fractional part, in this case it 8000 / 10000, it represents 0.0 . Thus the result would be 64,000,000. However, ** returns the high long of the calculation, and that is the number of 10,000's which would 6,400. All digits below the 10,000 mark represent fractional values, decimal places in this example, and fortunately they are all zero and can be ignored. If you can multiply 8,000 * 0.8 in your head you may do so, or do it long hand or use a calculator.
What if wanted one twelth as a fraction, well that is done the same way as you would normally which is to use 1 / 12, which on my calculator results in 0.083333333333 or so. However in our theoreticak machine
we represent it fractional as filling a long which can hold 0 to 9,999. In other words our next digits column is 10,000, so in this case for dividing by 12 we first calculate as a fraction by 10,000 / 12 = 833.333333 . Now I'm sure you recognise that string of digits in the one twelth calculation I did, although the decimal point is in a different position. And what is that difference? Well because in the second / 12 I divided it into 10,000 instead of 1, we need to move 4 decimal places, because the largest digit column is 1,000 in our decimal machine, thus 0.83333 becomes 833.33 (I left off some of the 3s ).
Now that we have 833, because we can only use the integer part of our calculation. If we multiply 8,000 by 833, we get 6,664,000. The ** operator returns the high long of our result, which in this machine is any digit column from 10,000 upwards, thus we get 666. The 4000 is a fractional part which we lose. Now if you use your calculator to multiply 8,000 * 0.083333333 we get 666.666666 etc. So it's not quite accurate because it's a recurring number. We only handle integers so the answer is 666.
Returning to the real world of the Propeller, which operates in binary, and hexadecimal is a simple representation of such in shorter form. If you muliply $1555_5555 by 12 you will get a value very close to $1_0000_0000, so you now see that $1555_5555 is used to represent 1/12.
I asked Jon in a different discussion last night, if the 2 cog version we were working on yesterday, had any hope. The answer was a "rebuilt" super version of my old code, so I think his answer is no. I am going to see if his code runs as advertised, and if it does it will probably take me a while to digest the SPIN, and examine the output. I don't think I will find "code folding back on itself" in the propeller manual, so it may take me a while. I'm not sure I'm willing to give up on 2 cog either.
I appreciated you as well as all the others for helping me learn. I work at my real job Tuesday through Saturday, so I may stop and visit some in the evenings if I get stuck.
I have a day job, too; luckily mine involves programming in Spin/PASM most of the time. When I offer help, I will never offer crippled code because I think that's a terrible disservice. I am a self-taught programmer and [small-scale] electronics engineer. That is to say that I try to be very practical with what I do. Again, I'm going to show you what I think is best is best given limited information about your problem; it's up to you to work toward understanding. If you want consulting, I (and others) can do that for you, but it's not free.
I think, as has been suggested, you may need to learn to code in PASM, too, but Spin is a great way to get a handle on the Propeller. In fact, unlike other versions of assembly, there is a very close relationship in many cases between Spin and PASM.
For example, this Spin code:
waitpeq(CAM_MASK, CAM_MASK, 0)
... looks like this in PASM:
waitpeq CAM_MASK, CAM_MASK
This is one simple example -- there are others. I'm not a very strong Assembly programmer but feel comfortable with PASM and am able to solve most of my programming needs (when speed is required) with it. Chip designed PASM first, then Spin to be very closely linked -- this helped him fit the Spin interpreter into a single cog.
I've concluded that my participation in your threads is more problematic than helpful so I will bow out now. Good luck with your project.
With all the programming I have studied in the 40 years of my life that I have been programming, SPIN is about the least structured mess I have ever seen.
If that is your opinion of Spin, and with 40 years under your belt.... why would choose Spin of C or C++ on the Propeller? The PropGCC support is excellent here on the forums.
The only reason I am using spin at this time is a starting point on a very long journey. With the limited time I have, one of my goals is "one positive step each day", whether it is concept, hardware or software. I am prepared to take years to do this. C will have to wait until it is necessary, as it is a quantum leap from where I am now.
@pjv: 3600 camshaft RPM is high, but not extreme: that works out to only 7200 RPM at the crankshaft.
Also- 3600 RPM / 60 seconds = 60 revolutions per second. 60 revolutions per second * 360 degrees= 21600 degrees per second. 21600 degrees per second / 1000 milliseconds = 2.16 millisecond per degree.
@Beavis3215:
Consider saving CNT in variables explicitly for CAM and the COIL timer so you can act on those more easily. Also consider timer roll-over conditions.
If the cam trigger is "centered" on 30 degrees, the Hall will trigger high just before 30, and go low just after, so you have to take that delay into account.
Since cogs can read and write pins independently of each other, though they are a shared resource, maybe think about running a cog solely for firing the coil, or multiple cogs for multiple coils, if you have a multiple cylinder engine. Each of these, if there are more than one, should be able to each do it's timing calculations and such independently of the others. And, if you have multiple cylinders / coils that can fire as "wasted spark", one cog could probably handle 2 coils, and maybe 4 if 2 have a 180 degree offset calculated in.
From a simple 1 tooth trigger to multiple triggers involving the crankshaft, things get a lot more complicated, and you haven't even touched fuel yet. If and when you get there, there are many more things to complicate the works.
Hoping to have a fuel cog, that works from the same fire_flag pulse and T. It will have a different delay to situate it in the running sequence, and will modulate the pulse width of FUEL using many parameters based on other sensors yet to come. As I have said before, this engine will never go over 3200rpm crankshaft speed and is single cylinder, so at least for now in prototyping just the science, SPIN may or may not be able to handle what I'm doing, yet to be determined. The main goal for now is for me is to be able to write a program that can fire a spark plug reliably at TDC at any RPM between 300 and 3200 using the present hardware.
@pjv: 3600 camshaft RPM is high, but not extreme: that works out to only 7200 RPM at the crankshaft.
Also- 3600 RPM / 60 seconds = 60 revolutions per second. 60 revolutions per second * 360 degrees= 21600 degrees per second. 21600 degrees per second / 1000 milliseconds = 2.16 millisecond per degree.
If your figure is 21600 degrees/second, then you need to take the reipricol to find the time time / degree. This
1 / 21600 = 46.3 microsends! That's quite a difference and should highlight the necessity of speedy calculations to achieve your goal.
In Propeller instruction terms, which are clock frequency / 4, if I recall correctly, that would typically be 80MHz / 4 = 20MHz = 50ns (nano seconds). From that figure we can work out the number of instructionsin your 46.3us period = 46.3us / 50ns = 926 instructions.
Using SPIN the language takes different times for the different operations it performs, some are vey quick and some take longer becuase of fetching bytecode from hub memory, interpreting and fetching your data etc. This is way posters have been expressing their concern for using SPIN to meet you aims.
You can certainly use SPIN for developing your algorithm with your, in comparison, slower test rig, to ensure your have the right approach. Then you will need to translate that algorithim to PASM which will run in the cog almost completely and thus reduce timing delays whilst waiting to access variables in the hub.
The best time to access the hub is after you've initiated and fired the coil in a single cog. As a previous poster has commented, the RPM does not change greatly from one trigger to another.
May I ask, have you tried the code which JohnnyMac kindly provided? And what were the results?
I agree 100% with 78rpm's comments in the previous post.
What I would also suggest, whether you continue in spin for a proof of concept, or switch to pasm is that you go with degree based timing rather than the current approach. By that I mean that you dedicate a cog to calculating the current position of the cam and crank shafts in degrees and place that in a global variable that all the cogs can access.
It would be nice to have a pulse per degree signal from the crankshaft for that, but most likely that is not available. What gears do you have, and how many teeth do they have?
3200 rpm crankshaft speed is 1600 rpm camshaft speed.
Not quite what I meant. The cam is turned by the crankshaft at half the rpm of the crankshaft so the camshaft gear will have twice as many teeth as the crankshaft gear. How many gear teeth are there on one of them?
By using a pulse from each tooth on either gear you can divide each rotation into several segments and get a more precise angular velocity. It can also simplify the position calculation since it can extrapolate the position between two known angles.
According to my math,1600 rpm is 9600 degrees/sec.
1600 r/min * 360deg/r * 1min/60sec
360 degrees takes 37.5 milliseconds.
30 degrees takes 3.13 milliseconds.
Seems pretty do able.
The math is good, and assuming you start the calculation immediately after the spark is initiated you would have at least 34.37mS, and possibly as much as 37.5mS to execute all the code and be ready for the next spark. Wonder if spin is fast enough.
The spark comes 3.13 milliseconds after it is initiated at this rpm. The idea for now is fixed timing at TDC. The steel target on the cam is placed at 30 degrees BTDC. The program calculates the 3.13 in clock ticks and executes the delay for the cam to travel 30 degrees, or that is what I'm trying to accomplish.
Any idea if the variables T and fire_flag will be shared in the 2 COGs? That has been the roadblock so far. I'm on a COG learning curve. I cant find any examples of COGs sharing variables in any book or manual, so I hope this is right. I am aware of time I will need to account for in the hub, and from code execution. I haven't test ran it yet to find out if it works.
The spark comes 3.13 milliseconds after it is initiated at this rpm. The idea for now is fixed timing at TDC. The steel target on the cam is placed at 30 degrees BTDC. The program calculates the 3.13 in clock ticks and executes the delay for the cam to travel 30 degrees, or that is what I'm trying to accomplish.
Any idea if the variables T and fire_flag will be shared in the 2 COGs? That has been the roadblock so far. I'm on a COG learning curve. I cant find any examples of COGs sharing variables in any book or manual, so I hope this is right. I am aware of time I will need to account for in the hub, and from code execution. I haven't test ran it yet to find out if it works.
If I am interpreting what the propeller manual is saying (P 212) about global variables correctly then those variables should be accessible to both cogs. I am currently away from home but I will see if I can rig up something to test your code on the weekend.
Comments
Add this line to the very end of that modified first version I posted most recently: It sets milliseconds to value, which is the time you have spent in those waitcnt periods after the cam sensor goes high. Which means it will adjust for the next pass and be closer to waht you want. That is not as close as you can get. You can make it quicker by changing this line: to this: because 30/360 is 1/12, which is what you are after. Better would be to make a constant in the CON section called, thirty_degrees set to 30/360.
To Execute even faster in SPIN we need to see if we can find a solution for replacing the / 12, because divsion is long winded when performed in software. Could possible use the log and anti-log table in the ROM
This is intended to run in its own cog (use cognew). You may need to add a global variable bias adjustment for the delay before firing.
Does the attached method have a chance, why or why not? See attached. Trying to implement parallel processing and have a pure cam time measuring method. It seems to be simple and have clean structure and not work, all at the same time. LOL
I am also wondering why you are having one cog send a firing signal to a second cog. Why not have the firing signal go out to a pin directly from the first cog?
Finally, the time of one engine revolution does not change much from one revolution to the next so why not base the calculation of the next firing based on the time of the previous revolution. That allows you to do the calculations during 300+ degrees of the cam.
PS, Also, do the calculations with cnt values rather than converting back and forth between time and degrees. Makes the calculations faster and more accurate.
Consulting the manual (hint, hint), one can determine the multiplier for ** by taking the desired fractional value (30 / 360 in this case) and multiplying it by 2 ^ 32.
If that value is going to be a variable within a small range, you may want to pre-calculate multipliers for ** for that range. Multiplication and division take a long time, so removing one of these from that step saves a little time.
Nope. Look carefully; the code folds back on itself which is why there's a little redundant code outside the loop.
This could all be for naught if the speed of your rig is too fast for Spin.
Echoing what pjv said, I have built EFI systems based on a propeller running PASM code.
These systems handled synchronized firing of injectors at engine speed in excess of 10,000 RPM.
No problem at all. Definitely look at PASM.
Regarding JonnyMac's multiplier value of $1555_5555 and how it is calculated, I will do an explanation on a theoretical machine on which calculations are in decimal, so easy for humans to understand, and that each long on this machine can hold a value between 0 and 10,000.
The ** operator which was used mutltiplies two longs together, and because of the values they hold, it produces a two long answer, of which the high long is the value which is returned. So if each long that we multiplied was 8,000 decimal, the first long is a real valueof 8000, but the second long is really a fractional part, in this case it 8000 / 10000, it represents 0.0 . Thus the result would be 64,000,000. However, ** returns the high long of the calculation, and that is the number of 10,000's which would 6,400. All digits below the 10,000 mark represent fractional values, decimal places in this example, and fortunately they are all zero and can be ignored. If you can multiply 8,000 * 0.8 in your head you may do so, or do it long hand or use a calculator.
What if wanted one twelth as a fraction, well that is done the same way as you would normally which is to use 1 / 12, which on my calculator results in 0.083333333333 or so. However in our theoreticak machine
we represent it fractional as filling a long which can hold 0 to 9,999. In other words our next digits column is 10,000, so in this case for dividing by 12 we first calculate as a fraction by 10,000 / 12 = 833.333333 . Now I'm sure you recognise that string of digits in the one twelth calculation I did, although the decimal point is in a different position. And what is that difference? Well because in the second / 12 I divided it into 10,000 instead of 1, we need to move 4 decimal places, because the largest digit column is 1,000 in our decimal machine, thus 0.83333 becomes 833.33 (I left off some of the 3s ).
Now that we have 833, because we can only use the integer part of our calculation. If we multiply 8,000 by 833, we get 6,664,000. The ** operator returns the high long of our result, which in this machine is any digit column from 10,000 upwards, thus we get 666. The 4000 is a fractional part which we lose. Now if you use your calculator to multiply 8,000 * 0.083333333 we get 666.666666 etc. So it's not quite accurate because it's a recurring number. We only handle integers so the answer is 666.
Returning to the real world of the Propeller, which operates in binary, and hexadecimal is a simple representation of such in shorter form. If you muliply $1555_5555 by 12 you will get a value very close to $1_0000_0000, so you now see that $1555_5555 is used to represent 1/12.
I appreciated you as well as all the others for helping me learn. I work at my real job Tuesday through Saturday, so I may stop and visit some in the evenings if I get stuck.
I think, as has been suggested, you may need to learn to code in PASM, too, but Spin is a great way to get a handle on the Propeller. In fact, unlike other versions of assembly, there is a very close relationship in many cases between Spin and PASM.
For example, this Spin code:
... looks like this in PASM:
This is one simple example -- there are others. I'm not a very strong Assembly programmer but feel comfortable with PASM and am able to solve most of my programming needs (when speed is required) with it. Chip designed PASM first, then Spin to be very closely linked -- this helped him fit the Spin interpreter into a single cog.
I've concluded that my participation in your threads is more problematic than helpful so I will bow out now. Good luck with your project.
If that is your opinion of Spin, and with 40 years under your belt.... why would choose Spin of C or C++ on the Propeller? The PropGCC support is excellent here on the forums.
Also- 3600 RPM / 60 seconds = 60 revolutions per second. 60 revolutions per second * 360 degrees= 21600 degrees per second. 21600 degrees per second / 1000 milliseconds = 2.16 millisecond per degree.
Consider saving CNT in variables explicitly for CAM and the COIL timer so you can act on those more easily. Also consider timer roll-over conditions.
If the cam trigger is "centered" on 30 degrees, the Hall will trigger high just before 30, and go low just after, so you have to take that delay into account.
Since cogs can read and write pins independently of each other, though they are a shared resource, maybe think about running a cog solely for firing the coil, or multiple cogs for multiple coils, if you have a multiple cylinder engine. Each of these, if there are more than one, should be able to each do it's timing calculations and such independently of the others. And, if you have multiple cylinders / coils that can fire as "wasted spark", one cog could probably handle 2 coils, and maybe 4 if 2 have a 180 degree offset calculated in.
From a simple 1 tooth trigger to multiple triggers involving the crankshaft, things get a lot more complicated, and you haven't even touched fuel yet. If and when you get there, there are many more things to complicate the works.
If your figure is 21600 degrees/second, then you need to take the reipricol to find the time time / degree. This
1 / 21600 = 46.3 microsends! That's quite a difference and should highlight the necessity of speedy calculations to achieve your goal.
In Propeller instruction terms, which are clock frequency / 4, if I recall correctly, that would typically be 80MHz / 4 = 20MHz = 50ns (nano seconds). From that figure we can work out the number of instructionsin your 46.3us period = 46.3us / 50ns = 926 instructions.
Using SPIN the language takes different times for the different operations it performs, some are vey quick and some take longer becuase of fetching bytecode from hub memory, interpreting and fetching your data etc. This is way posters have been expressing their concern for using SPIN to meet you aims.
You can certainly use SPIN for developing your algorithm with your, in comparison, slower test rig, to ensure your have the right approach. Then you will need to translate that algorithim to PASM which will run in the cog almost completely and thus reduce timing delays whilst waiting to access variables in the hub.
The best time to access the hub is after you've initiated and fired the coil in a single cog. As a previous poster has commented, the RPM does not change greatly from one trigger to another.
May I ask, have you tried the code which JohnnyMac kindly provided? And what were the results?
What I would also suggest, whether you continue in spin for a proof of concept, or switch to pasm is that you go with degree based timing rather than the current approach. By that I mean that you dedicate a cog to calculating the current position of the cam and crank shafts in degrees and place that in a global variable that all the cogs can access.
It would be nice to have a pulse per degree signal from the crankshaft for that, but most likely that is not available. What gears do you have, and how many teeth do they have?
Not quite what I meant. The cam is turned by the crankshaft at half the rpm of the crankshaft so the camshaft gear will have twice as many teeth as the crankshaft gear. How many gear teeth are there on one of them?
By using a pulse from each tooth on either gear you can divide each rotation into several segments and get a more precise angular velocity. It can also simplify the position calculation since it can extrapolate the position between two known angles.
1600 r/min * 360deg/r * 1min/60sec
360 degrees takes 37.5 milliseconds.
30 degrees takes 3.13 milliseconds.
Seems pretty do able.
The math is good, and assuming you start the calculation immediately after the spark is initiated you would have at least 34.37mS, and possibly as much as 37.5mS to execute all the code and be ready for the next spark. Wonder if spin is fast enough.
Any idea if the variables T and fire_flag will be shared in the 2 COGs? That has been the roadblock so far. I'm on a COG learning curve. I cant find any examples of COGs sharing variables in any book or manual, so I hope this is right. I am aware of time I will need to account for in the hub, and from code execution. I haven't test ran it yet to find out if it works.
If I am interpreting what the propeller manual is saying (P 212) about global variables correctly then those variables should be accessible to both cogs. I am currently away from home but I will see if I can rig up something to test your code on the weekend.