Arduino as ECU?
#61
Boost Pope
iTrader: (8)
Join Date: Sep 2005
Location: Chicago. (The less-murder part.)
Posts: 33,020
Total Cats: 6,588
Why not use external A-D converters? Take the burden off the processor and just fetch the values in a single read operation. You can get very high quality 10 and 12 bit multi-channel converters quite cheaply these days. With the servo throttle in particular, I'd want the main and redundant sensors to be on separate A-D converters anyway.
Don't get me wrong, I still think that you're going to die as a result of this, and probably take a lot of other people out with you. But you'll definitely get mentioned in EE Times and The SAE International Journal.
I understand the mechanics, I need to know the flow rate per microsecond (I have found a webpage that claims 212cc per min on a standard injector, is this right?),
You'll get slightly different values from different sources, which probably reflects the use of different fuel pressures.
Also, in a pulsed environment, you have to take into account the lag time on both opening and closing. In general, we tend to figure that for a high-impedance injector of the type we tend commonly to encounter, the actual flow time tends to be around 1 to 1.25ms less than the commanded on time, give or take.
what voltage/amperage they run at, and also if they need to be pulsed (i.e. if I supply continuous power will it burn out the solenoid?).
The vast majority of all fuel injectors in the world today are high impedance, including all injectors found as OEM on '89-'05 MX5s. These were the first to be invented, and are still the most common in the sum of extant passenger cars today.
High impedance injectors typically have a winding resistance in the general neighborhood of 10-14 ohms. Assuming a supply voltage of 12 volts, you'll have around 1 amp of current passing through each on, give or take. It's not necessary to regulate the supply voltage, you just put battery on one end and provide a switched closure to ground on the other. You can run them at 100% duty cycle and they will be just fine. Hi-z injectors are commonly referred to as saturated injectors, as their coils are designed to run in full saturation all the time.
Low impedance injectors are rapidly gaining in popularity. They are often found in high-performance applications using very large flowrates, and are starting to show up as OEM equipment on more and more cars, as they also have a beneficial effect on emissions at idle and very low duty cycle.
In a lo-z injector, the coil winding is generally in the 2-3 ohm neighborhood. Obviously this necessitates a more complex control scheme than simple on or off, or they'd just melt down. Also known as "peak and hold" injectors, you drive these by applying full voltage for the first millisecond or two, and then throttling them back to a much lower holding voltage. The idea is to slam the pintle open as quickly as possible by throwing gobs of current at it, and then, once it's open, restricting the current to only the level necessary to prevent it from closing without burning up the coil.
This can be accomplished in one of two ways. You can either PWM it, or you can use a linear analog circuit to control it. PWM is the method implemented in the MS, and while it's easy to do and very efficient, it has the disadvantage of generating a lot of electrical noise. The National LM1949 is an example of a chip designed for linear control, as used by JBPerf in their Peak & Hold driver board. While going this route raises the parts count and also generates a lot more heat, it doesn't produce a lot of noise and offloads the burden of worrying about injector control from the CPU. It's a closed-loop device using a current-sense resistor, so you just give it a simple on/off pulse and it figures out the rest.
As a result of all this, lo-z injectors have much shorter lag times than hi-z injectors, meaning you have much better control over fuel delivery at very low duty cycles, such as when trying to idle a 1.8 engine on 750cc injectors. (Don't laugh, some folks here are doing precisely this.)
Whatever you do, don't get taken in by the old trick of just putting 10 ohm resistors in series with lo-z injectors and then driving them with a conventional on/off signal. This is stupid, bad, wasteful, expensive, inefficient, and defeats the whole point of having bought lo-z injectors in the first place.
hmmm, timing from rising and falling edge would be much better... I'll add an option for it in my code, so it can be activated if needed.
Traditionally, we start the ignition timer on the leading edge of CKP (75° BTDC) on the principle that this is the shortest amount of time that we can get between the trigger event and the actual ignition event. Less uncertainty.
OTOH, as Jason and others have pointed out, crankshaft angular velocity is not a constant. The crankshaft accelerates during the first quarter revolution after a combustion event has been initiated, but then it starts to decelerate during the second quarter rev as the aforementioned combustion event concludes whilst another cylinder is simultaneously approaching the latter half of its compression cycle and increasingly opposing the movement of the crank.
Would taking a sample at 5° BTDC be better? It means that you're free-running for longer on the timer, but will the crankshaft's velocity when at 5° be closer to its velocity at the time of the ignition event than it would be at 75°?
Is there a physicist in the house?
#62
Right, I'd forgotten you were wanting to do throttle-by-wire. In that case, you'll have, at an absolute minimum, two TPSs (one on the pedal, one on the throttle shaft), and if you're smart and enjoy being alive, you'll have two on the pedal and two on the shaft, with cross-checking between them.
I was thinking about adding a fully redundant backup for the throttle, but that defeats the point of using a lowcost MCU board like the Arduino.
If the entire ECU can be built as cheap as possible, then one could simply run two in parallel, and we have a basic FADEC
Also, would anybody honestly, seriously and actually trust their lives to a £6 RC servo?
Why not use external A-D converters? Take the burden off the processor and just fetch the values in a single read operation. You can get very high quality 10 and 12 bit multi-channel converters quite cheaply these days. With the servo throttle in particular, I'd want the main and redundant sensors to be on separate A-D converters anyway.
Don't get me wrong, I still think that you're going to die as a result of this, and probably take a lot of other people out with you. But you'll definitely get mentioned in EE Times and The SAE International Journal.
Nothing I could build would be road legal anyway
There's a fairly comprehensive list of injector flowrates in the third post here: https://www.miataturbo.net/showthread.php?t=4288
You'll get slightly different values from different sources, which probably reflects the use of different fuel pressures.
Also, in a pulsed environment, you have to take into account the lag time on both opening and closing. In general, we tend to figure that for a high-impedance injector of the type we tend commonly to encounter, the actual flow time tends to be around 1 to 1.25ms less than the commanded on time, give or take.
While there are many variations in physical construction, there are only two broad classes of fuel injector, from an electrical standpoint anyway: high impedance and low impedance, or hi-z and lo-z. (Well, there's a third style used in direct-injection applications, but they're not relevant to us.)
The vast majority of all fuel injectors in the world today are high impedance, including all injectors found as OEM on '89-'05 MX5s. These were the first to be invented, and are still the most common in the sum of extant passenger cars today.
High impedance injectors typically have a winding resistance in the general neighborhood of 10-14 ohms. Assuming a supply voltage of 12 volts, you'll have around 1 amp of current passing through each on, give or take. It's not necessary to regulate the supply voltage, you just put battery on one end and provide a switched closure to ground on the other. You can run them at 100% duty cycle and they will be just fine. Hi-z injectors are commonly referred to as saturated injectors, as their coils are designed to run in full saturation all the time.
You'll get slightly different values from different sources, which probably reflects the use of different fuel pressures.
Also, in a pulsed environment, you have to take into account the lag time on both opening and closing. In general, we tend to figure that for a high-impedance injector of the type we tend commonly to encounter, the actual flow time tends to be around 1 to 1.25ms less than the commanded on time, give or take.
While there are many variations in physical construction, there are only two broad classes of fuel injector, from an electrical standpoint anyway: high impedance and low impedance, or hi-z and lo-z. (Well, there's a third style used in direct-injection applications, but they're not relevant to us.)
The vast majority of all fuel injectors in the world today are high impedance, including all injectors found as OEM on '89-'05 MX5s. These were the first to be invented, and are still the most common in the sum of extant passenger cars today.
High impedance injectors typically have a winding resistance in the general neighborhood of 10-14 ohms. Assuming a supply voltage of 12 volts, you'll have around 1 amp of current passing through each on, give or take. It's not necessary to regulate the supply voltage, you just put battery on one end and provide a switched closure to ground on the other. You can run them at 100% duty cycle and they will be just fine. Hi-z injectors are commonly referred to as saturated injectors, as their coils are designed to run in full saturation all the time.
Low impedance injectors are rapidly gaining in popularity. They are often found in high-performance applications using very large flowrates, and are starting to show up as OEM equipment on more and more cars, as they also have a beneficial effect on emissions at idle and very low duty cycle.
In a lo-z injector, the coil winding is generally in the 2-3 ohm neighborhood. Obviously this necessitates a more complex control scheme than simple on or off, or they'd just melt down. Also known as "peak and hold" injectors, you drive these by applying full voltage for the first millisecond or two, and then throttling them back to a much lower holding voltage. The idea is to slam the pintle open as quickly as possible by throwing gobs of current at it, and then, once it's open, restricting the current to only the level necessary to prevent it from closing without burning up the coil.
This can be accomplished in one of two ways. You can either PWM it, or you can use a linear analog circuit to control it. PWM is the method implemented in the MS, and while it's easy to do and very efficient, it has the disadvantage of generating a lot of electrical noise. The National LM1949 is an example of a chip designed for linear control, as used by JBPerf in their Peak & Hold driver board. While going this route raises the parts count and also generates a lot more heat, it doesn't produce a lot of noise and offloads the burden of worrying about injector control from the CPU. It's a closed-loop device using a current-sense resistor, so you just give it a simple on/off pulse and it figures out the rest.
As a result of all this, lo-z injectors have much shorter lag times than hi-z injectors, meaning you have much better control over fuel delivery at very low duty cycles, such as when trying to idle a 1.8 engine on 750cc injectors. (Don't laugh, some folks here are doing precisely this.)
Whatever you do, don't get taken in by the old trick of just putting 10 ohm resistors in series with lo-z injectors and then driving them with a conventional on/off signal. This is stupid, bad, wasteful, expensive, inefficient, and defeats the whole point of having bought lo-z injectors in the first place.
In a lo-z injector, the coil winding is generally in the 2-3 ohm neighborhood. Obviously this necessitates a more complex control scheme than simple on or off, or they'd just melt down. Also known as "peak and hold" injectors, you drive these by applying full voltage for the first millisecond or two, and then throttling them back to a much lower holding voltage. The idea is to slam the pintle open as quickly as possible by throwing gobs of current at it, and then, once it's open, restricting the current to only the level necessary to prevent it from closing without burning up the coil.
This can be accomplished in one of two ways. You can either PWM it, or you can use a linear analog circuit to control it. PWM is the method implemented in the MS, and while it's easy to do and very efficient, it has the disadvantage of generating a lot of electrical noise. The National LM1949 is an example of a chip designed for linear control, as used by JBPerf in their Peak & Hold driver board. While going this route raises the parts count and also generates a lot more heat, it doesn't produce a lot of noise and offloads the burden of worrying about injector control from the CPU. It's a closed-loop device using a current-sense resistor, so you just give it a simple on/off pulse and it figures out the rest.
As a result of all this, lo-z injectors have much shorter lag times than hi-z injectors, meaning you have much better control over fuel delivery at very low duty cycles, such as when trying to idle a 1.8 engine on 750cc injectors. (Don't laugh, some folks here are doing precisely this.)
Whatever you do, don't get taken in by the old trick of just putting 10 ohm resistors in series with lo-z injectors and then driving them with a conventional on/off signal. This is stupid, bad, wasteful, expensive, inefficient, and defeats the whole point of having bought lo-z injectors in the first place.
I'm torn here...
Traditionally, we start the ignition timer on the leading edge of CKP (75° BTDC) on the principle that this is the shortest amount of time that we can get between the trigger event and the actual ignition event. Less uncertainty.
OTOH, as Jason and others have pointed out, crankshaft angular velocity is not a constant. The crankshaft accelerates during the first quarter revolution after a combustion event has been initiated, but then it starts to decelerate during the second quarter rev as the aforementioned combustion event concludes whilst another cylinder is simultaneously approaching the latter half of its compression cycle and increasingly opposing the movement of the crank.
Would taking a sample at 5° BTDC be better? It means that you're free-running for longer on the timer, but will the crankshaft's velocity when at 5° be closer to its velocity at the time of the ignition event than it would be at 75°?
Is there a physicist in the house?
But... I'm stuck with what Mazda gave me, and with timing derived from both the rising and falling edge of the CKP, that should give enough accuracy for me... I'm not looking to do anything weird I just want to build an ECU with an Arduino
#63
Ok, I ran my code on a real Arduino today, and the Ignition timing pulse is never more than 0.5 degree late (and never early, as I'm erring on the side of late is better than early at low revs) at 1000RPM and never more than 1.5 degrees early at 8000RPM (with the timing advanced by 36 degrees as per the stock timing table. At 5500 RPM I'm never more than 0.1 of a degree early.
As this is alpha code, I think I can probably optimise it to get tighter timings. What degree of error do we expect/tolerate with respect to timing?
-Edit- Just a quick note to say good call on getting timing from the rising and falling edges of the CKP, the 8000RPM timing would be horrific if I only used the falling edge!
Also, Even though I don't have any injector time calculation in the code yet, the basic structure is built and only use about 4.5K of the AVR's 32K flash... plenty of space for some nice big lookup tables
As this is alpha code, I think I can probably optimise it to get tighter timings. What degree of error do we expect/tolerate with respect to timing?
-Edit- Just a quick note to say good call on getting timing from the rising and falling edges of the CKP, the 8000RPM timing would be horrific if I only used the falling edge!
Also, Even though I don't have any injector time calculation in the code yet, the basic structure is built and only use about 4.5K of the AVR's 32K flash... plenty of space for some nice big lookup tables
Last edited by bloodline; 09-26-2010 at 07:08 AM.
#65
I am happy to share the code and my mathematical models with anyone who wants to try this too, hopefully someone with experience interfacing with Real engine sensors and components.
#66
You should be better than 0.5* accuracy.
When you test the code, also test it with the crank simulator revving up and down at circa 5000 RPM per second squared.
If you need the uC to look away for a bit of time, the best time to do it is right after a spark event, which makes it as far as possible from the next one.
But you are starting to see why a Propeller might be better. One cog would be dedicated to crank angle prediction, and spark. A Prop would need external ADC's tho.
As for reading MAP, I can tell you for certain that you will need at least 50 ms updates, better yet, 25 ms. Any slower and you will have hesitation at low RPMs and at low throttle openings. And, if you only read it once and act on it (i.e. no software filtering), you MUST have analog filtering on it. Software filtering requires that you oversample. And the software filtering needs to be "Bessel" like - (decaying weighting) - simple running averages are not optimal, will suffer from aliasing artifacts and poor performance, and you can't just sample based on crank position (e.g. every 2 revs), because the sampling rate will change 10x from 700 to 7000 RPM. Far from optimal.
The same will be true of all the other sensors. Re: TPS, IMO it *is* important to sample it quickly in order to do off-idle tip-in enrichment properly.
When you test the code, also test it with the crank simulator revving up and down at circa 5000 RPM per second squared.
If you need the uC to look away for a bit of time, the best time to do it is right after a spark event, which makes it as far as possible from the next one.
But you are starting to see why a Propeller might be better. One cog would be dedicated to crank angle prediction, and spark. A Prop would need external ADC's tho.
As for reading MAP, I can tell you for certain that you will need at least 50 ms updates, better yet, 25 ms. Any slower and you will have hesitation at low RPMs and at low throttle openings. And, if you only read it once and act on it (i.e. no software filtering), you MUST have analog filtering on it. Software filtering requires that you oversample. And the software filtering needs to be "Bessel" like - (decaying weighting) - simple running averages are not optimal, will suffer from aliasing artifacts and poor performance, and you can't just sample based on crank position (e.g. every 2 revs), because the sampling rate will change 10x from 700 to 7000 RPM. Far from optimal.
The same will be true of all the other sensors. Re: TPS, IMO it *is* important to sample it quickly in order to do off-idle tip-in enrichment properly.
#67
Re: MAP filtering, look at my post re: my AEM.
The AEM has a software filter, but still, noise got through, until I added analog filtering:
https://www.miataturbo.net/showthrea...ghlight=filter
The AEM has a software filter, but still, noise got through, until I added analog filtering:
https://www.miataturbo.net/showthrea...ghlight=filter
#68
Oh, I was feeling quite good about my design until now
I can see loads of room for improvement, so I will have another go... my Interrupt handler was too complex etc, trying to do too much.
Would I be able to parasite the real signals from a running engine? Any examples how to do that?
After Joe said that the close point of the injector should be at a set point in the induction stroke, I realised that such a scheme would ensure the largest possible gap between the spark and the injectors, thus leaving room for and other housekeeping tasks.
As a side note, I changed the Arduino's ADC clock to 1Mhz, which drops the acquisition time down to 21microseconds, which is a very nice sample rate of 56Khz Unfortunately this probably won't be compatible with an future boards.
The Arduino developers are already talking about an ARM based Arduino... that would be Vastly more powerful, and would make this whole process MUCH easier.
I can see loads of room for improvement, so I will have another go... my Interrupt handler was too complex etc, trying to do too much.
When you test the code, also test it with the crank simulator revving up and down at circa 5000 RPM per second squared.
If you need the uC to look away for a bit of time, the best time to do it is right after a spark event.
As a side note, I changed the Arduino's ADC clock to 1Mhz, which drops the acquisition time down to 21microseconds, which is a very nice sample rate of 56Khz Unfortunately this probably won't be compatible with an future boards.
But you are starting to see why a Propeller might be better. One cog would be dedicated to crank angle prediction, and spark. A prop would need external ADC's tho.
#69
I have been reading this page for some extra info:
http://www.diy-efi.org/efi332/equations/algorith.htm
http://www.diy-efi.org/efi332/equations/algorith.htm
#70
After running a few tests, and reading the EFI site, I decided to refactor my code and move to a deterministic polling based system to replace my interrupt based code... Turns out this is a much much better idea for running an engine with limited CPU resources an unfortunate side effect is that my code is now far less generic and is Miata NA specific, I was hoping to make it so the timer code would be a "plug in" module and could be easily reused for any other engine.
I want
I want
#74
Quite serious, I assure you.
Hmmm, it's not too much of a stretch to have a different injection algorithm at higher revs if need be. I'm my current cycle, there is plenty of time for injectors.
Once I've got this code cleaned up, I will post it here, would that be of use to anyone?
It's entirely possible (given inadequate injector sizing for the application) for the injectors to be on 100% of the time at very high RPM and load. Or 99%, or 98%....
Once I've got this code cleaned up, I will post it here, would that be of use to anyone?
#76
Elite Member
iTrader: (1)
Join Date: Jun 2006
Location: Warrington/Birmingham
Posts: 2,642
Total Cats: 42
Quite serious, I assure you.
Hmmm, it's not too much of a stretch to have a different injection algorithm at higher revs if need be. I'm my current cycle, there is plenty of time for injectors.
Once I've got this code cleaned up, I will post it here, would that be of use to anyone?
Hmmm, it's not too much of a stretch to have a different injection algorithm at higher revs if need be. I'm my current cycle, there is plenty of time for injectors.
Once I've got this code cleaned up, I will post it here, would that be of use to anyone?
That seems like an overly complex way of going about it.
#77
Possibly, but this is my first time developing ECU software... so I expect I have made lots of mistakes
But I have found a few solutions that have bought me more CPU time... Notably, I decided to convert all degrees to 4096ths of a circle... that has allowed me to removed all division operations from my code, and replace them with shifts... I also found a way to speed up Analog conversions on the AVR and found that interrupts aren't actually a great idea in this context. Not to mention all the knowledge and advice provided by the forum members here!
So all in all it has been been a very successful project so far
But I have found a few solutions that have bought me more CPU time... Notably, I decided to convert all degrees to 4096ths of a circle... that has allowed me to removed all division operations from my code, and replace them with shifts... I also found a way to speed up Analog conversions on the AVR and found that interrupts aren't actually a great idea in this context. Not to mention all the knowledge and advice provided by the forum members here!
So all in all it has been been a very successful project so far
#79
I considered using a 32bit value of 33554432, total over kill I know, but then 1 degree is 91.0 divisions, which is quite a nice value... But 32bit math on an 8bit chip is unpleasant :(
If 4096 proves unworkable in a real situation, then I will try 1024 no doubt I will need to refine the design quite a bit as I test it.