AFR protection function (#861)

* Implementing AFR protection

* Update globals.h

* Bug fixes and code refactoring

* Moved variables to prevent ODR

* Dialog for AFR protection

* Data size and math corrections

* Possibility to use either AFR or lambda

* Updated default AFR protection values

* No need to do multiplications due to existing RPMdiv100 variable

* The X100 multiplier was wrongfully removed

* Add defaults in updates.ino, minor formatting cleanups

Co-authored-by: Josh Stewart <josh@noisymime.org>
This commit is contained in:
larmicfre 2022-06-20 01:52:07 +02:00 committed by GitHub
parent f64d9348dc
commit 298d994ba1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 157 additions and 13 deletions

View File

@ -1055,7 +1055,21 @@ page = 9
#else
coolantProtTemp = array, U08, 173, [6], "F", 1.8, -22.23, -40, 215, 0
#endif
unused179_191 = array, U08, 179, [13], "", 1, 0, 0, 255, 0
unused179_184 = array, U08, 179, [6], "", 1, 0, 0, 255, 0
; AFR engine protection
afrProtectEnabled = bits, U08, 185, [0:0], "Off", "On"
afrProtectMAP = scalar, U08, 186, "kPa", 2.0, 0.0, 0.0, 511.0, 0 ; 8 bit value, 1 byte
afrProtectRPM = scalar, U08, 187, "RPM", 100, 0.0, 100, 25500, 0 ; 8 bit value, 1 byte
afrProtectTPS = scalar, U08, 188, "%", 0.5, 0.0, 0.0, 100.0, 1 ; 8 bit value, 1 byte
#if LAMBDA
afrProtectDeviation = scalar, U08, 189, "Lambda", {0.1 / stoich}, 0.0, 0.00, 25.5, 2 ; 8 bit value, 1 byte
#else
afrProtectDeviation = scalar, U08, 189, "AFR", 0.1, 0.0, 0.0, 25.5, 1 ; 8 bit value, 1 byte
#endif
afrProtectCutTime = scalar, U08, 190, "seconds", 0.1, 0.0, 0.0, 2.5, 1 ; 8 bit value, 1 byte
afrProtectReactivationTPS = scalar, U08, 191, "%", 0.5, 0.0, 0.0, 100.0, 1 ; 8 bit value, 1 byte
page = 10
#if CELSIUS
@ -1571,6 +1585,19 @@ page = 15
#endif
defaultValue = vvtDelay, 60
; AFR protection default values
defaultValue = afrProtectEnabled, 0
defaultValue = afrProtectMAP, 180
defaultValue = afrProtectRPM, 4000
defaultValue = afrProtectTPS, 80
#if LAMBDA
defaultValue = afrProtectDeviation, 0.10
#else
defaultValue = afrProtectDeviation, 1.47
#endif
defaultValue = afrProtectCutTime, 0.8
defaultValue = afrProtectReactivationTPS, 20
;Default pins
defaultValue = fanPin, 0
defaultValue = vvt1Pin, 0
@ -1757,6 +1784,7 @@ menuDialog = main
groupChildMenu = revLimiterDialog, "Rev Limiters", { engineProtectType }
groupChildMenu = boostCut, "Boost Cut", { engineProtectType }
groupChildMenu = oilPressureProtection, "Oil Pressure", { engineProtectType }
groupChildMenu = afrProtect, "AFR Protection", { engineProtectType }
subMenu = flexFuel, "Flex Fuel", 2
subMenu = veTableDialog, "VE Table", 0
subMenu = sparkTbl, "Spark Table", 2
@ -1995,6 +2023,18 @@ menuDialog = main
hardRevLim = "A fixed hard rev limit is a single point that the fuel or ignition (or both) will be cut completely to reduce increasing RPMs"
engineProtectMaxRPM = "The RPM point that engine protections will engage from. Below this RPM value, engine protections will NOT be active"
; AFR Protection Help
afrProtectMAP = "Minimum manifold air pressure the AFR lean protection will activate"
afrProtectRPM = "The minimum RPM the AFR lean protection will activate"
afrProtectTPS = "The minimum current throttle position for the AFR lean protection to activate"
#if LAMBDA
afrProtectDeviation = "Maximum deviation from current lambda value in which lean protection will activate"
#else
afrProtectDeviation = "Maximum deviation from current AFR value in which lean protection will activate"
#endif
afrProtectCutTime = "A time delay before activating engine protection when all conditions has been met"
afrProtectReactivationTPS = "Going below this throttle position (%) will deactivate this protection"
fuel2InputPin = "The Arduino pin that is being used to trigger the second fuel table to be active"
fuel2InputPolarity = "Whether the 2nd fuel table should be active when input is high or low. This should be LOW for a typical ground switching input"
fuel2InputPullup = "Whether to use the built in PULLUP for the switching input. This should be Yes for a typical ground switching input"
@ -2818,7 +2858,23 @@ menuDialog = main
field = "Oil Pressure Protection", oilPressureProtEnbl, { oilPressureEnable }
panel = oil_pressure_prot_curve, { oilPressureEnable && oilPressureProtEnbl }
; AFR engine protection dialog
dialog = afrProtect, "AFR Protection", yAxis
field = "AFR protection is used to prevent engine from running lean"
field = "#Note: This function requires wideband sensor and proper AFR table"
field = ""
field = "Enable AFR protection ", afrProtectEnabled, {egoType == 2}
field = "Minimum manifold air pressure ", afrProtectMAP, {afrProtectEnabled}
field = "Minimum engine RPM ", afrProtectRPM, {afrProtectEnabled}
field = "Minimum throttle position ", afrProtectTPS, {afrProtectEnabled}
#if LAMBDA
field = "Maximum lambda deviation ", afrProtectDeviation, {afrProtectEnabled}
#else
field = "Maximum AFR deviation ", afrProtectDeviation, {afrProtectEnabled}
#endif
field = "Time before cut ", afrProtectCutTime, {afrProtectEnabled}
field = ""
field = "Reactivate below throttle ", afrProtectReactivationTPS, {afrProtectEnabled}
indicatorPanel = protectIndicatorPanel, 1, { 1 }
indicator = { engineProtectStatus}, "Engine Protect OFF", "Engine Protect ON", green, black, red, black

View File

@ -2,7 +2,6 @@
#define HARD_REV_FIXED 1
#define HARD_REV_COOLANT 2
byte checkEngineProtect();
byte checkRevLimit();
byte checkBoostLimit();

View File

@ -112,7 +112,85 @@ byte checkOilPressureLimit()
byte checkAFRLimit()
{
byte checkAFRLimitActive = 0;
static bool checkAFRLimitActive = false;
static bool afrProtectCountEnabled = false;
static unsigned long afrProtectCount = 0;
static constexpr char X2_MULTIPLIER = 2;
static constexpr char X100_MULTIPLIER = 100;
/*
To use this function, a wideband sensor is required.
First of all, check whether engine protection is enabled,
thereafter check whether AFR protection is enabled and at last
if wideband sensor is used.
After confirmation, the following conditions has to be met:
- MAP above x kPa
- RPM above x
- TPS above x %
- AFR threshold (AFR target + defined maximum deviation)
- Time before cut
See afrProtect variables in globals.h for more information.
If all conditions above are true, a specified time delay is starting
to count down in which leads to the engine protection function
to be activated using selected protection cut method (e.g. ignition,
fuel or both).
For reactivation, the following condition has to be met:
- TPS below x %
*/
/*
Do 3 checks here;
- whether engine protection is enabled
- whether AFR protection is enabled
- whether wideband sensor is used
*/
if(configPage6.engineProtectType != PROTECT_CUT_OFF && configPage9.afrProtectEnabled && configPage6.egoType == EGO_TYPE_WIDE) {
/* Conditions */
bool mapCondition = (currentStatus.MAP >= (configPage9.afrProtectMinMAP * X2_MULTIPLIER)) ? true : false;
bool rpmCondition = (currentStatus.RPMdiv100 >= configPage9.afrProtectMinRPM) ? true : false;
bool tpsCondition = (currentStatus.TPS >= configPage9.afrProtectMinTPS) ? true : false;
bool afrCondition = (currentStatus.O2 >= (currentStatus.afrTarget + configPage9.afrProtectDeviation)) ? true : false;
/* Check if conditions above are fulfilled */
if(mapCondition && rpmCondition && tpsCondition && afrCondition)
{
/* All conditions fulfilled - start counter for 'protection delay' */
if(!afrProtectCountEnabled)
{
afrProtectCountEnabled = true;
afrProtectCount = millis();
}
/* Check if countdown has reached its target, if so then instruct to cut */
if(millis() >= (afrProtectCount + (configPage9.afrProtectCutTime * X100_MULTIPLIER)))
{
checkAFRLimitActive = true;
BIT_SET(currentStatus.engineProtectStatus, ENGINE_PROTECT_BIT_AFR);
}
}
else
{
/* Conditions have presumably changed - deactivate and reset counter */
if(afrProtectCountEnabled)
{
afrProtectCountEnabled = false;
afrProtectCount = 0;
}
}
/* Check if condition for reactivation is fulfilled */
if(checkAFRLimitActive && (currentStatus.TPS <= configPage9.afrProtectReactivationTPS))
{
checkAFRLimitActive = false;
afrProtectCountEnabled = false;
BIT_CLEAR(currentStatus.engineProtectStatus, ENGINE_PROTECT_BIT_AFR);
}
}
return checkAFRLimitActive;
}

View File

@ -248,6 +248,10 @@
#define COMPOSITE_LOG_TRIG 2
#define COMPOSITE_LOG_SYNC 3
#define EGO_TYPE_OFF 0
#define EGO_TYPE_NARROW 1
#define EGO_TYPE_WIDE 2
#define INJ_TYPE_PORT 0
#define INJ_TYPE_TBODY 1
@ -1180,13 +1184,13 @@ struct config9 {
byte unused10_182;
byte unused10_183;
byte unused10_184;
byte unused10_185;
byte unused10_186;
byte unused10_187;
byte unused10_188;
byte unused10_189;
byte unused10_190;
byte unused10_191;
byte afrProtectEnabled : 1; /* < AFR protection enabled status. 0 = disabled, 1 = enabled */
byte afrProtectMinMAP; /* < Minimum MAP. Stored value is divided by 2. Increments of 2 kPa, maximum 511 (?) kPa */
byte afrProtectMinRPM; /* < Minimum RPM. Stored value is divded by 100. Increments of 100 RPM, maximum 25500 RPM */
byte afrProtectMinTPS; /* < Minimum TPS. */
byte afrProtectDeviation; /* < Maximum deviation from AFR target table. Stored value is multiplied by 10 */
byte afrProtectCutTime; /* < Time in ms before cut. Stored value is divided by 100. Maximum of 2550 ms */
byte afrProtectReactivationTPS; /* Disable engine protection cut once below this TPS percentage */
#if defined(CORE_AVR)
};

View File

@ -1328,7 +1328,7 @@ uint16_t PW(int REQ_FUEL, byte VE, long MAP, uint16_t corrections, int injOpen)
if ( configPage2.multiplyMAP == MULTIPLY_MAP_MODE_100) { iMAP = ((unsigned int)MAP << 7) / 100; }
else if( configPage2.multiplyMAP == MULTIPLY_MAP_MODE_BARO) { iMAP = ((unsigned int)MAP << 7) / currentStatus.baro; }
if ( (configPage2.includeAFR == true) && (configPage6.egoType == 2) && (currentStatus.runSecs > configPage6.ego_sdelay) ) {
if ( (configPage2.includeAFR == true) && (configPage6.egoType == EGO_TYPE_WIDE) && (currentStatus.runSecs > configPage6.ego_sdelay) ) {
iAFR = ((unsigned int)currentStatus.O2 << 7) / currentStatus.afrTarget; //Include AFR (vs target) if enabled
}
if ( (configPage2.incorporateAFR == true) && (configPage2.includeAFR == false) ) {
@ -1341,7 +1341,7 @@ uint16_t PW(int REQ_FUEL, byte VE, long MAP, uint16_t corrections, int injOpen)
unsigned long intermediate = ((uint32_t)REQ_FUEL * (uint32_t)iVE) >> 7; //Need to use an intermediate value to avoid overflowing the long
if ( configPage2.multiplyMAP > 0 ) { intermediate = (intermediate * (unsigned long)iMAP) >> 7; }
if ( (configPage2.includeAFR == true) && (configPage6.egoType == 2) && (currentStatus.runSecs > configPage6.ego_sdelay) ) {
if ( (configPage2.includeAFR == true) && (configPage6.egoType == EGO_TYPE_WIDE) && (currentStatus.runSecs > configPage6.ego_sdelay) ) {
//EGO type must be set to wideband and the AFR warmup time must've elapsed for this to be used
intermediate = (intermediate * (unsigned long)iAFR) >> 7;
}

View File

@ -659,6 +659,13 @@ void doUpdates()
++table_Y;
}
//AFR Protection added, add default values
configPage9.afrProtectEnabled = 0; //Disable by default
configPage9.afrProtectMinMAP = 90; //Is divided by 2, vlue represents 180kPa
configPage9.afrProtectMinRPM = 40; //4000 RPM min
configPage9.afrProtectMinTPS = 160; //80% TPS min
configPage9.afrProtectDeviation = 14; //1.4 AFR deviation
writeAllConfig();
storeEEPROMVersion(20);
}