From 2cd7ed327b063c92554e2ea6039b2333c99cd9b9 Mon Sep 17 00:00:00 2001 From: DeionSi Date: Thu, 23 Dec 2021 04:56:08 +0100 Subject: [PATCH] Chrysler NGC 6 and 8 cylinder decoder (#707) * Chrysler NGC 6 and 8 cylinder decoder * Extra comments * Only recalculate new ignition mode for valid teeth --- speeduino/decoders.h | 1 + speeduino/decoders.ino | 178 +++++++++++++++++++++++++++++++++++++---- speeduino/init.ino | 12 ++- 3 files changed, 171 insertions(+), 20 deletions(-) diff --git a/speeduino/decoders.h b/speeduino/decoders.h index 8d672771..a702cadb 100644 --- a/speeduino/decoders.h +++ b/speeduino/decoders.h @@ -192,6 +192,7 @@ void triggerSec_DRZ400(); void triggerSetup_NGC(); void triggerPri_NGC(); void triggerSec_NGC4(); +void triggerSec_NGC68(); uint16_t getRPM_NGC(); void triggerSetEndTeeth_NGC(); diff --git a/speeduino/decoders.ino b/speeduino/decoders.ino index 2d32ec5c..cac08b90 100644 --- a/speeduino/decoders.ino +++ b/speeduino/decoders.ino @@ -4177,11 +4177,14 @@ 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 +/** Chrysler NGC - a dedicated decoder for vehicles with 4, 6 and 8 cylinder NGC pattern. +4-cyl: 36+2-2 crank wheel and 7 tooth cam +6-cyl: 36-2+2 crank wheel and 12 tooth cam in 6 groups +8-cyl: 36-2+2 crank wheel and 15 tooth cam in 8 groups +The crank decoder uses the polarity of the missing teeth to determine position +The 4-cyl cam decoder uses the polarity of the missing teeth to determine position +The 6 and 8-cyl cam decoder uses the amount of teeth in the two previous groups of teeth to determine position +* @defgroup dec Chrysler NGC - 4, 6 and 8-cylinder * @{ */ @@ -4193,20 +4196,49 @@ void triggerSetup_NGC() //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 + triggerFilterTime = 1000000 / (MAX_RPM/60) / (360/triggerToothAngle); //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; + 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. + if (configPage2.nCylinders == 4) { + triggerSecFilterTime = (1000000 / (MAX_RPM/60) / (360/36) * 2); //Two nearest edges are 36 degrees apart. Multiply by 2 for half cam speed. + } else { + triggerSecFilterTime = (1000000 / (MAX_RPM/60) / (360/21) * 2); //Two nearest edges are 21 degrees apart. Multiply by 2 for half cam speed. + } secondaryToothCount = 0; + toothSystemCount = 0; toothLastSecToothRisingTime = 0; toothLastSecToothTime = 0; toothLastMinusOneSecToothTime = 0; + + //toothAngles is reused to store the cam pattern, only used for 6 and 8 cylinder pattern + if (configPage2.nCylinders == 6) { + toothAngles[0] = 1; // Pos 0 is required to be the same as group 6 for easier math + toothAngles[1] = 3; // Group 1 ... + toothAngles[2] = 1; + toothAngles[3] = 2; + toothAngles[4] = 3; + toothAngles[5] = 2; + toothAngles[6] = 1; + toothAngles[7] = 3; // Pos 7 is required to be the same as group 1 for easier math + } + else if (configPage2.nCylinders == 8) { + toothAngles[0] = 3; // Pos 0 is required to be the same as group 8 for easier math + toothAngles[1] = 1; // Group 1 ... + toothAngles[2] = 1; + toothAngles[3] = 2; + toothAngles[4] = 3; + toothAngles[5] = 2; + toothAngles[6] = 2; + toothAngles[7] = 1; + toothAngles[8] = 3; + toothAngles[9] = 1; // Pos 9 is required to be the same as group 1 for easier math + } } void triggerPri_NGC() { @@ -4222,11 +4254,10 @@ void triggerPri_NGC() { { toothCurrentCount++; validTrigger = true; + bool isMissingTooth = false; 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 @@ -4256,13 +4287,19 @@ void triggerPri_NGC() { 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) ) + if ( + ( configPage2.nCylinders == 4 && ( (toothCurrentCount == 1 && (secondaryToothCount == 1 || secondaryToothCount == 2) ) || (toothCurrentCount == 19 && secondaryToothCount == 4) ) ) || + ( configPage2.nCylinders == 6 && ( (toothCurrentCount == 1 && (toothSystemCount == 1 || toothSystemCount == 2) ) || (toothCurrentCount == 19 && (toothSystemCount == 2 || toothSystemCount == 3) ) ) ) || + ( configPage2.nCylinders == 8 && ( (toothCurrentCount == 1 && (toothSystemCount == 1 || toothSystemCount == 2) ) || (toothCurrentCount == 19 && (toothSystemCount == 3 || toothSystemCount == 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) ) + else if ( + ( configPage2.nCylinders == 4 && ( (toothCurrentCount == 1 && secondaryToothCount == 5) || (toothCurrentCount == 19 && secondaryToothCount == 7) ) ) || + ( configPage2.nCylinders == 6 && ( (toothCurrentCount == 1 && (toothSystemCount == 4 || toothSystemCount == 5) ) || (toothCurrentCount == 19 && (toothSystemCount == 5 || toothSystemCount == 6) ) ) ) || + ( configPage2.nCylinders == 8 && ( (toothCurrentCount == 1 && (toothSystemCount == 5 || toothSystemCount == 6) ) || (toothCurrentCount == 19 && (toothSystemCount == 7 || toothSystemCount == 8) ) ) ) ) { revolutionOne = true; currentStatus.hasSync = true; @@ -4272,7 +4309,8 @@ void triggerPri_NGC() { 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. + 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 @@ -4293,12 +4331,16 @@ void triggerPri_NGC() { } } - toothLastMinusOneToothTime = toothLastToothTime; + if (isMissingTooth == true) { // If we have a missing tooth, copy the gap from the previous tooth as that is the correct normal tooth length + toothLastMinusOneToothTime = curTime - (toothLastToothTime - toothLastMinusOneToothTime); + } + else { + toothLastMinusOneToothTime = toothLastToothTime; + } toothLastToothTime = curTime; - } - //NEW IGNITION MODE - if( (configPage2.perToothIgn == true) && (BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK) == false) ) + //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); @@ -4308,6 +4350,7 @@ void triggerPri_NGC() { checkPerToothTiming(crankAngle, (configPage4.triggerTeeth + toothCurrentCount)); } else{ checkPerToothTiming(crankAngle, toothCurrentCount); } + } } } @@ -4361,6 +4404,63 @@ void triggerSec_NGC4() } } +#define secondaryToothLastCount checkSyncToothCount + +void triggerSec_NGC68() +{ + //Only check the cam wheel for sequential operation + if( configPage4.sparkMode != IGN_MODE_SEQUENTIAL && configPage2.injLayout != INJ_SEQUENTIAL ) { + return; + } + + curTime2 = micros(); + + curGap2 = curTime2 - toothLastSecToothTime; + + if ( curGap2 > triggerSecFilterTime ) + { + if ( toothLastSecToothTime > 0 && toothLastToothTime > 0 && toothLastMinusOneToothTime > 0 ) //Make sure we have enough tooth information to calculate tooth lengths + { + /* Cam wheel can have a single tooth in a group which can screw up the "targetgap" calculations + Instead use primary wheel tooth gap as comparison as those values are always correct. 2.1 primary teeth are the same duration as one secondary tooth. */ + if (curGap2 >= (3 * (toothLastToothTime - toothLastMinusOneToothTime) ) ) // Check if we have a bigger gap, that is missing teeth + { + //toothSystemCount > 0 means we have cam sync and identifies which group we have synced with + //toothAngles is reused to store the cam pattern + if (secondaryToothCount > 0 && secondaryToothLastCount > 0) { // Only check for cam sync if we have actually detected two groups and can get cam sync + if (toothSystemCount > 0 && secondaryToothCount == (unsigned int)toothAngles[toothSystemCount+1]) { // Do a quick check if we already have cam sync + toothSystemCount++; + if (toothSystemCount > configPage2.nCylinders) { toothSystemCount = 1; } + } + else { // Check for a pair of matching groups which tells us which group we are at, this should only happen when we don't have cam sync + toothSystemCount = 0; // We either haven't got cam sync yet or we lost cam sync + for (byte group = 1; group <= configPage2.nCylinders; group++) { + if (secondaryToothCount == (unsigned int)toothAngles[group] && secondaryToothLastCount == (byte)toothAngles[group-1] ) { // Find a matching pattern/position + toothSystemCount = group; + break; + } + } + } + } + + secondaryToothLastCount = secondaryToothCount; + //This is the first tooth in this group + secondaryToothCount = 1; + + 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) { + //Normal tooth + secondaryToothCount++; + triggerSecFilterTime = curGap2 >> 2; //Set filter at 25% of the current speed + } + } + + toothLastSecToothTime = curTime2; + } +} + uint16_t getRPM_NGC() { uint16_t tempRPM = 0; @@ -4421,6 +4521,50 @@ void triggerSetEndTeeth_NGC() else if(tempIgnition4EndTooth > 70) { tempIgnition4EndTooth = 70; } // These are missing teeth, so set the next one before instead ignition4EndTooth = tempIgnition4EndTooth; + #if IGN_CHANNELS >= 6 + int16_t tempIgnition5EndTooth; + tempIgnition5EndTooth = ( (ignition5EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition + if(tempIgnition5EndTooth < 1) { tempIgnition5EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 + else if(tempIgnition5EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition5EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count + if(tempIgnition5EndTooth == 17 || tempIgnition5EndTooth == 18) { tempIgnition5EndTooth = 16; } // These are missing teeth, so set the next one before instead + else if(tempIgnition5EndTooth == 35 || tempIgnition5EndTooth == 36) { tempIgnition5EndTooth = 34; } // These are missing teeth, so set the next one before instead + else if(tempIgnition5EndTooth == 53 || tempIgnition5EndTooth == 54) { tempIgnition5EndTooth = 52; } // These are missing teeth, so set the next one before instead + else if(tempIgnition5EndTooth > 70) { tempIgnition5EndTooth = 70; } // These are missing teeth, so set the next one before instead + ignition5EndTooth = tempIgnition5EndTooth; + + int16_t tempIgnition6EndTooth; + tempIgnition6EndTooth = ( (ignition6EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition + if(tempIgnition6EndTooth < 1) { tempIgnition6EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 + else if(tempIgnition6EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition6EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count + if(tempIgnition6EndTooth == 17 || tempIgnition6EndTooth == 18) { tempIgnition6EndTooth = 16; } // These are missing teeth, so set the next one before instead + else if(tempIgnition6EndTooth == 35 || tempIgnition6EndTooth == 36) { tempIgnition6EndTooth = 34; } // These are missing teeth, so set the next one before instead + else if(tempIgnition6EndTooth == 53 || tempIgnition6EndTooth == 54) { tempIgnition6EndTooth = 52; } // These are missing teeth, so set the next one before instead + else if(tempIgnition6EndTooth > 70) { tempIgnition6EndTooth = 70; } // These are missing teeth, so set the next one before instead + ignition6EndTooth = tempIgnition6EndTooth; + #endif + + #if IGN_CHANNELS >= 8 + int16_t tempIgnition7EndTooth; + tempIgnition7EndTooth = ( (ignition7EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition + if(tempIgnition7EndTooth < 1) { tempIgnition7EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 + else if(tempIgnition7EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition7EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count + if(tempIgnition7EndTooth == 17 || tempIgnition7EndTooth == 18) { tempIgnition7EndTooth = 16; } // These are missing teeth, so set the next one before instead + else if(tempIgnition7EndTooth == 35 || tempIgnition7EndTooth == 36) { tempIgnition7EndTooth = 34; } // These are missing teeth, so set the next one before instead + else if(tempIgnition7EndTooth == 53 || tempIgnition7EndTooth == 54) { tempIgnition7EndTooth = 52; } // These are missing teeth, so set the next one before instead + else if(tempIgnition7EndTooth > 70) { tempIgnition7EndTooth = 70; } // These are missing teeth, so set the next one before instead + ignition7EndTooth = tempIgnition7EndTooth; + + int16_t tempIgnition8EndTooth; + tempIgnition8EndTooth = ( (ignition8EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1; // Which tooth is the last tooth before ignition + if(tempIgnition8EndTooth < 1) { tempIgnition8EndTooth += (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth below 1 + else if(tempIgnition8EndTooth > (configPage4.triggerTeeth + toothAdder)) { tempIgnition8EndTooth -= (configPage4.triggerTeeth + toothAdder); } // Wrap around teeth above max tooth count + if(tempIgnition8EndTooth == 17 || tempIgnition8EndTooth == 18) { tempIgnition8EndTooth = 16; } // These are missing teeth, so set the next one before instead + else if(tempIgnition8EndTooth == 35 || tempIgnition8EndTooth == 36) { tempIgnition8EndTooth = 34; } // These are missing teeth, so set the next one before instead + else if(tempIgnition8EndTooth == 53 || tempIgnition8EndTooth == 54) { tempIgnition8EndTooth = 52; } // These are missing teeth, so set the next one before instead + else if(tempIgnition8EndTooth > 70) { tempIgnition8EndTooth = 70; } // These are missing teeth, so set the next one before instead + ignition8EndTooth = tempIgnition8EndTooth; + #endif + lastToothCalcAdvance = currentStatus.advance; } /** @} */ \ No newline at end of file diff --git a/speeduino/init.ino b/speeduino/init.ino index 5aa454e0..7d574540 100644 --- a/speeduino/init.ino +++ b/speeduino/init.ino @@ -3235,17 +3235,23 @@ void initialiseTriggers() break; case DECODER_NGC: - //Chrysler NGC 4 cylinder + //Chrysler NGC - 4, 6 and 8 cylinder triggerSetup_NGC(); triggerHandler = triggerPri_NGC; - triggerSecondaryHandler = triggerSec_NGC4; decoderHasSecondary = true; getRPM = getRPM_NGC; getCrankAngle = getCrankAngle_missingTooth; triggerSetEndTeeth = triggerSetEndTeeth_NGC; primaryTriggerEdge = CHANGE; - secondaryTriggerEdge = CHANGE; + if (configPage2.nCylinders == 4) { + triggerSecondaryHandler = triggerSec_NGC4; + secondaryTriggerEdge = CHANGE; + } + else { + triggerSecondaryHandler = triggerSec_NGC68; + secondaryTriggerEdge = FALLING; + } attachInterrupt(triggerInterrupt, triggerHandler, primaryTriggerEdge); attachInterrupt(triggerInterrupt2, triggerSecondaryHandler, secondaryTriggerEdge);