From b5f60ba0126721220d8d269fc83db92e902cb5e7 Mon Sep 17 00:00:00 2001 From: DeionSi Date: Mon, 11 Oct 2021 01:55:32 +0200 Subject: [PATCH] Chrysler NGC pattern for 4-cylinder cam pattern (#679) * Chrysler NGC pattern for 4-cylinder cam pattern * New ignition mode for NGC decoder * NGC decoder: Cleanup/performance improvements * NGC decoder: Comment fixes --- reference/speeduino.ini | 7 +- speeduino/decoders.h | 6 + speeduino/decoders.ino | 250 ++++++++++++++++++++++++++++++++++++++++ speeduino/init.ino | 14 +++ 4 files changed, 274 insertions(+), 3 deletions(-) diff --git a/reference/speeduino.ini b/reference/speeduino.ini index 66140880..0a196a20 100644 --- a/reference/speeduino.ini +++ b/reference/speeduino.ini @@ -147,6 +147,7 @@ #define trigger_Webber = 19 #define trigger_FordST170 = 20 #define trigger_DRZ400 = 21 + #define trigger_NGC = 22 [Constants] @@ -452,7 +453,7 @@ page = 4 TrigEdge = bits, U08, 5,[0:0], "RISING", "FALLING" TrigSpeed = bits, U08, 5,[1:1], "Crank Speed", "Cam Speed" IgInv = bits, U08, 5,[2:2], "Going Low", "Going High" - TrigPattern= bits, U08, 5,[3:7], "Missing Tooth", "Basic Distributor", "Dual Wheel", "GM 7X", "4G63 / Miata / 3000GT", "GM 24X", "Jeep 2000", "Audi 135", "Honda D17", "Miata 99-05", "Mazda AU", "Non-360 Dual", "Nissan 360", "Subaru 6/7", "Daihatsu +1", "Harley EVO", "36-2-2-2", "36-2-1", "DSM 420a", "Weber-Marelli", "Ford ST170", "DRZ400", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID" + TrigPattern= bits, U08, 5,[3:7], "Missing Tooth", "Basic Distributor", "Dual Wheel", "GM 7X", "4G63 / Miata / 3000GT", "GM 24X", "Jeep 2000", "Audi 135", "Honda D17", "Miata 99-05", "Mazda AU", "Non-360 Dual", "Nissan 360", "Subaru 6/7", "Daihatsu +1", "Harley EVO", "36-2-2-2", "36-2-1", "DSM 420a", "Weber-Marelli", "Ford ST170", "DRZ400", "Chrysler NGC", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID" TrigEdgeSec= bits, U08, 6,[0:0], "RISING", "FALLING" fuelPumpPin= bits , U08, 6,[1:6], "Board Default", "INVALID", "INVALID", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "INVALID", "A8", "A9", "A10", "A11", "A12", "A13", "A14", "A15", "INVALID" useResync = bits, U08, 6,[7:7], "No", "Yes" @@ -2602,7 +2603,7 @@ menuDialog = main field = "Skip Revolutions", SkipCycles field = "Note: This is the number of revolutions that will be skipped during" field = "cranking before the injectors and coils are fired" - field = "Trigger edge", TrigEdge { TrigPattern != 4 } ;4G63 uses both edges + field = "Trigger edge", TrigEdge { TrigPattern != 4 && TrigPattern != 22 } ;4G63 uses both edges ;NGC uses both edges field = "Secondary trigger edge", TrigEdgeSec, { (TrigPattern == 0 && TrigSpeed == 0 && trigPatternSec != 2) || TrigPattern == 2 || TrigPattern == 9 || TrigPattern == 12 || TrigPattern == 18 || TrigPattern == 19 || TrigPattern == 20 || TrigPattern == 21 } ;Missing tooth, dual wheel and Miata 9905, weber-marelli, ST170, DRZ400 field = "Missing Tooth Secondary type", trigPatternSec, { (TrigPattern == 0&& TrigSpeed == 0) } field = "Level for 1st phase", PollLevelPol, { (TrigPattern == 0 && TrigSpeed == 0 && trigPatternSec == 2) } @@ -2627,7 +2628,7 @@ menuDialog = main field = "Cranking advance Angle", CrankAng field = "Spark Outputs triggers", IgInv panel = lockSparkSettings - panel = newIgnitionMode, {}, {TrigPattern == 0 || TrigPattern == 1 || TrigPattern == 2 || TrigPattern == 3 || TrigPattern == 4 || TrigPattern == 9 || TrigPattern == 12 || TrigPattern == 13 || TrigPattern == 16 || TrigPattern == 18 || TrigPattern == 19} ;Only works for missing tooth, distributor, dual wheel, GM 7X, 4g63, Miata 99-05, nissan 360, Subaru 6/7, 420a, weber-marelli + panel = newIgnitionMode, {}, {TrigPattern == 0 || TrigPattern == 1 || TrigPattern == 2 || TrigPattern == 3 || TrigPattern == 4 || TrigPattern == 9 || TrigPattern == 12 || TrigPattern == 13 || TrigPattern == 16 || TrigPattern == 18 || TrigPattern == 19 || TrigPattern == 22} ;Only works for missing tooth, distributor, dual wheel, GM 7X, 4g63, Miata 99-05, nissan 360, Subaru 6/7, 420a, weber-marelli, NGC dialog = dwellSettings, "Dwell Settings", 4 topicHelp = "https://wiki.speeduino.com/en/configuration/Dwell" diff --git a/speeduino/decoders.h b/speeduino/decoders.h index 268d34eb..180c436c 100644 --- a/speeduino/decoders.h +++ b/speeduino/decoders.h @@ -33,6 +33,7 @@ #define DECODER_WEBER 19 #define DECODER_ST170 20 #define DECODER_DRZ400 21 +#define DECODER_NGC 22 //This isn't to to filter out wrong pulses on triggers, but just to smooth out the cam angle reading for better closed loop VVT control. #define ANGLE_FILTER(input, alpha, prior) (((long)input * (256 - alpha) + ((long)prior * alpha))) >> 8 @@ -188,6 +189,11 @@ void triggerSetEndTeeth_FordST170(); void triggerSetup_DRZ400(); void triggerSec_DRZ400(); +void triggerSetup_NGC(); +void triggerPri_NGC(); +void triggerSec_NGC4(); +uint16_t getRPM_NGC(); +void triggerSetEndTeeth_NGC(); extern void (*triggerHandler)(); //Pointer for the trigger function (Gets pointed to the relevant decoder) extern void (*triggerSecondaryHandler)(); //Pointer for the secondary trigger function (Gets pointed to the relevant decoder) diff --git a/speeduino/decoders.ino b/speeduino/decoders.ino index 27351d91..0167b63e 100644 --- a/speeduino/decoders.ino +++ b/speeduino/decoders.ino @@ -65,6 +65,8 @@ volatile unsigned long toothLastSecToothTime = 0; //The time (micros()) that the volatile unsigned long toothLastThirdToothTime = 0; //The time (micros()) that the last tooth was registered on the second cam input volatile unsigned long toothLastMinusOneToothTime = 0; //The time (micros()) that the tooth before the last tooth was registered volatile unsigned long toothLastMinusOneSecToothTime = 0; //The time (micros()) that the tooth before the last tooth was registered on secondary input +volatile unsigned long toothLastToothRisingTime = 0; //The time (micros()) that the last tooth rose (used by special decoders to determine missing teeth polarity) +volatile unsigned long toothLastSecToothRisingTime = 0; //The time (micros()) that the last tooth rose on the secondary input (used by special decoders to determine missing teeth polarity) volatile unsigned long targetGap2; volatile unsigned long toothOneTime = 0; //The time (micros()) that tooth 1 last triggered volatile unsigned long toothOneMinusOneTime = 0; //The 2nd to last time (micros()) that tooth 1 last triggered @@ -4176,3 +4178,251 @@ void triggerSec_DRZ400() triggerSecFilterTime = (toothOneTime - toothOneMinusOneTime) >> 1; //Set filter at 50% of the current crank speed. } + +/** Chrysler NGC - a dedicated decoder for vehicles with 4 cylinder NGC pattern. +36+2-2 trigger wheel running at crank speed +7 tooth trigger wheel running at cam speed +Both wheels use the polarity of the missing teeth to determine position +* @defgroup dec Chrysler NGC 4-cylinder +* @{ +*/ + +void triggerSetup_NGC() +{ + secondDerivEnabled = false; + decoderIsSequential = true; + + //Primary trigger + configPage4.triggerTeeth = 36; //The number of teeth on the wheel incl missing teeth. + triggerToothAngle = 10; //The number of degrees that passes from tooth to tooth + triggerFilterTime = (int)(1000000 / (MAX_RPM / 60 * 36)); //Trigger filter time is the shortest possible time (in uS) that there can be between crank teeth (ie at max RPM). Any pulses that occur faster than this time will be disgarded as noise + toothCurrentCount = 0; + toothOneTime = 0; + toothOneMinusOneTime = 0; + toothLastMinusOneToothTime = 0; + toothLastToothRisingTime = 0; + MAX_STALL_TIME = (3333UL * triggerToothAngle * 2 ); //Minimum 50rpm. (3333uS is the time per degree at 50rpm) + + //Secondary trigger + triggerSecFilterTime = (1000000 / MAX_RPM * 60 / (360 / 36) / 2); //Two nearest edges are 36 degrees apart. Divide by 2 for cam speed. + secondaryToothCount = 0; + toothLastSecToothRisingTime = 0; + toothLastSecToothTime = 0; + toothLastMinusOneSecToothTime = 0; +} + +void triggerPri_NGC() { + curTime = micros(); + // We need to know the polarity of the missing tooth to determine position + if (READ_PRI_TRIGGER() == HIGH) { + toothLastToothRisingTime = curTime; + return; + } + + curGap = curTime - toothLastToothTime; + if ( curGap >= triggerFilterTime ) //Pulses should never be less than triggerFilterTime, so if they are it means a false trigger. + { + toothCurrentCount++; + validTrigger = true; + + if ( toothLastToothTime > 0 && toothLastMinusOneToothTime > 0 ) { //Make sure we haven't enough tooth information to calculate missing tooth length + + bool isMissingTooth = false; + + //Only check for missing tooth if we expect this one to be it or if we haven't found one yet + if (toothCurrentCount == 17 || toothCurrentCount == 35 || ( currentStatus.hasSync == false && BIT_CHECK(currentStatus.status3, BIT_STATUS3_HALFSYNC) == false) ) { + //If the time between the current tooth and the last is greater than 2x the time between the last tooth and the tooth before that, we make the assertion that we must be at the first tooth after the gap + if (curGap > ( (toothLastToothTime - toothLastMinusOneToothTime) * 2 ) ) + { + isMissingTooth = true; //Missing tooth detected + triggerFilterTime = 0; //This is used to prevent a condition where serious intermitent signals (Eg someone furiously plugging the sensor wire in and out) can leave the filter in an unrecoverable state + triggerToothAngleIsCorrect = false; //The tooth angle is bigger at this point + + // Figure out the polarity of the missing tooth by comparing how far ago the last tooth rose + if ((toothLastToothRisingTime - toothLastToothTime) < (curTime - toothLastToothRisingTime)) { + //Just passed the HIGH missing tooth + toothCurrentCount = 1; + + toothOneMinusOneTime = toothOneTime; + toothOneTime = curTime; + + if (currentStatus.hasSync == true) { currentStatus.startRevolutions++; } + else { currentStatus.startRevolutions = 0; } + } + else { + //Just passed the first tooth after the LOW missing tooth + toothCurrentCount = 19; + } + + //If Sequential fuel or ignition is in use, further checks are needed before determining sync + if( (configPage4.sparkMode == IGN_MODE_SEQUENTIAL) || (configPage2.injLayout == INJ_SEQUENTIAL) ) + { + // Verify the tooth counters are valid and use this to determine current revolution + if ( (toothCurrentCount == 1 && (secondaryToothCount == 1 || secondaryToothCount == 2) ) || (toothCurrentCount == 19 && secondaryToothCount == 4) ) + { + revolutionOne = false; + currentStatus.hasSync = true; + BIT_CLEAR(currentStatus.status3, BIT_STATUS3_HALFSYNC); //the engine is fully synced so clear the Half Sync bit + } + else if ( (toothCurrentCount == 1 && secondaryToothCount == 5) || (toothCurrentCount == 19 && secondaryToothCount == 7) ) + { + revolutionOne = true; + currentStatus.hasSync = true; + BIT_CLEAR(currentStatus.status3, BIT_STATUS3_HALFSYNC); //the engine is fully synced so clear the Half Sync bit + } + // If tooth counters are not valid, set half sync bit + else { + if (currentStatus.hasSync == true) { currentStatus.syncLossCounter++; } + currentStatus.hasSync = false; + BIT_SET(currentStatus.status3, BIT_STATUS3_HALFSYNC); } //If there is primary trigger but no secondary we only have half sync. + } + else { currentStatus.hasSync = true; BIT_CLEAR(currentStatus.status3, BIT_STATUS3_HALFSYNC); } //If nothing is using sequential, we have sync and also clear half sync bit + + } + else { + // If we have found a missing tooth and don't get the next one at the correct tooth we end up here -> Resync + if (currentStatus.hasSync == true) { currentStatus.syncLossCounter++; } + currentStatus.hasSync = false; + BIT_CLEAR(currentStatus.status3, BIT_STATUS3_HALFSYNC); + } + } + + if(isMissingTooth == false) + { + //Regular (non-missing) tooth + setFilter(curGap); + triggerToothAngleIsCorrect = true; + } + } + + toothLastMinusOneToothTime = toothLastToothTime; + toothLastToothTime = curTime; + } + + //NEW IGNITION MODE + if( (configPage2.perToothIgn == true) && (BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK) == false) ) + { + int16_t crankAngle = ( (toothCurrentCount-1) * triggerToothAngle ) + configPage4.triggerAngle; + crankAngle = ignitionLimits(crankAngle); + if( (configPage4.sparkMode == IGN_MODE_SEQUENTIAL) && (revolutionOne == true) && (configPage4.TrigSpeed == CRANK_SPEED) ) + { + crankAngle += 360; + checkPerToothTiming(crankAngle, (configPage4.triggerTeeth + toothCurrentCount)); + } + else{ checkPerToothTiming(crankAngle, toothCurrentCount); } + } +} + +void triggerSec_NGC4() +{ + //Only check the cam wheel for sequential operation + if( configPage4.sparkMode != IGN_MODE_SEQUENTIAL && configPage2.injLayout != INJ_SEQUENTIAL ) { + return; + } + + curTime2 = micros(); + + // We need to know the polarity of the missing tooth to determine position + if (READ_SEC_TRIGGER() == HIGH) { + toothLastSecToothRisingTime = curTime2; + return; + } + + curGap2 = curTime2 - toothLastSecToothTime; + + if ( curGap2 > triggerSecFilterTime ) + { + if ( toothLastSecToothTime > 0 && toothLastMinusOneSecToothTime > 0 ) //Make sure we have enough tooth information to calculate tooth lengths + { + if (secondaryToothCount > 0) { secondaryToothCount++; } + + if (curGap2 >= ((3 * (toothLastSecToothTime - toothLastMinusOneSecToothTime)) >> 1)) // Check if we have a bigger gap, that is a long tooth + { + // Check long tooth polarity + if ((toothLastSecToothRisingTime - toothLastSecToothTime) < (curTime2 - toothLastSecToothRisingTime)) { + //Just passed the HIGH missing tooth + if ( secondaryToothCount == 0 || secondaryToothCount == 8 ) { secondaryToothCount = 1; } // synced + else if (secondaryToothCount > 0) { secondaryToothCount = 0; } //Any other number of teeth seen means we missed something or something extra was seen so attempt resync. + } + else { + //Just passed the first tooth after the LOW missing tooth + if ( secondaryToothCount == 0 || secondaryToothCount == 5 ) { secondaryToothCount = 5; } + else if (secondaryToothCount > 0) { secondaryToothCount = 0; } + } + + triggerSecFilterTime = 0; //This is used to prevent a condition where serious intermitent signals (Eg someone furiously plugging the sensor wire in and out) can leave the filter in an unrecoverable state + } + else if (secondaryToothCount > 0) { + triggerSecFilterTime = curGap2 >> 2; //Set filter at 25% of the current speed. Filter can only be recalc'd for the regular teeth, not the missing one. + } + + } + + toothLastMinusOneSecToothTime = toothLastSecToothTime; + toothLastSecToothTime = curTime2; + } +} + +uint16_t getRPM_NGC() +{ + uint16_t tempRPM = 0; + if( currentStatus.RPM < currentStatus.crankRPM) + { + if (triggerToothAngleIsCorrect == true) { tempRPM = crankingGetRPM(36, 360); } + else { tempRPM = currentStatus.RPM; } //Can't do per tooth RPM if we're at any of the missing teeth as it messes the calculation + } + else + { + tempRPM = stdGetRPM(360); + } + return tempRPM; +} + +void triggerSetEndTeeth_NGC() +{ + byte toothAdder = 0; + if( (configPage4.sparkMode == IGN_MODE_SEQUENTIAL) && (configPage4.TrigSpeed == CRANK_SPEED) ) { toothAdder = configPage4.triggerTeeth; } + + int16_t tempIgnition1EndTooth; + tempIgnition1EndTooth = ( (ignition1EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition + if(tempIgnition1EndTooth < 1) { tempIgnition1EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 + else if(tempIgnition1EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition1EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count + if(tempIgnition1EndTooth == 17 || tempIgnition1EndTooth == 18) { tempIgnition1EndTooth = 16; } // These are missing teeth, so set the next one before instead + else if(tempIgnition1EndTooth == 35 || tempIgnition1EndTooth == 36) { tempIgnition1EndTooth = 34; } // These are missing teeth, so set the next one before instead + else if(tempIgnition1EndTooth == 53 || tempIgnition1EndTooth == 54) { tempIgnition1EndTooth = 52; } // These are missing teeth, so set the next one before instead + else if(tempIgnition1EndTooth > 70) { tempIgnition1EndTooth = 70; } // These are missing teeth, so set the next one before instead + ignition1EndTooth = tempIgnition1EndTooth; + + int16_t tempIgnition2EndTooth; + tempIgnition2EndTooth = ( (ignition2EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition + if(tempIgnition2EndTooth < 1) { tempIgnition2EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 + else if(tempIgnition2EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition2EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count + if(tempIgnition2EndTooth == 17 || tempIgnition2EndTooth == 18) { tempIgnition2EndTooth = 16; } // These are missing teeth, so set the next one before instead + else if(tempIgnition2EndTooth == 35 || tempIgnition2EndTooth == 36) { tempIgnition2EndTooth = 34; } // These are missing teeth, so set the next one before instead + else if(tempIgnition2EndTooth == 53 || tempIgnition2EndTooth == 54) { tempIgnition2EndTooth = 52; } // These are missing teeth, so set the next one before instead + else if(tempIgnition2EndTooth > 70) { tempIgnition2EndTooth = 70; } // These are missing teeth, so set the next one before instead + ignition2EndTooth = tempIgnition2EndTooth; + + int16_t tempIgnition3EndTooth; + tempIgnition3EndTooth = ( (ignition3EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition + if(tempIgnition3EndTooth < 1) { tempIgnition3EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 + else if(tempIgnition3EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition3EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count + if(tempIgnition3EndTooth == 17 || tempIgnition3EndTooth == 18) { tempIgnition3EndTooth = 16; } // These are missing teeth, so set the next one before instead + else if(tempIgnition3EndTooth == 35 || tempIgnition3EndTooth == 36) { tempIgnition3EndTooth = 34; } // These are missing teeth, so set the next one before instead + else if(tempIgnition3EndTooth == 53 || tempIgnition3EndTooth == 54) { tempIgnition3EndTooth = 52; } // These are missing teeth, so set the next one before instead + else if(tempIgnition3EndTooth > 70) { tempIgnition3EndTooth = 70; } // These are missing teeth, so set the next one before instead + ignition3EndTooth = tempIgnition3EndTooth; + + int16_t tempIgnition4EndTooth; + tempIgnition4EndTooth = ( (ignition4EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition + if(tempIgnition4EndTooth < 1) { tempIgnition4EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 + else if(tempIgnition4EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition4EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count + if(tempIgnition4EndTooth == 17 || tempIgnition4EndTooth == 18) { tempIgnition4EndTooth = 16; } // These are missing teeth, so set the next one before instead + else if(tempIgnition4EndTooth == 35 || tempIgnition4EndTooth == 36) { tempIgnition4EndTooth = 34; } // These are missing teeth, so set the next one before instead + else if(tempIgnition4EndTooth == 53 || tempIgnition4EndTooth == 54) { tempIgnition4EndTooth = 52; } // These are missing teeth, so set the next one before instead + else if(tempIgnition4EndTooth > 70) { tempIgnition4EndTooth = 70; } // These are missing teeth, so set the next one before instead + ignition4EndTooth = tempIgnition4EndTooth; + + lastToothCalcAdvance = currentStatus.advance; +} +/** @} */ \ No newline at end of file diff --git a/speeduino/init.ino b/speeduino/init.ino index f1f3e05b..e843cabd 100644 --- a/speeduino/init.ino +++ b/speeduino/init.ino @@ -3237,8 +3237,22 @@ void initialiseTriggers() attachInterrupt(triggerInterrupt2, triggerSecondaryHandler, secondaryTriggerEdge); break; + case DECODER_NGC: + //Chrysler NGC 4 cylinder + triggerSetup_NGC(); + triggerHandler = triggerPri_NGC; + triggerSecondaryHandler = triggerSec_NGC4; + decoderHasSecondary = true; + getRPM = getRPM_NGC; + getCrankAngle = getCrankAngle_missingTooth; + triggerSetEndTeeth = triggerSetEndTeeth_NGC; + primaryTriggerEdge = CHANGE; + secondaryTriggerEdge = CHANGE; + attachInterrupt(triggerInterrupt, triggerHandler, primaryTriggerEdge); + attachInterrupt(triggerInterrupt2, triggerSecondaryHandler, secondaryTriggerEdge); + break; default: triggerHandler = triggerPri_missingTooth;