1 /* FreeEMS - the open source engine management system
3 * Copyright 2008, 2009 Fred Cooke
5 * This file is part of the FreeEMS project.
7 * FreeEMS software is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * FreeEMS software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with any FreeEMS software. If not, see http://www.gnu.org/licenses/
20 * We ask that if you make any changes to this file you email them upstream to
21 * us at admin(at)diyefi(dot)org or, even better, fork the code on github.com!
23 * Thank you for choosing FreeEMS to run your engine!
27 /** @file fuelAndIgnitionCalcs.c
28 * @ingroup measurementsAndCalculations
30 * @brief Fuel and ignition calculations.
32 * This file contains all of the main fuel and ignition calculations based
33 * upon the variables that we have already determined in previous stages.
39 #define FUELANDIGNITIONCALCS_C
40 #include "inc/freeEMS.h"
41 #include "inc/commsCore.h"
42 #include "inc/tableLookup.h"
43 #include "inc/fuelAndIgnitionCalcs.h"
46 /** @brief Fuel and ignition calculations
48 * Using a variety of primary algorithms calculate a base pulsewidth and then
49 * apply various corrections to it such as injector dead time, transient fuel
50 * correction, engine temperature enrichment and per cylinder trims. The fuel
51 * injection timing is also determined here.
53 * Calculate the ignition timing and dwell here too. Several corrections are
54 * applied to these as well.
56 * @todo TODO implement the all of the ignition stuff and finish off all of the fuel injection stuff.
57 * @todo TODO change the way configuration is done and make sure the most common options are after the first if().
58 * @todo TODO add actual configuration options to the fixed config blocks for these items.
62 void calculateFuelAndIgnition(){
63 /*&&&&&&&&&&&&& Perform the basic calculations one step at a time to get a final pulsewidth &&&&&&&&&&&&*/
65 if(TRUE
/* Genuine method */){
66 unsigned short airInletTemp
= CoreVars
->IAT
; /* All except MAF use this. */
67 /* Determine the type of air flow data */
68 if(TRUE
/* SpeedDensity */){
69 /* This won't overflow until 512kPa or about 60psi of boost with 128% VE. */
70 DerivedVars
->AirFlow
= ((unsigned long)CoreVars
->MAP
* DerivedVars
->VEMain
) / oneHundredPercentVE
;
71 /* Result is 450 - 65535 always. */
72 }else if(FALSE
/*AlphaN*/){
73 DerivedVars
->AirFlow
= DerivedVars
->VEMain
; /* Not actually VE, but rather tuned air flow without density information */
74 }else if(FALSE
/*MAF*/){
75 DerivedVars
->AirFlow
= CoreVars
->MAF
; /* Just fix temperature at appropriate level to provide correct Lambda */
76 /// @todo TODO figure out what the correct "temperature" is to make MAF work correctly!
77 airInletTemp
= roomTemperature
; // 293.15k is 20c * 100 to get value, so divide by 100 to get real number
78 }else if(FALSE
/*FixedAF*/){ /* Fixed air flow from config */
79 DerivedVars
->AirFlow
= fixedConfigs2
.sensorPresets
.presetAF
;
80 }else{ /* Default to no fuel delivery and error */
81 DerivedVars
->AirFlow
= 0;
82 /* If anyone is listening, let them know something is wrong */
83 // sendError(AIRFLOW_NOT_CONFIGURED_CODE); // or maybe queue it?
87 /* This won't overflow until well past 125C inlet, 1.5 Lambda and fuel as dense as water */
88 DerivedVars
->densityAndFuel
= (((unsigned long)((unsigned long)airInletTemp
* DerivedVars
->Lambda
) / stoichiometricLambda
) * fixedConfigs1
.engineSettings
.densityOfFuelAtSTP
) / densityOfFuelTotalDivisor
;
89 /* Result is 7500 - 60000 always. */
91 /* Divisors for air inlet temp and pressure :
92 * #define airInletTempDivisor 100
93 * #define airPressureDivisor 100
94 * cancel each other out! all others are used. */
97 DerivedVars
->BasePW
= (bootFuelConst
* DerivedVars
->AirFlow
) / DerivedVars
->densityAndFuel
;
98 }else if(FALSE
/*configured*/){ /* Fixed PW from config */
99 DerivedVars
->BasePW
= fixedConfigs2
.sensorPresets
.presetBPW
;
100 }else{ /* Default to no fuel delivery and error */
101 DerivedVars
->BasePW
= 0;
102 /* If anyone is listening, let them know something is wrong */
103 // sendError(BPW_NOT_CONFIGURED_CODE); // or maybe queue it?
106 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
111 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&& Apply All Corrections PCFC, ETE, IDT, TFC etc &&&&&&&&&&&&&&&&&&&&&&&&&&&*/
113 /* Apply the corrections after calculating */
114 DerivedVars
->FinalPW
= DerivedVars
->BasePW
;
115 DerivedVars
->FinalPW
+= DerivedVars
->TFCTotal
; /** @todo TODO check for overflow when TFC is positive and underflow when negative */
116 DerivedVars
->FinalPW
+= DerivedVars
->ETE
; /** @todo TODO check for overflow of ETE always */
119 unsigned char channel
; // the declaration of this variable is used in multiple loops below.
120 #define oneHundredPercentPCFT 32768 /** @todo TODO move oneHundredPercentPCFT to a header with all other #defines found in code */
122 /* "Calculate" the individual fuel pulse widths */
123 for(channel
= 0; channel
< INJECTION_CHANNELS
; channel
++){ /// @todo TODO make injector channels come from config, not defines.
124 /* Add or subtract the per cylinder fuel trims */
125 unsigned short trimmedPW
;
126 trimmedPW
= ((unsigned long)DerivedVars
->FinalPW
* TablesB
.SmallTablesB
.perCylinderFuelTrims
[channel
]) / oneHundredPercentPCFT
;
128 /* Check for overflow */
129 unsigned short absoluteLastPW
;
130 /* If the trim is greater than 100% then the trimmedPW MUST be larger */
131 /* If it's less than 100% it can't have overflowed */ /* If it's not larger, it overflowed */
132 if((TablesB
.SmallTablesB
.perCylinderFuelTrims
[channel
] > oneHundredPercentPCFT
) && (DerivedVars
->FinalPW
> trimmedPW
)){
133 absoluteLastPW
= SHORTMAX
; /* So max it out! */
135 /* Add on the IDT and check again */
136 unsigned short withIDTPW
= trimmedPW
+ DerivedVars
->IDT
;
137 if(trimmedPW
> withIDTPW
){ /* If it's not larger, it overflowed */
138 absoluteLastPW
= SHORTMAX
; /* So max it out! */
140 absoluteLastPW
= withIDTPW
;
144 /* Load the final value with trim and opening time checked for overflow into the array */
145 injectorMainPulseWidthsMath
[channel
] = absoluteLastPW
;
148 /* Reference PW for comparisons etc */
149 unsigned short refPW
= DerivedVars
->FinalPW
+ DerivedVars
->IDT
;
150 if(DerivedVars
->FinalPW
> refPW
){ /* If it's not larger, it overflowed */
151 refPW
= SHORTMAX
; /* So max it out! */
153 DerivedVars
->RefPW
= refPW
;
154 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
159 /*&&&&&&&&&&&&&&&&& Based on IDT schedule PW start such that Fuel is correctly timed &&&&&&&&&&&&&&&&&&&*/
161 for(channel
= 0;channel
< INJECTION_CHANNELS
;channel
++){ /// @todo TODO make injector channels come from config, not defines.
162 //injectorMainAdvances[channel] = IDT blah blah.
165 /* This will involve using RPM, injector firing angle and IDT to schedule the events correctly */
167 /** @todo TODO work needs to be done on scheduling before this can be completed. */
169 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
174 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& Calculate Dwell and Ignition angle &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
175 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
180 /*&&&&&&&&&&&&&&& Based on Dwell and Ignition angle schedule the start and end of dwell &&&&&&&&&&&&&&&&*/
181 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
186 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& TEMPORARY (and old) &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
188 /* "Calculate" the nominal total pulse width before per channel corrections */
189 masterPulseWidth
= (ADCArrays
->EGO
<< 6) + (ADCArrays
->MAP
>> 4);
191 /* "Calculate" the individual fuel pulse widths */
192 for(channel
= 0; channel
< INJECTION_CHANNELS
; channel
++){
193 injectorMainPulseWidthsMath
[channel
] = masterPulseWidth
;
196 /// @todo TODO x 6 main pulsewidths, x 6 staged pulsewidths, x 6 flags for staged channels if(coreSettingsA & STAGED_ON){}
198 /* Set the staged status on or off (for now based on changeable settings) */
199 if(fixedConfigs1
.coreSettingsA
& STAGED_ON
){
200 coreStatusA
|= STAGED_REQUIRED
;
201 /// @todo TODO determine the requirement for staged based on some sort of map and or complex load based configuration.
203 coreStatusA
&= STAGED_NOT_REQUIRED
;
206 // temporary ign tests
207 unsigned short intendedAdvance
= ADCArrays
->MAT
<< 6;
208 unsigned short intendedDwell
= intendedAdvance
>> 1;
211 for(c
=0;c
<IGNITION_CHANNELS
;c
++){
212 ignitionAdvances
[IGNITION_CHANNELS
] = intendedAdvance
;
214 *currentDwellMath
= intendedDwell
;
216 // unsigned short minPeriod = ignitionMinimumDwell << 1;
217 // if(intendedDwell < ignitionMinimumDwell){
218 // dwellLength = ignitionMinimumDwell;
220 // dwellLength = intendedDwell;
222 // if(intendedPeriod < minPeriod){
223 // dwellPeriod = minPeriod;
225 // dwellPeriod = intendedPeriod;
227 // PITLD0 = dwellPeriod;
229 /** @todo TODO Calculate the fuel advances (six of) */
230 // just use one for all for now...
231 totalAngleAfterReferenceInjection
= (ADCArrays
->TPS
<< 6);
233 /** @todo TODO Calculate the dwell period (one of) */
235 /** @todo TODO Calculate the ignition advances (twelve of) */
237 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& TEMPORARY END &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/