2015-05-28 16:49:44 -07:00
/*
Speeduino - Simple engine management for the Arduino Mega 2560 platform
Copyright ( C ) Josh Stewart
A full copy of the license may be found in the projects root directory
*/
2021-06-21 22:30:52 -07:00
/** @file
Corrections to injection pulsewidth .
2015-01-27 15:01:12 -08:00
The corrections functions in this file affect the fuel pulsewidth ( Either increasing or decreasing )
based on factors other than the VE lookup .
2021-06-21 22:30:52 -07:00
These factors include :
- Temperature ( Warmup Enrichment and After Start Enrichment )
2022-04-10 17:49:58 -07:00
- Acceleration / Deceleration
2021-06-21 22:30:52 -07:00
- Flood clear mode
- etc .
Most correction functions return value 100 ( like 100 % = = 1 ) for no need for correction .
There are 2 top level functions that call more detailed corrections for Fuel and Ignition respectively :
- @ ref correctionsFuel ( ) - All fuel related corrections
- @ ref correctionsIgn ( ) - All ignition related corrections
2015-01-27 15:01:12 -08:00
*/
//************************************************************************************************************
2014-01-07 00:02:00 -08:00
2015-02-14 09:04:00 -08:00
# include "globals.h"
2018-08-15 00:44:30 -07:00
# include "corrections.h"
2020-02-22 14:54:13 -08:00
# include "speeduino.h"
2018-06-05 23:51:28 -07:00
# include "timers.h"
2018-07-19 00:33:40 -07:00
# include "maths.h"
2018-08-13 19:47:15 -07:00
# include "sensors.h"
2018-07-19 00:33:40 -07:00
# include "src/PID_v1/PID_v1.h"
2014-01-07 00:02:00 -08:00
2016-04-08 06:52:32 -07:00
long PID_O2 , PID_output , PID_AFRTarget ;
2021-06-21 22:30:52 -07:00
/** Instance of the PID object in case that algorithm is used (Always instantiated).
* Needs to be global as it maintains state outside of each function call .
* Comes from Arduino ( ? ) PID library .
*/
PID egoPID ( & PID_O2 , & PID_output , & PID_AFRTarget , configPage6 . egoKP , configPage6 . egoKI , configPage6 . egoKD , REVERSE ) ;
2016-04-08 06:52:32 -07:00
2021-01-22 20:28:20 -08:00
byte activateMAPDOT ; //The mapDOT value seen when the MAE was activated.
byte activateTPSDOT ; //The tpsDOT value seen when the MAE was activated.
2022-04-10 20:38:23 -07:00
bool idleAdvActive = false ;
2020-01-23 19:18:57 -08:00
uint16_t AFRnextCycle ;
unsigned long knockStartTime ;
byte lastKnockCount ;
int16_t knockWindowMin ; //The current minimum crank angle for a knock pulse to be valid
int16_t knockWindowMax ; //The current maximum crank angle for a knock pulse to be valid
2022-04-04 14:27:54 -07:00
uint8_t aseTaper ;
2023-10-24 16:09:57 -07:00
uint8_t dfcoDelay ;
2022-04-04 14:27:54 -07:00
uint8_t idleAdvTaper ;
uint8_t crankingEnrichTaper ;
2023-10-24 16:09:57 -07:00
uint8_t dfcoTaper ;
2020-01-23 19:18:57 -08:00
2022-04-10 17:49:58 -07:00
/** Initialise instances and vars related to corrections (at ECU boot-up).
2021-06-21 22:30:52 -07:00
*/
2022-11-05 15:43:29 -07:00
void initialiseCorrections ( void )
2016-04-08 06:52:32 -07:00
{
egoPID . SetMode ( AUTOMATIC ) ; //Turn O2 PID on
2016-12-26 01:53:37 -08:00
currentStatus . flexIgnCorrection = 0 ;
2017-11-05 01:56:04 -07:00
currentStatus . egoCorrection = 100 ; //Default value of no adjustment must be set to avoid randomness on first correction cycle after startup
2017-10-31 22:10:06 -07:00
AFRnextCycle = 0 ;
2018-08-31 00:36:09 -07:00
currentStatus . knockActive = false ;
2022-04-10 17:49:58 -07:00
currentStatus . battery10 = 125 ; //Set battery voltage to sensible value for dwell correction for "flying start" (else ignition gets spurious pulses after boot)
2016-04-08 06:52:32 -07:00
}
2021-06-21 22:30:52 -07:00
/** Dispatch calculations for all fuel related corrections.
Calls all the other corrections functions and combines their results .
2015-01-27 15:01:12 -08:00
This is the only function that should be called from anywhere outside the file
*/
2022-11-05 15:43:29 -07:00
uint16_t correctionsFuel ( void )
2022-04-10 22:32:49 -07:00
{
uint32_t sumCorrections = 100 ;
uint16_t result ; //temporary variable to store the result of each corrections function
//The values returned by each of the correction functions are multiplied together and then divided back to give a single 0-255 value.
currentStatus . wueCorrection = correctionWUE ( ) ;
if ( currentStatus . wueCorrection ! = 100 ) { sumCorrections = div100 ( sumCorrections * currentStatus . wueCorrection ) ; }
2022-06-26 19:18:17 -07:00
currentStatus . ASEValue = correctionASE ( ) ;
if ( currentStatus . ASEValue ! = 100 ) { sumCorrections = div100 ( sumCorrections * currentStatus . ASEValue ) ; }
2022-04-10 22:32:49 -07:00
result = correctionCranking ( ) ;
if ( result ! = 100 ) { sumCorrections = div100 ( sumCorrections * result ) ; }
currentStatus . AEamount = correctionAccel ( ) ;
2023-01-09 15:46:34 -08:00
if ( ( configPage2 . aeApplyMode = = AE_MODE_MULTIPLIER ) | | BIT_CHECK ( currentStatus . engine , BIT_ENGINE_DCC ) ) // multiply by the AE amount in case of multiplier AE mode or Decel
2022-04-10 22:32:49 -07:00
{
2023-01-09 15:46:34 -08:00
if ( currentStatus . AEamount ! = 100 ) { sumCorrections = div100 ( sumCorrections * currentStatus . AEamount ) ; }
2022-04-10 22:32:49 -07:00
}
result = correctionFloodClear ( ) ;
if ( result ! = 100 ) { sumCorrections = div100 ( sumCorrections * result ) ; }
currentStatus . egoCorrection = correctionAFRClosedLoop ( ) ;
if ( currentStatus . egoCorrection ! = 100 ) { sumCorrections = div100 ( sumCorrections * currentStatus . egoCorrection ) ; }
currentStatus . batCorrection = correctionBatVoltage ( ) ;
if ( configPage2 . battVCorMode = = BATTV_COR_MODE_OPENTIME )
{
inj_opentime_uS = configPage2 . injOpen * currentStatus . batCorrection ; // Apply voltage correction to injector open time.
2024-02-19 20:36:45 -08:00
//currentStatus.batCorrection = 100; // This is to ensure that the correction is not applied twice. There is no battery correction fator as we have instead changed the open time
2022-04-10 22:32:49 -07:00
}
if ( configPage2 . battVCorMode = = BATTV_COR_MODE_WHOLE )
{
if ( currentStatus . batCorrection ! = 100 ) { sumCorrections = div100 ( sumCorrections * currentStatus . batCorrection ) ; }
}
currentStatus . iatCorrection = correctionIATDensity ( ) ;
if ( currentStatus . iatCorrection ! = 100 ) { sumCorrections = div100 ( sumCorrections * currentStatus . iatCorrection ) ; }
currentStatus . baroCorrection = correctionBaro ( ) ;
if ( currentStatus . baroCorrection ! = 100 ) { sumCorrections = div100 ( sumCorrections * currentStatus . baroCorrection ) ; }
currentStatus . flexCorrection = correctionFlex ( ) ;
if ( currentStatus . flexCorrection ! = 100 ) { sumCorrections = div100 ( sumCorrections * currentStatus . flexCorrection ) ; }
currentStatus . fuelTempCorrection = correctionFuelTemp ( ) ;
if ( currentStatus . fuelTempCorrection ! = 100 ) { sumCorrections = div100 ( sumCorrections * currentStatus . fuelTempCorrection ) ; }
currentStatus . launchCorrection = correctionLaunch ( ) ;
if ( currentStatus . launchCorrection ! = 100 ) { sumCorrections = div100 ( sumCorrections * currentStatus . launchCorrection ) ; }
bitWrite ( currentStatus . status1 , BIT_STATUS1_DFCO , correctionDFCO ( ) ) ;
2023-10-24 16:09:57 -07:00
byte dfcoTaperCorrection = correctionDFCOfuel ( ) ;
if ( dfcoTaperCorrection = = 0 ) { sumCorrections = 0 ; }
else if ( dfcoTaperCorrection ! = 100 ) { sumCorrections = div100 ( sumCorrections * dfcoTaperCorrection ) ; }
2022-04-10 22:32:49 -07:00
if ( sumCorrections > 1500 ) { sumCorrections = 1500 ; } //This is the maximum allowable increase during cranking
return ( uint16_t ) sumCorrections ;
}
2021-06-21 22:30:52 -07:00
/** Warm Up Enrichment (WUE) corrections.
2015-01-27 15:01:12 -08:00
Uses a 2 D enrichment table ( WUETable ) where the X axis is engine temp and the Y axis is the amount of extra fuel to add
*/
2022-11-05 15:43:29 -07:00
byte correctionWUE ( void )
2014-01-07 00:02:00 -08:00
{
2017-06-03 03:45:25 -07:00
byte WUEValue ;
2014-05-08 06:01:36 -07:00
//Possibly reduce the frequency this runs at (Costs about 50 loops per second)
2019-10-29 04:50:46 -07:00
//if (currentStatus.coolant > (WUETable.axisX[9] - CALIBRATION_TEMPERATURE_OFFSET))
if ( currentStatus . coolant > ( table2D_getAxisValue ( & WUETable , 9 ) - CALIBRATION_TEMPERATURE_OFFSET ) )
2017-06-03 03:45:25 -07:00
{
//This prevents us doing the 2D lookup if we're already up to temp
BIT_CLEAR ( currentStatus . engine , BIT_ENGINE_WARMUP ) ;
2019-11-10 00:04:32 -08:00
WUEValue = table2D_getRawValue ( & WUETable , 9 ) ;
2017-06-03 03:45:25 -07:00
}
else
{
BIT_SET ( currentStatus . engine , BIT_ENGINE_WARMUP ) ;
WUEValue = table2D_getValue ( & WUETable , currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) ;
}
return WUEValue ;
2014-01-07 00:02:00 -08:00
}
2021-06-21 22:30:52 -07:00
/** Cranking Enrichment corrections.
2015-02-06 13:29:51 -08:00
Additional fuel % to be added when the engine is cranking
*/
2022-11-05 15:43:29 -07:00
uint16_t correctionCranking ( void )
2015-02-06 13:29:51 -08:00
{
2020-03-16 16:38:03 -07:00
uint16_t crankingValue = 100 ;
2020-05-18 22:22:43 -07:00
//Check if we are actually cranking
2017-07-27 07:48:08 -07:00
if ( BIT_CHECK ( currentStatus . engine , BIT_ENGINE_CRANK ) )
2017-08-23 00:18:59 -07:00
{
2017-07-27 07:48:08 -07:00
crankingValue = table2D_getValue ( & crankingEnrichTable , currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) ;
2020-03-16 16:38:03 -07:00
crankingValue = ( uint16_t ) crankingValue * 5 ; //multiplied by 5 to get range from 0% to 1275%
2022-04-04 14:27:54 -07:00
crankingEnrichTaper = 0 ;
2017-07-27 07:48:08 -07:00
}
2020-05-18 22:22:43 -07:00
//If we're not cranking, check if if cranking enrichment tapering to ASE should be done
2022-04-04 14:27:54 -07:00
else if ( crankingEnrichTaper < configPage10 . crankingEnrichTaper )
2020-05-18 22:22:43 -07:00
{
crankingValue = table2D_getValue ( & crankingEnrichTable , currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) ;
crankingValue = ( uint16_t ) crankingValue * 5 ; //multiplied by 5 to get range from 0% to 1275%
//Taper start value needs to account for ASE that is now running, so total correction does not increase when taper begins
unsigned long taperStart = ( unsigned long ) crankingValue * 100 / currentStatus . ASEValue ;
2022-04-04 14:27:54 -07:00
crankingValue = ( uint16_t ) map ( crankingEnrichTaper , 0 , configPage10 . crankingEnrichTaper , taperStart , 100 ) ; //Taper from start value to 100%
2020-05-18 22:22:43 -07:00
if ( crankingValue < 100 ) { crankingValue = 100 ; } //Sanity check
2022-06-19 22:03:31 -07:00
if ( BIT_CHECK ( LOOP_TIMER , BIT_TIMER_10HZ ) ) { crankingEnrichTaper + + ; }
2020-05-18 22:22:43 -07:00
}
2017-06-03 03:45:25 -07:00
return crankingValue ;
2015-02-06 13:29:51 -08:00
}
2022-06-26 17:39:14 -07:00
/** After Start Enrichment calculation.
2020-01-23 19:18:57 -08:00
* This is a short period ( Usually < 20 seconds ) immediately after the engine first fires ( But not when cranking )
2021-06-21 22:30:52 -07:00
* where an additional amount of fuel is added ( Over and above the WUE amount ) .
2020-01-23 19:18:57 -08:00
*
* @ return uint8_t The After Start Enrichment modifier as a % . 100 % = No modification .
2022-06-19 22:03:31 -07:00
*/
2022-11-05 15:43:29 -07:00
byte correctionASE ( void )
2014-01-07 00:02:00 -08:00
{
2022-06-26 19:18:17 -07:00
int16_t ASEValue = currentStatus . ASEValue ;
2022-04-10 17:49:58 -07:00
//Two checks are required:
2019-10-31 20:25:51 -07:00
//1) Is the engine run time less than the configured ase time
2015-02-06 13:29:51 -08:00
//2) Make sure we're not still cranking
2022-06-19 22:03:31 -07:00
if ( BIT_CHECK ( currentStatus . engine , BIT_ENGINE_CRANK ) ! = true )
2014-01-29 21:28:21 -08:00
{
2022-06-26 19:18:17 -07:00
if ( BIT_CHECK ( LOOP_TIMER , BIT_TIMER_10HZ ) | | ( currentStatus . ASEValue = = 0 ) )
2020-03-23 18:36:24 -07:00
{
2022-06-19 22:03:31 -07:00
if ( ( currentStatus . runSecs < ( table2D_getValue ( & ASECountTable , currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) ) ) & & ! ( BIT_CHECK ( currentStatus . engine , BIT_ENGINE_CRANK ) ) )
2020-03-23 18:36:24 -07:00
{
BIT_SET ( currentStatus . engine , BIT_ENGINE_ASE ) ; //Mark ASE as active.
2022-06-19 22:03:31 -07:00
ASEValue = 100 + table2D_getValue ( & ASETable , currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) ;
aseTaper = 0 ;
2020-03-23 18:36:24 -07:00
}
else
{
2022-06-19 22:03:31 -07:00
if ( aseTaper < configPage2 . aseTaperTime ) //Check if we've reached the end of the taper time
{
BIT_SET ( currentStatus . engine , BIT_ENGINE_ASE ) ; //Mark ASE as active.
ASEValue = 100 + map ( aseTaper , 0 , configPage2 . aseTaperTime , table2D_getValue ( & ASETable , currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) , 0 ) ;
aseTaper + + ;
}
else
{
BIT_CLEAR ( currentStatus . engine , BIT_ENGINE_ASE ) ; //Mark ASE as inactive.
ASEValue = 100 ;
}
2020-03-23 18:36:24 -07:00
}
2022-06-19 22:03:31 -07:00
//Safety checks
if ( ASEValue > 255 ) { ASEValue = 255 ; }
if ( ASEValue < 0 ) { ASEValue = 0 ; }
2022-06-26 19:18:17 -07:00
ASEValue = ( byte ) ASEValue ;
2020-03-23 18:36:24 -07:00
}
2022-06-19 22:03:31 -07:00
}
else
{
2022-06-26 19:18:17 -07:00
//Engine is cranking, ASE disabled
2022-06-19 22:03:31 -07:00
BIT_CLEAR ( currentStatus . engine , BIT_ENGINE_ASE ) ; //Mark ASE as inactive.
ASEValue = 100 ;
2014-01-29 21:28:21 -08:00
}
2022-06-26 19:18:17 -07:00
return ASEValue ;
2014-01-07 00:02:00 -08:00
}
2021-06-21 22:30:52 -07:00
/** Acceleration enrichment correction calculation.
2019-05-30 01:02:18 -07:00
*
* Calculates the % change of the throttle over time ( % / second ) and performs a lookup based on this
2020-04-18 15:21:52 -07:00
* Coolant - based modifier is applied on the top of this .
2019-05-30 01:02:18 -07:00
* When the enrichment is turned on , it runs at that amount for a fixed period of time ( taeTime )
*
2021-06-21 22:30:52 -07:00
* @ return uint16_t The Acceleration enrichment modifier as a % . 100 % = No modification .
*
2020-04-18 15:21:52 -07:00
* As the maximum enrichment amount is + 255 % and maximum cold adjustment for this is 255 % , the overall return value
2021-06-21 22:30:52 -07:00
* from this function can be 100 + ( 255 * 255 / 100 ) = 750. Hence this function returns a uint16_t rather than byte .
2019-05-30 01:02:18 -07:00
*/
2022-11-05 15:43:29 -07:00
uint16_t correctionAccel ( void )
2014-01-07 00:02:00 -08:00
{
2017-11-08 03:51:40 -08:00
int16_t accelValue = 100 ;
2021-01-21 21:31:14 -08:00
int16_t MAP_change = 0 ;
2022-04-15 19:51:54 -07:00
int16_t TPS_change = 0 ;
2021-01-21 21:31:14 -08:00
if ( configPage2 . aeMode = = AE_MODE_MAP )
{
//Get the MAP rate change
MAP_change = ( currentStatus . MAP - MAPlast ) ;
2023-11-05 14:10:08 -08:00
currentStatus . mapDOT = ( MICROS_PER_SEC / ( MAP_time - MAPlast_time ) ) * MAP_change ; //This is the % per second that the MAP has moved
2022-12-13 16:17:47 -08:00
//currentStatus.mapDOT = 15 * MAP_change; //This is the kpa per second that the MAP has moved
2021-01-21 21:31:14 -08:00
}
else if ( configPage2 . aeMode = = AE_MODE_TPS )
{
//Get the TPS rate change
2022-05-24 18:02:55 -07:00
TPS_change = ( currentStatus . TPS - currentStatus . TPSlast ) ;
2023-11-05 14:10:08 -08:00
//currentStatus.tpsDOT = ldiv(MICROS_PER_SEC, (TPS_time - TPSlast_time)).quot * TPS_change; //This is the % per second that the TPS has moved
2022-12-13 16:17:47 -08:00
currentStatus . tpsDOT = ( TPS_READ_FREQUENCY * TPS_change ) / 2 ; //This is the % per second that the TPS has moved, adjusted for the 0.5% resolution of the TPS
2021-01-21 21:31:14 -08:00
}
2015-01-27 15:01:12 -08:00
//First, check whether the accel. enrichment is already running
2022-12-13 16:17:47 -08:00
if ( BIT_CHECK ( currentStatus . engine , BIT_ENGINE_ACC ) | | BIT_CHECK ( currentStatus . engine , BIT_ENGINE_DCC ) )
2015-01-27 15:01:12 -08:00
{
//If it is currently running, check whether it should still be running or whether it's reached it's end time
2019-05-21 14:47:09 -07:00
if ( micros_safe ( ) > = currentStatus . AEEndTime )
2015-01-27 15:01:12 -08:00
{
//Time to turn enrichment off
BIT_CLEAR ( currentStatus . engine , BIT_ENGINE_ACC ) ;
2022-12-13 16:17:47 -08:00
BIT_CLEAR ( currentStatus . engine , BIT_ENGINE_DCC ) ;
2019-05-21 14:47:09 -07:00
currentStatus . AEamount = 0 ;
2017-06-03 03:45:25 -07:00
accelValue = 100 ;
2019-05-30 01:02:18 -07:00
//Reset the relevant DOT value to 0
if ( configPage2 . aeMode = = AE_MODE_MAP ) { currentStatus . mapDOT = 0 ; }
else if ( configPage2 . aeMode = = AE_MODE_TPS ) { currentStatus . tpsDOT = 0 ; }
2017-06-03 03:45:25 -07:00
}
else
{
2021-01-21 21:31:14 -08:00
//Enrichment still needs to keep running.
//Simply return the total TAE amount
2019-05-21 14:47:09 -07:00
accelValue = currentStatus . AEamount ;
2021-01-21 21:31:14 -08:00
//Need to check whether the accel amount has increased from when AE was turned on
//If the accel amount HAS increased, we clear the current enrich phase and a new one will be started below
2022-12-13 16:17:47 -08:00
if ( ( configPage2 . aeMode = = AE_MODE_MAP ) & & ( abs ( currentStatus . mapDOT ) > activateMAPDOT ) )
{
BIT_CLEAR ( currentStatus . engine , BIT_ENGINE_ACC ) ;
BIT_CLEAR ( currentStatus . engine , BIT_ENGINE_DCC ) ;
}
else if ( ( configPage2 . aeMode = = AE_MODE_TPS ) & & ( abs ( currentStatus . tpsDOT ) > activateTPSDOT ) )
{
BIT_CLEAR ( currentStatus . engine , BIT_ENGINE_ACC ) ;
BIT_CLEAR ( currentStatus . engine , BIT_ENGINE_DCC ) ;
}
2015-01-27 15:01:12 -08:00
}
}
2021-01-21 21:31:14 -08:00
2022-12-13 16:17:47 -08:00
if ( ! BIT_CHECK ( currentStatus . engine , BIT_ENGINE_ACC ) & & ! BIT_CHECK ( currentStatus . engine , BIT_ENGINE_DCC ) ) //Need to check this again as it may have been changed in the above section (Both ACC and DCC are off if this has changed)
2014-05-06 04:07:49 -07:00
{
2019-05-30 01:02:18 -07:00
if ( configPage2 . aeMode = = AE_MODE_MAP )
2017-06-03 03:45:25 -07:00
{
2022-12-13 16:17:47 -08:00
if ( abs ( MAP_change ) < = configPage2 . maeMinChange )
2017-06-03 03:45:25 -07:00
{
2019-06-04 01:27:38 -07:00
accelValue = 100 ;
currentStatus . mapDOT = 0 ;
}
else
{
//If MAE isn't currently turned on, need to check whether it needs to be turned on
2022-12-13 16:17:47 -08:00
if ( abs ( currentStatus . mapDOT ) > configPage2 . maeThresh )
2018-06-09 21:57:52 -07:00
{
2022-12-13 16:17:47 -08:00
activateMAPDOT = abs ( currentStatus . mapDOT ) ;
2019-06-04 01:27:38 -07:00
currentStatus . AEEndTime = micros_safe ( ) + ( ( unsigned long ) configPage2 . aeTime * 10000 ) ; //Set the time in the future where the enrichment will be turned off. taeTime is stored as mS / 10, so multiply it by 100 to get it in uS
2022-12-13 16:17:47 -08:00
//Check if the MAP rate of change is negative or positive. Negative means decelarion.
if ( currentStatus . mapDOT < 0 )
2018-06-09 21:57:52 -07:00
{
2022-12-13 16:17:47 -08:00
BIT_SET ( currentStatus . engine , BIT_ENGINE_DCC ) ; //Mark deceleration enleanment as active.
accelValue = configPage2 . decelAmount ; //In decel, use the decel fuel amount as accelValue
} //Deceleration
//Positive MAP rate of change is acceleration.
else
2020-04-18 15:21:52 -07:00
{
2022-12-13 16:17:47 -08:00
BIT_SET ( currentStatus . engine , BIT_ENGINE_ACC ) ; //Mark acceleration enrichment as active.
accelValue = table2D_getValue ( & maeTable , currentStatus . mapDOT / 10 ) ; //The x-axis of mae table is divided by 10 to fit values in byte.
//Apply the RPM taper to the above
//The RPM settings are stored divided by 100:
uint16_t trueTaperMin = configPage2 . aeTaperMin * 100 ;
uint16_t trueTaperMax = configPage2 . aeTaperMax * 100 ;
if ( currentStatus . RPM > trueTaperMin )
2020-04-18 15:21:52 -07:00
{
2022-12-13 16:17:47 -08:00
if ( currentStatus . RPM > trueTaperMax ) { accelValue = 0 ; } //RPM is beyond taper max limit, so accel enrich is turned off
else
{
int16_t taperRange = trueTaperMax - trueTaperMin ;
int16_t taperPercent = ( ( currentStatus . RPM - trueTaperMin ) * 100UL ) / taperRange ; //The percentage of the way through the RPM taper range
accelValue = percentage ( ( 100 - taperPercent ) , accelValue ) ; //Calculate the above percentage of the calculated accel amount.
}
2020-04-18 15:21:52 -07:00
}
2022-12-13 16:17:47 -08:00
//Apply AE cold coolant modifier, if CLT is less than taper end temperature
if ( currentStatus . coolant < ( int ) ( configPage2 . aeColdTaperMax - CALIBRATION_TEMPERATURE_OFFSET ) )
2020-04-18 15:21:52 -07:00
{
2022-12-13 16:17:47 -08:00
//If CLT is less than taper min temp, apply full modifier on top of accelValue
if ( currentStatus . coolant < = ( int ) ( configPage2 . aeColdTaperMin - CALIBRATION_TEMPERATURE_OFFSET ) )
{
uint16_t accelValue_uint = percentage ( configPage2 . aeColdPct , accelValue ) ;
accelValue = ( int16_t ) accelValue_uint ;
}
//If CLT is between taper min and max, taper the modifier value and apply it on top of accelValue
else
{
int16_t taperRange = ( int16_t ) configPage2 . aeColdTaperMax - configPage2 . aeColdTaperMin ;
int16_t taperPercent = ( int ) ( ( currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET - configPage2 . aeColdTaperMin ) * 100 ) / taperRange ;
int16_t coldPct = ( int16_t ) 100 + percentage ( ( 100 - taperPercent ) , ( configPage2 . aeColdPct - 100 ) ) ;
uint16_t accelValue_uint = ( uint16_t ) accelValue * coldPct / 100 ; //Potential overflow (if AE is large) without using uint16_t (percentage() may overflow)
accelValue = ( int16_t ) accelValue_uint ;
}
2020-04-18 15:21:52 -07:00
}
2022-12-13 16:17:47 -08:00
accelValue = 100 + accelValue ; //In case of AE, add the 100 normalisation to the calculated amount
2020-04-18 15:21:52 -07:00
}
2019-06-04 01:27:38 -07:00
} //MAE Threshold
2022-12-13 16:17:47 -08:00
} //MAP change threshold
} //AE Mode
2019-05-30 01:02:18 -07:00
else if ( configPage2 . aeMode = = AE_MODE_TPS )
{
2022-12-13 16:17:47 -08:00
//Check for only very small movement. This not only means we can skip the lookup, but helps reduce false triggering around 0-2% throttle openings
if ( abs ( TPS_change ) < = configPage2 . taeMinChange )
2019-05-30 01:02:18 -07:00
{
accelValue = 100 ;
currentStatus . tpsDOT = 0 ;
}
else
{
//If TAE isn't currently turned on, need to check whether it needs to be turned on
2022-12-13 16:17:47 -08:00
if ( abs ( currentStatus . tpsDOT ) > configPage2 . taeThresh )
2019-05-30 01:02:18 -07:00
{
2022-12-13 16:17:47 -08:00
activateTPSDOT = abs ( currentStatus . tpsDOT ) ;
2019-05-30 01:02:18 -07:00
currentStatus . AEEndTime = micros_safe ( ) + ( ( unsigned long ) configPage2 . aeTime * 10000 ) ; //Set the time in the future where the enrichment will be turned off. taeTime is stored as mS / 10, so multiply it by 100 to get it in uS
2022-12-13 16:17:47 -08:00
//Check if the TPS rate of change is negative or positive. Negative means decelarion.
if ( currentStatus . tpsDOT < 0 )
2019-05-30 01:02:18 -07:00
{
2022-12-13 16:17:47 -08:00
BIT_SET ( currentStatus . engine , BIT_ENGINE_DCC ) ; //Mark deceleration enleanment as active.
accelValue = configPage2 . decelAmount ; //In decel, use the decel fuel amount as accelValue
} //Deceleration
//Positive TPS rate of change is Acceleration.
else
2020-04-18 15:21:52 -07:00
{
2022-12-13 16:17:47 -08:00
BIT_SET ( currentStatus . engine , BIT_ENGINE_ACC ) ; //Mark acceleration enrichment as active.
accelValue = table2D_getValue ( & taeTable , currentStatus . tpsDOT / 10 ) ; //The x-axis of tae table is divided by 10 to fit values in byte.
//Apply the RPM taper to the above
//The RPM settings are stored divided by 100:
uint16_t trueTaperMin = configPage2 . aeTaperMin * 100 ;
uint16_t trueTaperMax = configPage2 . aeTaperMax * 100 ;
if ( currentStatus . RPM > trueTaperMin )
2020-04-18 15:21:52 -07:00
{
2022-12-13 16:17:47 -08:00
if ( currentStatus . RPM > trueTaperMax ) { accelValue = 0 ; } //RPM is beyond taper max limit, so accel enrich is turned off
else
{
int16_t taperRange = trueTaperMax - trueTaperMin ;
int16_t taperPercent = ( ( currentStatus . RPM - trueTaperMin ) * 100UL ) / taperRange ; //The percentage of the way through the RPM taper range
accelValue = percentage ( ( 100 - taperPercent ) , accelValue ) ; //Calculate the above percentage of the calculated accel amount.
}
2020-04-18 15:21:52 -07:00
}
2022-12-13 16:17:47 -08:00
//Apply AE cold coolant modifier, if CLT is less than taper end temperature
if ( currentStatus . coolant < ( int ) ( configPage2 . aeColdTaperMax - CALIBRATION_TEMPERATURE_OFFSET ) )
2020-04-18 15:21:52 -07:00
{
2022-12-13 16:17:47 -08:00
//If CLT is less than taper min temp, apply full modifier on top of accelValue
if ( currentStatus . coolant < = ( int ) ( configPage2 . aeColdTaperMin - CALIBRATION_TEMPERATURE_OFFSET ) )
{
uint16_t accelValue_uint = percentage ( configPage2 . aeColdPct , accelValue ) ;
accelValue = ( int16_t ) accelValue_uint ;
}
//If CLT is between taper min and max, taper the modifier value and apply it on top of accelValue
else
{
int16_t taperRange = ( int16_t ) configPage2 . aeColdTaperMax - configPage2 . aeColdTaperMin ;
int16_t taperPercent = ( int ) ( ( currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET - configPage2 . aeColdTaperMin ) * 100 ) / taperRange ;
int16_t coldPct = ( int16_t ) 100 + percentage ( ( 100 - taperPercent ) , ( configPage2 . aeColdPct - 100 ) ) ;
uint16_t accelValue_uint = ( uint16_t ) accelValue * coldPct / 100 ; //Potential overflow (if AE is large) without using uint16_t
accelValue = ( int16_t ) accelValue_uint ;
}
2020-04-18 15:21:52 -07:00
}
2022-12-13 16:17:47 -08:00
accelValue = 100 + accelValue ; //In case of AE, add the 100 normalisation to the calculated amount
} //Acceleration
2019-05-30 01:02:18 -07:00
} //TAE Threshold
2022-12-13 16:17:47 -08:00
} //TPS change threshold
2019-05-30 01:02:18 -07:00
} //AE Mode
} //AE active
2017-02-13 06:07:05 -08:00
2017-06-03 03:45:25 -07:00
return accelValue ;
2014-05-06 04:07:49 -07:00
}
2021-06-21 22:30:52 -07:00
/** Simple check to see whether we are cranking with the TPS above the flood clear threshold.
@ return 100 ( not cranking and thus no need for flood - clear ) or 0 ( Engine cranking and TPS above @ ref config4 . floodClear limit ) .
2014-05-06 04:07:49 -07:00
*/
2022-11-05 15:43:29 -07:00
byte correctionFloodClear ( void )
2014-05-06 04:07:49 -07:00
{
2017-06-03 03:45:25 -07:00
byte floodValue = 100 ;
2017-06-04 05:48:59 -07:00
if ( BIT_CHECK ( currentStatus . engine , BIT_ENGINE_CRANK ) )
2014-05-06 04:07:49 -07:00
{
//Engine is currently cranking, check what the TPS is
2018-01-23 17:05:50 -08:00
if ( currentStatus . TPS > = configPage4 . floodClear )
2014-05-06 04:07:49 -07:00
{
//Engine is cranking and TPS is above threshold. Cut all fuel
2017-06-03 03:45:25 -07:00
floodValue = 0 ;
2014-05-06 04:07:49 -07:00
}
}
2017-06-03 03:45:25 -07:00
return floodValue ;
2014-01-07 00:02:00 -08:00
}
2015-02-05 13:11:33 -08:00
2021-06-21 22:30:52 -07:00
/** Battery Voltage correction.
Uses a 2 D enrichment table ( WUETable ) where the X axis is engine temp and the Y axis is the amount of extra fuel to add .
2015-04-04 03:10:13 -07:00
*/
2022-11-05 15:43:29 -07:00
byte correctionBatVoltage ( void )
2015-04-04 03:10:13 -07:00
{
2017-06-03 03:45:25 -07:00
byte batValue = 100 ;
2020-02-06 20:57:40 -08:00
batValue = table2D_getValue ( & injectorVCorrectionTable , currentStatus . battery10 ) ;
2017-06-03 03:45:25 -07:00
return batValue ;
2015-04-04 03:10:13 -07:00
}
2021-06-21 22:30:52 -07:00
/** Simple temperature based corrections lookup based on the inlet air temperature (IAT).
This corrects for changes in air density from movement of the temperature .
2015-10-17 15:29:41 -07:00
*/
2022-11-05 15:43:29 -07:00
byte correctionIATDensity ( void )
2015-10-17 15:29:41 -07:00
{
2017-06-03 03:45:25 -07:00
byte IATValue = 100 ;
2019-10-29 04:50:46 -07:00
IATValue = table2D_getValue ( & IATDensityCorrectionTable , currentStatus . IAT + CALIBRATION_TEMPERATURE_OFFSET ) ; //currentStatus.IAT is the actual temperature, values in IATDensityCorrectionTable.axisX are temp+offset
2017-06-03 03:45:25 -07:00
return IATValue ;
2015-10-18 05:20:16 -07:00
}
2022-04-10 17:49:58 -07:00
/** Correction for current barometric / ambient pressure.
* @ returns A percentage value indicating the amount the fuelling should be changed based on the barometric reading . 100 = No change . 110 = 10 % increase . 90 = 10 % decrease
2019-11-03 15:14:11 -08:00
*/
2022-11-05 15:43:29 -07:00
byte correctionBaro ( void )
2019-11-03 15:14:11 -08:00
{
byte baroValue = 100 ;
baroValue = table2D_getValue ( & baroFuelTable , currentStatus . baro ) ;
return baroValue ;
}
2021-06-21 22:30:52 -07:00
/** Launch control has a setting to increase the fuel load to assist in bringing up boost.
2015-10-18 05:20:16 -07:00
This simple check applies the extra fuel if we ' re currently launching
*/
2022-11-05 15:43:29 -07:00
byte correctionLaunch ( void )
2015-10-18 05:20:16 -07:00
{
2017-06-03 03:45:25 -07:00
byte launchValue = 100 ;
2018-01-23 17:05:50 -08:00
if ( currentStatus . launchingHard | | currentStatus . launchingSoft ) { launchValue = ( 100 + configPage6 . lnchFuelAdd ) ; }
2017-06-03 03:45:25 -07:00
return launchValue ;
2015-10-17 15:29:41 -07:00
}
2023-10-24 16:09:57 -07:00
/**
*/
byte correctionDFCOfuel ( void )
{
byte scaleValue = 100 ;
if ( BIT_CHECK ( currentStatus . status1 , BIT_STATUS1_DFCO ) )
{
if ( ( configPage9 . dfcoTaperEnable = = 1 ) & & ( dfcoTaper ! = 0 ) )
{
//Do a check if the user reduced the duration while active to avoid overflow
if ( dfcoTaper > configPage9 . dfcoTaperTime ) { dfcoTaper = configPage9 . dfcoTaperTime ; }
scaleValue = map ( dfcoTaper , configPage9 . dfcoTaperTime , 0 , 100 , configPage9 . dfcoTaperFuel ) ;
if ( BIT_CHECK ( LOOP_TIMER , BIT_TIMER_10HZ ) ) { dfcoTaper - - ; }
}
else { scaleValue = 0 ; } //Taper ended or disabled, disable fuel
}
else { dfcoTaper = configPage9 . dfcoTaperTime ; } //Keep updating the duration until DFCO is active
return scaleValue ;
}
2016-02-24 18:11:13 -08:00
/*
2022-04-10 17:49:58 -07:00
* Returns true if deceleration fuel cutoff should be on , false if its off
2016-02-24 18:11:13 -08:00
*/
2022-11-05 15:43:29 -07:00
bool correctionDFCO ( void )
2016-02-24 18:11:13 -08:00
{
2017-06-03 03:45:25 -07:00
bool DFCOValue = false ;
2018-01-23 17:05:50 -08:00
if ( configPage2 . dfcoEnabled = = 1 )
2017-06-03 03:45:25 -07:00
{
2020-05-28 19:25:33 -07:00
if ( BIT_CHECK ( currentStatus . status1 , BIT_STATUS1_DFCO ) = = 1 )
2020-04-07 18:25:53 -07:00
{
2020-04-02 20:46:11 -07:00
DFCOValue = ( currentStatus . RPM > ( configPage4 . dfcoRPM * 10 ) ) & & ( currentStatus . TPS < configPage4 . dfcoTPSThresh ) ;
2023-10-24 16:09:57 -07:00
if ( DFCOValue = = false ) { dfcoDelay = 0 ; }
2020-04-02 20:46:11 -07:00
}
2020-04-07 18:25:53 -07:00
else
{
2023-10-05 05:02:15 -07:00
if ( ( currentStatus . TPS < configPage4 . dfcoTPSThresh ) & & ( currentStatus . coolant > = ( int ) ( configPage2 . dfcoMinCLT - CALIBRATION_TEMPERATURE_OFFSET ) ) & & ( currentStatus . RPM > ( unsigned int ) ( ( configPage4 . dfcoRPM * 10 ) + ( configPage4 . dfcoHyster * 2 ) ) ) )
2020-04-07 18:25:53 -07:00
{
2023-10-24 16:09:57 -07:00
if ( dfcoDelay < configPage2 . dfcoDelay )
2022-04-04 14:27:54 -07:00
{
2023-10-24 16:09:57 -07:00
if ( BIT_CHECK ( LOOP_TIMER , BIT_TIMER_10HZ ) ) { dfcoDelay + + ; }
2022-04-04 14:27:54 -07:00
}
else { DFCOValue = true ; }
2020-04-02 20:46:11 -07:00
}
2023-10-24 16:09:57 -07:00
else { dfcoDelay = 0 ; } //Prevent future activation right away if previous time wasn't activated
2020-04-07 18:25:53 -07:00
} // DFCO active check
} // DFCO enabled check
2017-06-03 03:45:25 -07:00
return DFCOValue ;
2016-02-24 18:11:13 -08:00
}
2021-06-21 22:30:52 -07:00
/** Flex fuel adjustment to vary fuel based on ethanol content.
2017-02-13 06:07:05 -08:00
* The amount of extra fuel required is a linear relationship based on the % of ethanol .
2016-11-28 01:37:24 -08:00
*/
2022-11-05 15:43:29 -07:00
byte correctionFlex ( void )
2016-11-28 01:37:24 -08:00
{
2017-06-03 03:45:25 -07:00
byte flexValue = 100 ;
2018-03-09 19:07:06 -08:00
2018-01-24 22:25:11 -08:00
if ( configPage2 . flexEnabled = = 1 )
2017-06-03 03:45:25 -07:00
{
2018-01-24 22:25:11 -08:00
flexValue = table2D_getValue ( & flexFuelTable , currentStatus . ethanolPct ) ;
2017-06-03 03:45:25 -07:00
}
return flexValue ;
2016-11-28 01:37:24 -08:00
}
2020-08-17 19:34:56 -07:00
/*
* Fuel temperature adjustment to vary fuel based on fuel temperature reading
*/
2022-11-05 15:43:29 -07:00
byte correctionFuelTemp ( void )
2020-08-17 19:34:56 -07:00
{
byte fuelTempValue = 100 ;
if ( configPage2 . flexEnabled = = 1 )
{
fuelTempValue = table2D_getValue ( & fuelTempTable , currentStatus . fuelTemp + CALIBRATION_TEMPERATURE_OFFSET ) ;
}
return fuelTempValue ;
}
2021-06-21 22:30:52 -07:00
/** Lookup the AFR target table and perform either a simple or PID adjustment based on this.
2015-02-05 13:11:33 -08:00
Simple ( Best suited to narrowband sensors ) :
If the O2 sensor reports that the mixture is lean / rich compared to the desired AFR target , it will make a 1 % adjustment
2021-10-20 14:03:29 -07:00
It then waits egoDelta number of ignition events and compares O2 against the target table again . If it is still lean / rich then the adjustment is increased to 2 % .
2021-06-21 22:30:52 -07:00
2015-02-05 13:11:33 -08:00
This continues until either :
2021-06-21 22:30:52 -07:00
- the O2 reading flips from lean to rich , at which point the adjustment cycle starts again at 1 % or
2021-10-20 14:03:29 -07:00
- the adjustment amount increases to egoLimit at which point it stays at this level until the O2 state ( rich / lean ) changes
2017-02-13 06:07:05 -08:00
2015-02-05 13:11:33 -08:00
PID ( Best suited to wideband sensors ) :
2017-02-13 06:07:05 -08:00
2015-02-05 13:11:33 -08:00
*/
2022-11-05 15:43:29 -07:00
byte correctionAFRClosedLoop ( void )
2015-02-05 13:11:33 -08:00
{
2017-06-03 03:45:25 -07:00
byte AFRValue = 100 ;
2021-01-19 18:01:04 -08:00
if ( ( configPage6 . egoType > 0 ) | | ( configPage2 . incorporateAFR = = true ) ) //afrTarget value lookup must be done if O2 sensor is enabled, and always if incorporateAFR is enabled
2015-02-05 13:11:33 -08:00
{
2022-04-10 17:49:58 -07:00
currentStatus . afrTarget = currentStatus . O2 ; //Catch all in case the below doesn't run. This prevents the Include AFR option from doing crazy things if the AFR target conditions aren't met. This value is changed again below if all conditions are met.
2017-06-03 03:45:25 -07:00
2017-10-31 22:10:06 -07:00
//Determine whether the Y axis of the AFR target table tshould be MAP (Speed-Density) or TPS (Alpha-N)
2020-08-13 05:48:26 -07:00
//Note that this should only run after the sensor warmup delay when using Include AFR option, but on Incorporate AFR option it needs to be done at all times
if ( ( currentStatus . runSecs > configPage6 . ego_sdelay ) | | ( configPage2 . incorporateAFR = = true ) ) { currentStatus . afrTarget = get3DTableValue ( & afrTable , currentStatus . fuelLoad , currentStatus . RPM ) ; } //Perform the target lookup
2021-01-19 18:01:04 -08:00
}
2024-03-20 17:37:45 -07:00
if ( ( configPage6 . egoType > 0 ) & & ( BIT_CHECK ( currentStatus . status1 , BIT_STATUS1_DFCO ) ! = 1 ) ) //egoType of 0 means no O2 sensor. If DFCO is active do not run the ego controllers to prevent iterator wind-up.
2021-01-19 18:01:04 -08:00
{
2020-04-23 18:17:42 -07:00
AFRValue = currentStatus . egoCorrection ; //Need to record this here, just to make sure the correction stays 'on' even if the nextCycle count isn't ready
2022-12-13 17:03:53 -08:00
if ( ( ignitionCount > = AFRnextCycle ) | | ( ignitionCount < ( AFRnextCycle - configPage6 . egoCount ) ) )
2015-02-05 13:11:33 -08:00
{
2020-04-06 22:03:15 -07:00
AFRnextCycle = ignitionCount + configPage6 . egoCount ; //Set the target ignition event for the next calculation
//Check all other requirements for closed loop adjustments
2023-10-24 04:06:53 -07:00
if ( ( currentStatus . coolant > ( int ) ( configPage6 . egoTemp - CALIBRATION_TEMPERATURE_OFFSET ) ) & & ( currentStatus . RPM > ( unsigned int ) ( configPage6 . egoRPM * 100 ) ) & & ( currentStatus . TPS < = configPage6 . egoTPSMax ) & & ( currentStatus . O2 < configPage6 . ego_max ) & & ( currentStatus . O2 > configPage6 . ego_min ) & & ( currentStatus . runSecs > configPage6 . ego_sdelay ) & & ( BIT_CHECK ( currentStatus . status1 , BIT_STATUS1_DFCO ) = = 0 ) & & ( currentStatus . MAP < = ( configPage9 . egoMAPMax * 2U ) ) & & ( currentStatus . MAP > = ( configPage9 . egoMAPMin * 2U ) ) )
2015-02-15 02:32:38 -08:00
{
2017-11-05 01:56:04 -07:00
//Check which algorithm is used, simple or PID
2018-01-23 17:05:50 -08:00
if ( configPage6 . egoAlgorithm = = EGO_ALGORITHM_SIMPLE )
2015-02-15 02:32:38 -08:00
{
2017-11-05 01:56:04 -07:00
//*************************************************************************************************************************************
//Simple algorithm
if ( currentStatus . O2 > currentStatus . afrTarget )
2015-02-15 02:32:38 -08:00
{
2017-11-05 01:56:04 -07:00
//Running lean
2022-04-10 17:49:58 -07:00
if ( currentStatus . egoCorrection < ( 100 + configPage6 . egoLimit ) ) //Fuelling adjustment must be at most the egoLimit amount (up or down)
2017-11-05 01:56:04 -07:00
{
2022-04-10 17:49:58 -07:00
AFRValue = ( currentStatus . egoCorrection + 1 ) ; //Increase the fuelling by 1%
2017-11-05 01:56:04 -07:00
}
2018-06-04 00:01:53 -07:00
else { AFRValue = currentStatus . egoCorrection ; } //Means we're at the maximum adjustment amount, so simply return that again
2015-02-15 02:32:38 -08:00
}
2018-06-04 00:01:53 -07:00
else if ( currentStatus . O2 < currentStatus . afrTarget )
{
2017-11-05 01:56:04 -07:00
//Running Rich
2022-04-10 17:49:58 -07:00
if ( currentStatus . egoCorrection > ( 100 - configPage6 . egoLimit ) ) //Fuelling adjustment must be at most the egoLimit amount (up or down)
2017-11-05 01:56:04 -07:00
{
2022-04-10 17:49:58 -07:00
AFRValue = ( currentStatus . egoCorrection - 1 ) ; //Decrease the fuelling by 1%
2017-11-05 01:56:04 -07:00
}
2018-06-04 00:01:53 -07:00
else { AFRValue = currentStatus . egoCorrection ; } //Means we're at the maximum adjustment amount, so simply return that again
}
else { AFRValue = currentStatus . egoCorrection ; } //Means we're already right on target
2015-02-15 02:32:38 -08:00
}
2018-01-23 17:05:50 -08:00
else if ( configPage6 . egoAlgorithm = = EGO_ALGORITHM_PID )
2017-11-05 01:56:04 -07:00
{
//*************************************************************************************************************************************
//PID algorithm
2022-04-10 17:49:58 -07:00
egoPID . SetOutputLimits ( ( long ) ( - configPage6 . egoLimit ) , ( long ) ( configPage6 . egoLimit ) ) ; //Set the limits again, just in case the user has changed them since the last loop. Note that these are sent to the PID library as (Eg:) -15 and +15
egoPID . SetTunings ( configPage6 . egoKP , configPage6 . egoKI , configPage6 . egoKD ) ; //Set the PID values again, just in case the user has changed them since the last loop
2017-11-05 01:56:04 -07:00
PID_O2 = ( long ) ( currentStatus . O2 ) ;
PID_AFRTarget = ( long ) ( currentStatus . afrTarget ) ;
2019-12-01 19:47:30 -08:00
bool PID_compute = egoPID . Compute ( ) ;
2017-11-05 01:56:04 -07:00
//currentStatus.egoCorrection = 100 + PID_output;
2019-12-01 19:47:30 -08:00
if ( PID_compute = = true ) { AFRValue = 100 + PID_output ; }
2017-11-05 01:56:04 -07:00
}
else { AFRValue = 100 ; } // Occurs if the egoAlgorithm is set to 0 (No Correction)
2020-04-06 22:03:15 -07:00
} //Multi variable check
2020-04-23 18:17:42 -07:00
else { AFRValue = 100 ; } // If multivariable check fails disable correction
2020-04-06 22:03:15 -07:00
} //Ignition count check
2017-06-03 03:45:25 -07:00
} //egoType
2017-02-13 06:07:05 -08:00
2017-06-03 03:45:25 -07:00
return AFRValue ; //Catch all (Includes when AFR target = current AFR
2015-02-05 13:11:33 -08:00
}
2016-12-11 03:51:04 -08:00
//******************************** IGNITION ADVANCE CORRECTIONS ********************************
2021-06-21 22:30:52 -07:00
/** Dispatch calculations for all ignition related corrections.
* @ param base_advance - Base ignition advance ( deg . ? )
* @ return Advance considering all ( ~ 12 ) individual corrections
*/
2017-06-03 03:45:25 -07:00
int8_t correctionsIgn ( int8_t base_advance )
2016-12-11 03:51:04 -08:00
{
2017-06-03 03:45:25 -07:00
int8_t advance ;
advance = correctionFlexTiming ( base_advance ) ;
2020-07-21 18:27:21 -07:00
advance = correctionWMITiming ( advance ) ;
2017-01-10 23:30:46 -08:00
advance = correctionIATretard ( advance ) ;
2019-04-11 05:31:24 -07:00
advance = correctionCLTadvance ( advance ) ;
2019-11-07 21:40:53 -08:00
advance = correctionIdleAdvance ( advance ) ;
2017-01-10 23:30:46 -08:00
advance = correctionSoftRevLimit ( advance ) ;
2018-06-29 08:19:51 -07:00
advance = correctionNitrous ( advance ) ;
2017-01-10 23:30:46 -08:00
advance = correctionSoftLaunch ( advance ) ;
2017-01-17 18:20:04 -08:00
advance = correctionSoftFlatShift ( advance ) ;
2018-08-13 19:47:15 -07:00
advance = correctionKnock ( advance ) ;
2017-05-09 22:46:14 -07:00
2023-10-24 16:09:57 -07:00
advance = correctionDFCOignition ( advance ) ;
2016-12-11 03:51:04 -08:00
//Fixed timing check must go last
2017-01-10 23:30:46 -08:00
advance = correctionFixedTiming ( advance ) ;
2019-11-07 21:40:53 -08:00
advance = correctionCrankingFixedTiming ( advance ) ; //This overrides the regular fixed timing, must come last
2016-12-11 03:51:04 -08:00
return advance ;
}
2021-06-21 22:30:52 -07:00
/** Correct ignition timing to configured fixed value.
* Must be called near end to override all other corrections .
*/
2020-02-02 22:35:29 -08:00
int8_t correctionFixedTiming ( int8_t advance )
2016-12-11 03:51:04 -08:00
{
2018-05-01 00:33:52 -07:00
int8_t ignFixValue = advance ;
2018-06-29 01:07:39 -07:00
if ( configPage2 . fixAngEnable = = 1 ) { ignFixValue = configPage4 . FixAng ; } //Check whether the user has set a fixed timing angle
2017-06-03 03:45:25 -07:00
return ignFixValue ;
2016-12-11 03:51:04 -08:00
}
2021-06-21 22:30:52 -07:00
/** Correct ignition timing to configured fixed value to use during craning.
* Must be called near end to override all other corrections .
*/
2020-02-02 22:35:29 -08:00
int8_t correctionCrankingFixedTiming ( int8_t advance )
2016-12-11 03:51:04 -08:00
{
2021-11-08 12:57:15 -08:00
int8_t ignCrankFixValue = advance ;
2023-05-09 17:32:11 -07:00
if ( BIT_CHECK ( currentStatus . engine , BIT_ENGINE_CRANK ) )
{
if ( configPage2 . crkngAddCLTAdv = = 0 ) { ignCrankFixValue = configPage4 . CrankAng ; } //Use the fixed cranking ignition angle
else { ignCrankFixValue = correctionCLTadvance ( configPage4 . CrankAng ) ; } //Use the CLT compensated cranking ignition angle
}
2017-06-03 03:45:25 -07:00
return ignCrankFixValue ;
2016-12-11 03:51:04 -08:00
}
2020-02-02 22:35:29 -08:00
int8_t correctionFlexTiming ( int8_t advance )
2016-12-11 03:51:04 -08:00
{
2020-04-15 23:31:54 -07:00
int16_t ignFlexValue = advance ;
2018-01-23 17:05:50 -08:00
if ( configPage2 . flexEnabled = = 1 ) //Check for flex being enabled
2017-06-03 03:45:25 -07:00
{
2020-04-15 23:31:54 -07:00
ignFlexValue = ( int16_t ) table2D_getValue ( & flexAdvTable , currentStatus . ethanolPct ) - OFFSET_IGNITION ; //Negative values are achieved with offset
currentStatus . flexIgnCorrection = ( int8_t ) ignFlexValue ; //This gets cast to a signed 8 bit value to allows for negative advance (ie retard) values here.
ignFlexValue = ( int8_t ) advance + currentStatus . flexIgnCorrection ;
2017-06-03 03:45:25 -07:00
}
2020-04-15 23:31:54 -07:00
return ( int8_t ) ignFlexValue ;
2016-12-11 03:51:04 -08:00
}
2020-07-21 18:27:21 -07:00
int8_t correctionWMITiming ( int8_t advance )
{
2022-01-01 00:30:21 -08:00
if ( ( configPage10 . wmiEnabled > = 1 ) & & ( configPage10 . wmiAdvEnabled = = 1 ) & & ! BIT_CHECK ( currentStatus . status4 , BIT_STATUS4_WMI_EMPTY ) ) //Check for wmi being enabled
2020-07-21 18:27:21 -07:00
{
2022-01-01 00:30:21 -08:00
if ( ( currentStatus . TPS > = configPage10 . wmiTPS ) & & ( currentStatus . RPM > = configPage10 . wmiRPM ) & & ( currentStatus . MAP / 2 > = configPage10 . wmiMAP ) & & ( ( currentStatus . IAT + CALIBRATION_TEMPERATURE_OFFSET ) > = configPage10 . wmiIAT ) )
2020-07-21 18:27:21 -07:00
{
return ( int16_t ) advance + table2D_getValue ( & wmiAdvTable , currentStatus . MAP / 2 ) - OFFSET_IGNITION ; //Negative values are achieved with offset
}
}
return advance ;
}
2021-06-21 22:30:52 -07:00
/** Ignition correction for inlet air temperature (IAT).
*/
2020-02-02 22:35:29 -08:00
int8_t correctionIATretard ( int8_t advance )
2016-12-11 03:51:04 -08:00
{
2017-05-09 00:29:55 -07:00
int8_t advanceIATadjust = table2D_getValue ( & IATRetardTable , currentStatus . IAT ) ;
2017-06-03 03:45:25 -07:00
2021-11-08 12:57:15 -08:00
return advance - advanceIATadjust ;
2016-12-11 03:51:04 -08:00
}
2021-06-21 22:30:52 -07:00
/** Ignition correction for coolant temperature (CLT).
*/
2020-02-02 22:35:29 -08:00
int8_t correctionCLTadvance ( int8_t advance )
2019-04-11 05:31:24 -07:00
{
2019-06-23 21:46:11 -07:00
int8_t ignCLTValue = advance ;
2019-04-11 05:31:24 -07:00
//Adjust the advance based on CLT.
2019-06-23 21:46:11 -07:00
int8_t advanceCLTadjust = ( int16_t ) ( table2D_getValue ( & CLTAdvanceTable , currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) ) - 15 ;
ignCLTValue = ( advance + advanceCLTadjust ) ;
2019-04-11 05:31:24 -07:00
return ignCLTValue ;
}
2021-06-21 22:30:52 -07:00
/** Ignition Idle advance correction.
*/
2020-02-02 22:35:29 -08:00
int8_t correctionIdleAdvance ( int8_t advance )
2019-11-07 21:40:53 -08:00
{
int8_t ignIdleValue = advance ;
//Adjust the advance based on idle target rpm.
2022-04-10 20:38:23 -07:00
if ( ( configPage2 . idleAdvEnabled > = 1 ) & & ( runSecsX10 > = ( configPage2 . idleAdvDelay * 5 ) ) & & idleAdvActive )
2019-11-07 21:40:53 -08:00
{
2022-04-15 19:18:58 -07:00
//currentStatus.CLIdleTarget = (byte)table2D_getValue(&idleTargetTable, currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET); //All temps are offset by 40 degrees
2019-12-01 19:47:30 -08:00
int idleRPMdelta = ( currentStatus . CLIdleTarget - ( currentStatus . RPM / 10 ) ) + 50 ;
2019-11-07 21:40:53 -08:00
// Limit idle rpm delta between -500rpm - 500rpm
if ( idleRPMdelta > 100 ) { idleRPMdelta = 100 ; }
if ( idleRPMdelta < 0 ) { idleRPMdelta = 0 ; }
2022-04-04 14:27:54 -07:00
if ( ( currentStatus . RPM < ( configPage2 . idleAdvRPM * 100 ) ) & & ( ( configPage2 . vssMode = = 0 ) | | ( currentStatus . vss < configPage2 . idleAdvVss ) )
2021-07-19 01:03:07 -07:00
& & ( ( ( configPage2 . idleAdvAlgorithm = = 0 ) & & ( currentStatus . TPS < configPage2 . idleAdvTPS ) ) | | ( ( configPage2 . idleAdvAlgorithm = = 1 ) & & ( currentStatus . CTPSActive = = 1 ) ) ) ) // closed throttle position sensor (CTPS) based idle state
2019-11-07 21:40:53 -08:00
{
2022-04-04 14:27:54 -07:00
if ( idleAdvTaper < configPage9 . idleAdvStartDelay )
{
if ( BIT_CHECK ( LOOP_TIMER , BIT_TIMER_10HZ ) ) { idleAdvTaper + + ; }
}
else
2021-07-19 01:03:07 -07:00
{
int8_t advanceIdleAdjust = ( int16_t ) ( table2D_getValue ( & idleAdvanceTable , idleRPMdelta ) ) - 15 ;
if ( configPage2 . idleAdvEnabled = = 1 ) { ignIdleValue = ( advance + advanceIdleAdjust ) ; }
else if ( configPage2 . idleAdvEnabled = = 2 ) { ignIdleValue = advanceIdleAdjust ; }
}
2019-11-07 21:40:53 -08:00
}
2022-04-04 14:27:54 -07:00
else { idleAdvTaper = 0 ; }
2019-11-07 21:40:53 -08:00
}
2022-04-10 20:38:23 -07:00
2023-05-03 07:24:45 -07:00
/* When Idle advance is the only idle speed control mechanism, activate as soon as not cranking.
When some other mechanism is also present , wait until the engine is no more than 200 RPM below idle target speed on first time
*/
if ( ( ! idleAdvActive & & BIT_CHECK ( currentStatus . engine , BIT_ENGINE_RUN ) ) & &
( ( configPage6 . iacAlgorithm = = 0 ) | | ( currentStatus . RPM > ( ( ( uint16_t ) currentStatus . CLIdleTarget * 10 ) - ( uint16_t ) IGN_IDLE_THRESHOLD ) ) ) )
{
idleAdvActive = true ;
}
else
if ( idleAdvActive & & ! BIT_CHECK ( currentStatus . engine , BIT_ENGINE_RUN ) ) { idleAdvActive = false ; } //Clear flag if engine isn't running anymore
2022-04-10 20:38:23 -07:00
2019-11-07 21:40:53 -08:00
return ignIdleValue ;
}
2021-06-21 22:30:52 -07:00
/** Ignition soft revlimit correction.
*/
2020-02-02 22:35:29 -08:00
int8_t correctionSoftRevLimit ( int8_t advance )
2016-12-11 03:51:04 -08:00
{
2017-06-03 03:45:25 -07:00
byte ignSoftRevValue = advance ;
2016-12-11 03:51:04 -08:00
BIT_CLEAR ( currentStatus . spark , BIT_SPARK_SFTLIM ) ;
2021-12-21 18:29:13 -08:00
if ( configPage6 . engineProtectType = = PROTECT_CUT_IGN | | configPage6 . engineProtectType = = PROTECT_CUT_BOTH )
{
2022-03-27 14:31:58 -07:00
if ( currentStatus . RPMdiv100 > = configPage4 . SoftRevLim ) //Softcut RPM limit
2021-10-11 16:25:14 -07:00
{
2022-04-04 14:27:54 -07:00
BIT_SET ( currentStatus . spark , BIT_SPARK_SFTLIM ) ;
if ( softLimitTime < configPage4 . SoftLimMax )
2021-12-21 18:29:13 -08:00
{
if ( configPage2 . SoftLimitMode = = SOFT_LIMIT_RELATIVE ) { ignSoftRevValue = ignSoftRevValue - configPage4 . SoftLimRetard ; } //delay timing by configured number of degrees in relative mode
else if ( configPage2 . SoftLimitMode = = SOFT_LIMIT_FIXED ) { ignSoftRevValue = configPage4 . SoftLimRetard ; } //delay timing to configured number of degrees in fixed mode
2022-04-04 14:27:54 -07:00
if ( BIT_CHECK ( LOOP_TIMER , BIT_TIMER_10HZ ) ) { softLimitTime + + ; }
2021-12-21 18:29:13 -08:00
}
2022-04-04 14:27:54 -07:00
}
else if ( BIT_CHECK ( LOOP_TIMER , BIT_TIMER_10HZ ) ) { softLimitTime = 0 ; } //Only reset time at runSecsX10 update rate
2020-05-08 15:39:13 -07:00
}
2017-06-03 03:45:25 -07:00
return ignSoftRevValue ;
2016-12-11 03:51:04 -08:00
}
2021-06-21 22:30:52 -07:00
/** Ignition Nitrous oxide correction.
*/
2020-02-02 22:35:29 -08:00
int8_t correctionNitrous ( int8_t advance )
2018-06-29 08:19:51 -07:00
{
byte ignNitrous = advance ;
//Check if nitrous is currently active
if ( configPage10 . n2o_enable > 0 )
{
//Check which stage is running (if any)
2020-03-18 20:18:54 -07:00
if ( ( currentStatus . nitrous_status = = NITROUS_STAGE1 ) | | ( currentStatus . nitrous_status = = NITROUS_BOTH ) )
2018-06-29 08:19:51 -07:00
{
ignNitrous - = configPage10 . n2o_stage1_retard ;
}
2020-03-18 20:18:54 -07:00
if ( ( currentStatus . nitrous_status = = NITROUS_STAGE2 ) | | ( currentStatus . nitrous_status = = NITROUS_BOTH ) )
2018-06-29 08:19:51 -07:00
{
ignNitrous - = configPage10 . n2o_stage2_retard ;
}
}
return ignNitrous ;
}
2021-06-21 22:30:52 -07:00
/** Ignition soft launch correction.
*/
2020-02-02 22:35:29 -08:00
int8_t correctionSoftLaunch ( int8_t advance )
2016-12-11 03:51:04 -08:00
{
2017-06-03 03:45:25 -07:00
byte ignSoftLaunchValue = advance ;
2017-02-13 06:07:05 -08:00
//SoftCut rev limit for 2-step launch control.
2023-12-05 19:47:13 -08:00
if ( configPage6 . launchEnabled & & currentStatus . clutchTrigger & & ( currentStatus . clutchEngagedRPM < ( ( unsigned int ) ( configPage6 . flatSArm ) * 100 ) ) & & ( currentStatus . RPM > ( ( unsigned int ) ( configPage6 . lnchSoftLim ) * 100 ) ) & & ( currentStatus . TPS > = configPage10 . lnchCtrlTPS ) )
2017-01-17 18:20:04 -08:00
{
2017-02-13 06:07:05 -08:00
currentStatus . launchingSoft = true ;
2017-01-17 18:20:04 -08:00
BIT_SET ( currentStatus . spark , BIT_SPARK_SLAUNCH ) ;
2018-01-23 17:05:50 -08:00
ignSoftLaunchValue = configPage6 . lnchRetard ;
2017-06-03 03:45:25 -07:00
}
else
{
currentStatus . launchingSoft = false ;
BIT_CLEAR ( currentStatus . spark , BIT_SPARK_SLAUNCH ) ;
2017-02-13 06:07:05 -08:00
}
2017-01-17 18:20:04 -08:00
2017-06-03 03:45:25 -07:00
return ignSoftLaunchValue ;
2017-01-17 18:20:04 -08:00
}
2021-06-21 22:30:52 -07:00
/** Ignition correction for soft flat shift.
*/
2020-02-02 22:35:29 -08:00
int8_t correctionSoftFlatShift ( int8_t advance )
2017-01-17 18:20:04 -08:00
{
2021-07-18 16:35:42 -07:00
int8_t ignSoftFlatValue = advance ;
2017-06-03 03:45:25 -07:00
2023-12-05 19:47:13 -08:00
if ( configPage6 . flatSEnable & & currentStatus . clutchTrigger & & ( currentStatus . clutchEngagedRPM > ( ( unsigned int ) ( configPage6 . flatSArm ) * 100 ) ) & & ( currentStatus . RPM > ( currentStatus . clutchEngagedRPM - ( configPage6 . flatSSoftWin * 100 ) ) ) )
2017-02-13 06:07:05 -08:00
{
2017-01-17 18:20:04 -08:00
BIT_SET ( currentStatus . spark2 , BIT_SPARK2_FLATSS ) ;
2018-01-23 17:05:50 -08:00
ignSoftFlatValue = configPage6 . flatSRetard ;
2017-01-17 18:20:04 -08:00
}
2017-06-03 03:45:25 -07:00
else { BIT_CLEAR ( currentStatus . spark2 , BIT_SPARK2_FLATSS ) ; }
2017-01-17 18:20:04 -08:00
2017-06-03 03:45:25 -07:00
return ignSoftFlatValue ;
2016-12-11 03:51:04 -08:00
}
2021-06-21 22:30:52 -07:00
/** Ignition knock (retard) correction.
*/
2020-02-02 22:35:29 -08:00
int8_t correctionKnock ( int8_t advance )
2018-07-26 05:09:48 -07:00
{
byte knockRetard = 0 ;
2022-04-10 17:49:58 -07:00
//First check is to do the window calculations (Assuming knock is enabled)
2018-08-31 00:36:09 -07:00
if ( configPage10 . knock_mode ! = KNOCK_MODE_OFF )
2018-08-13 19:47:15 -07:00
{
2020-06-05 17:52:27 -07:00
knockWindowMin = table2D_getValue ( & knockWindowStartTable , currentStatus . RPMdiv100 ) ;
knockWindowMax = knockWindowMin + table2D_getValue ( & knockWindowDurationTable , currentStatus . RPMdiv100 ) ;
2018-08-31 00:36:09 -07:00
}
if ( ( configPage10 . knock_mode = = KNOCK_MODE_DIGITAL ) )
{
//
if ( knockCounter > configPage10 . knock_count )
{
if ( currentStatus . knockActive = = true )
{
//Knock retard is currently
}
else
{
//Knock needs to be activated
lastKnockCount = knockCounter ;
knockStartTime = micros ( ) ;
knockRetard = configPage10 . knock_firstStep ;
}
}
2018-08-13 19:47:15 -07:00
}
2018-07-26 05:09:48 -07:00
return advance - knockRetard ;
}
2023-10-24 16:09:57 -07:00
/** Ignition DFCO taper correction.
*/
int8_t correctionDFCOignition ( int8_t advance )
{
int8_t dfcoRetard = advance ;
if ( ( configPage9 . dfcoTaperEnable = = 1 ) & & BIT_CHECK ( currentStatus . status1 , BIT_STATUS1_DFCO ) )
{
if ( dfcoTaper ! = 0 )
{
dfcoRetard - = map ( dfcoTaper , configPage9 . dfcoTaperTime , 0 , 0 , configPage9 . dfcoTaperAdvance ) ;
}
else { dfcoRetard - = configPage9 . dfcoTaperAdvance ; } //Taper ended, use full value
}
else { dfcoTaper = configPage9 . dfcoTaperTime ; } //Keep updating the duration until DFCO is active
return dfcoRetard ;
}
2021-06-21 22:30:52 -07:00
/** Ignition Dwell Correction.
*/
2017-04-21 00:29:09 -07:00
uint16_t correctionsDwell ( uint16_t dwell )
{
2017-06-03 03:45:25 -07:00
uint16_t tempDwell = dwell ;
2022-09-26 18:55:42 -07:00
uint16_t sparkDur_uS = ( configPage4 . sparkDur * 100 ) ; //Spark duration is in mS*10. Multiple it by 100 to get spark duration in uS
2023-05-21 20:37:40 -07:00
if ( currentStatus . actualDwell = = 0 ) { currentStatus . actualDwell = tempDwell ; } //Initialise the actualDwell value if this is the first time being called
//**************************************************************************************************************************
2017-04-21 00:29:09 -07:00
//Pull battery voltage based dwell correction and apply if needed
currentStatus . dwellCorrection = table2D_getValue ( & dwellVCorrectionTable , currentStatus . battery10 ) ;
2022-02-17 19:55:43 -08:00
if ( currentStatus . dwellCorrection ! = 100 ) { tempDwell = div100 ( dwell ) * currentStatus . dwellCorrection ; }
2017-04-21 00:29:09 -07:00
2023-05-21 20:37:40 -07:00
//**************************************************************************************************************************
//Dwell error correction is a basic closed loop to keep the dwell time consistent even when adjusting its end time for the per tooth timing.
//This is mostly of benefit to low resolution triggers at low rpm (<1500)
if ( ( configPage2 . perToothIgn = = true ) & & ( configPage4 . dwellErrCorrect = = 1 ) )
{
int16_t error = tempDwell - currentStatus . actualDwell ;
if ( tempDwell > INT16_MAX ) { tempDwell = INT16_MAX ; } //Prevent overflow when casting to signed int
if ( error > ( ( int16_t ) tempDwell / 2 ) ) { error + = error ; } //Double correction amount if actual dwell is less than 50% of the requested dwell
if ( error > 0 ) { tempDwell + = error ; }
}
//**************************************************************************************************************************
/*
Dwell limiter - If the total required dwell time per revolution is longer than the maximum time available at the current RPM , reduce dwell . This can occur if there are multiple sparks per revolution
This only times this can occur are :
1. Single channel spark mode where there will be nCylinders / 2 sparks per revolution
2. Rotary ignition in wasted spark configuration ( FC / FD ) , results in 2 pulses per rev . RX - 8 is fully sequential resulting in 1 pulse , so not required
*/
2022-09-26 18:55:42 -07:00
uint16_t dwellPerRevolution = tempDwell + sparkDur_uS ;
2017-05-09 00:29:55 -07:00
int8_t pulsesPerRevolution = 1 ;
2022-04-03 03:09:55 -07:00
if ( ( ( configPage4 . sparkMode = = IGN_MODE_SINGLE ) | | ( ( configPage4 . sparkMode = = IGN_MODE_ROTARY ) & & ( configPage10 . rotaryType ! = ROTARY_IGN_RX8 ) ) ) & & ( configPage2 . nCylinders > 1 ) ) //No point in running this for 1 cylinder engines
2017-04-21 00:29:09 -07:00
{
2018-01-23 17:05:50 -08:00
pulsesPerRevolution = ( configPage2 . nCylinders > > 1 ) ;
2017-04-21 00:29:09 -07:00
dwellPerRevolution = dwellPerRevolution * pulsesPerRevolution ;
}
if ( dwellPerRevolution > revolutionTime )
{
//Possibly need some method of reducing spark duration here as well, but this is a start
2023-11-05 14:10:08 -08:00
uint16_t adjustedSparkDur = udiv_32_16 ( sparkDur_uS * revolutionTime , dwellPerRevolution ) ;
tempDwell = udiv_32_16 ( revolutionTime , ( uint16_t ) pulsesPerRevolution ) - adjustedSparkDur ;
2017-04-21 00:29:09 -07:00
}
2023-05-21 20:37:40 -07:00
2017-06-03 03:45:25 -07:00
return tempDwell ;
2017-04-21 00:29:09 -07:00
}