diff --git a/speeduino/crankMaths.h b/speeduino/crankMaths.h index c6537b33..5073de83 100644 --- a/speeduino/crankMaths.h +++ b/speeduino/crankMaths.h @@ -1,7 +1,13 @@ -#define CRANKMATH_METHOD_INTERVAL_RPM 0 -#define CRANKMATH_METHOD_INTERVAL_TOOTH 1 -#define CRANKMATH_METHOD_ALPHA_BETA 2 -#define CRANKMATH_METHOD_2ND_DERIVATIVE 3 +#define CRANKMATH_METHOD_INTERVAL_DEFAULT 0 +#define CRANKMATH_METHOD_INTERVAL_REV 1 +#define CRANKMATH_METHOD_INTERVAL_TOOTH 2 +#define CRANKMATH_METHOD_ALPHA_BETA 3 +#define CRANKMATH_METHOD_2ND_DERIVATIVE 4 -unsigned long angleToTime(int16_t angle); -uint16_t timeToAngle(unsigned long time); \ No newline at end of file +#define fastDegreesToUS(degrees) (degrees * (unsigned long)timePerDegree) + +unsigned long angleToTime(int16_t angle, byte method); +uint16_t timeToAngle(unsigned long time, byte method); + +volatile int timePerDegree; +volatile uint16_t degreesPeruSx2048; \ No newline at end of file diff --git a/speeduino/crankMaths.ino b/speeduino/crankMaths.ino index a9b46a27..6490b52e 100644 --- a/speeduino/crankMaths.ino +++ b/speeduino/crankMaths.ino @@ -4,40 +4,64 @@ * * Future angle calculations will use a predicted speed/acceleration * * Past angle calculations will use the known speed * -* Multiple prediction methods will be implemented here for testing: -* * Last interval using both last full revolution and gap between last teeth -* * 2nd derivative prediction (Speed + acceleration) -* * Closed loop error correction (Alpha-beta filter). +* Currently 4 methods are planned and/or available: +* 1) Last interval based on a full revolution +* 2) Last interval based on the time between the last 2 teeth (Crank Pattern dependant) +* 3) Closed loop error correction (Alpha-beta filter) +* 4) 2nd derivative prediction (Speed + acceleration) */ -#define fastDegreesToUS(degrees) (degrees * (unsigned long)timePerDegree) -unsigned long angleToTime(int16_t angle) +unsigned long angleToTime(int16_t angle, byte method) { -//#define degreesToUS(degrees) (decoderIsLowRes == true ) ? ((degrees * 166666UL) / currentStatus.RPM) : (degrees * (unsigned long)timePerDegree) -//#define degreesToUS(degrees) ((degrees * revolutionTime) / 360) -//Fast version of divide by 360: -//#define degreesToUS(degrees) ((degrees * revolutionTime * 3054198967ULL) >> 40) -//#define degreesToUS(degrees) (degrees * (unsigned long)timePerDegree) - return ((angle * revolutionTime) / 360); + unsigned long returnTime = 0; + + if(method == CRANKMATH_METHOD_INTERVAL_REV || method == CRANKMATH_METHOD_INTERVAL_DEFAULT ) + { + returnTime = ((angle * revolutionTime) / 360); + //returnTime = angle * (unsigned long)timePerDegree; + } + + return returnTime; } /* * Convert a time (uS) into an angle at current speed +* Currently 4 methods are planned and/or available: +* 1) Last interval based on a full revolution +* 2) Last interval based on the time between the last 2 teeth (Crank Pattern dependant) +* 3) Closed loop error correction (Alpha-beta filter) +* 4) 2nd derivative prediction (Speed + acceleration) */ -uint16_t timeToAngle(unsigned long time) +uint16_t timeToAngle(unsigned long time, byte method) { + uint16_t returnAngle = 0; -//#define uSToDegrees(time) (((unsigned long)time * currentStatus.RPM) / 166666) -//Crazy magic numbers method from Hackers delight (www.hackersdelight.org/magic.htm): -//#define uSToDegrees(time) ( (((uint64_t)time * currentStatus.RPM * 211107077ULL) >> 45) ) - return (((unsigned long)time * currentStatus.RPM) / 166666); - -/* - switch(calculationAlgorithm) + if(method == CRANKMATH_METHOD_INTERVAL_REV || method == CRANKMATH_METHOD_INTERVAL_DEFAULT ) { - case CRANKMATH_METHOD_INTERVAL_TOOTH: - returnValue = ((elapsedTime * triggerToothAngle) / toothTime); + //A last interval method of calculating angle that does not take into account any acceleration. The interval used is the time taken to complete the last full revolution + //degreesPeruSx2048 is the number of degrees the crank moves per uS. This value is almost always <1uS, so it is multiplied by 2048. This allows an angle calcuation with only a multiply and a bitshift without any appreciable drop in accuracy + returnAngle = (time * degreesPeruSx2048) / 2048; //Divide by 2048 will be converted at compile time to bitshift } - */ + else if (method == CRANKMATH_METHOD_INTERVAL_TOOTH) + { + //Still uses a last interval method (ie retrospective), but bases the interval on the gap between the 2 most recent teeth rather than the last full revolution + if(triggerToothAngleIsCorrect == true) + { + returnAngle = ( (unsigned long)(time * triggerToothAngle) / (toothLastToothTime - toothLastMinusOneToothTime) ); + } + else { returnAngle = timeToAngle(time, CRANKMATH_METHOD_INTERVAL_REV); } //Safety check. This can occur if the last tooth seen was outside the normal pattern etc + } + else if (method == CRANKMATH_METHOD_ALPHA_BETA) + { + //Not yet implemented. Default to Rev + returnAngle = timeToAngle(time, CRANKMATH_METHOD_INTERVAL_REV); + } + else if (method == CRANKMATH_METHOD_2ND_DERIVATIVE) + { + //Not yet implemented. Default to Rev + returnAngle = timeToAngle(time, CRANKMATH_METHOD_INTERVAL_REV); + } + + return returnAngle; } \ No newline at end of file diff --git a/speeduino/decoders.h b/speeduino/decoders.h index 80cf66fb..e5e924ff 100644 --- a/speeduino/decoders.h +++ b/speeduino/decoders.h @@ -21,12 +21,12 @@ void triggerSetup_missingTooth(); void triggerPri_missingTooth(); void triggerSec_missingTooth(); uint16_t getRPM_missingTooth(); -int getCrankAngle_missingTooth(int timePerDegree); +int getCrankAngle_missingTooth(); void triggerSetup_DualWheel(); void triggerPri_DualWheel(); void triggerSec_DualWheel(); uint16_t getRPM_DualWheel(); -int getCrankAngle_DualWheel(int timePerDegree); +int getCrankAngle_DualWheel(); unsigned long MAX_STALL_TIME = 500000UL; //The maximum time (in uS) that the system will continue to function before the engine is considered stalled/stopped. This is unique to each decoder, depending on the number of teeth etc. 500000 (half a second) is used as the default value, most decoders will be much less. @@ -69,6 +69,8 @@ bool decoderIsSequential; //Whether or not the decoder supports sequential opera bool decoderIsLowRes = false; //Is set true, certain extra calculations are performed for better timing accuracy bool decoderHasFixedCrankingTiming = false; //Whether or not the decoder supports fixed cranking timing byte checkSyncToothCount; //How many teeth must've been seen on this revolution before we try to confirm sync (Useful for missing tooth type decoders) +unsigned long elapsedTime; +unsigned long lastCrankAngleCalc; int16_t ignition1EndTooth = 0; int16_t ignition2EndTooth = 0; diff --git a/speeduino/decoders.ino b/speeduino/decoders.ino index 14c8067c..bf0cf5df 100644 --- a/speeduino/decoders.ino +++ b/speeduino/decoders.ino @@ -251,7 +251,7 @@ uint16_t getRPM_missingTooth() return tempRPM; } -int getCrankAngle_missingTooth(int timePerDegree) +int getCrankAngle_missingTooth() { //This is the current angle ATDC the engine is at. This is the last known position based on what tooth was last 'seen'. It is only accurate to the resolution of the trigger wheel (Eg 36-1 is 10 degrees) unsigned long tempToothLastToothTime; @@ -260,23 +260,20 @@ int getCrankAngle_missingTooth(int timePerDegree) //Grab some variables that are used in the trigger code and assign them to temp variables. noInterrupts(); tempToothCurrentCount = toothCurrentCount; - tempToothLastToothTime = toothLastToothTime; tempRevolutionOne = revolutionOne; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + tempToothLastToothTime = toothLastToothTime; + //elapsedTime = (micros() - toothLastToothTime); //micros() is no longer interrupt safe interrupts(); int crankAngle = ((tempToothCurrentCount - 1) * triggerToothAngle) + configPage4.triggerAngle; //Number of teeth that have passed since tooth 1, multiplied by the angle each tooth represents, plus the angle that tooth 1 is ATDC. This gives accuracy only to the nearest tooth. - //Estimate the number of degrees travelled since the last tooth} - - - //crankAngle += DIV_ROUND_CLOSEST(elapsedTime, timePerDegree); - if(elapsedTime < SHRT_MAX ) { crankAngle += div((int)elapsedTime, timePerDegree).quot; } //This option is much faster, but only available for smaller values of elapsedTime - else { crankAngle += ldiv(elapsedTime, timePerDegree).quot; } - //crankAngle += uSToDegrees(elapsedTime); - + //Sequential check (simply sets whether we're on the first or 2nd revoltuion of the cycle) if ( (tempRevolutionOne == true) && (configPage4.TrigSpeed == 0) ) { crankAngle += 360; } + lastCrankAngleCalc = micros(); + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); + if (crankAngle >= 720) { crankAngle -= 720; } else if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } if (crankAngle < 0) { crankAngle += CRANK_ANGLE_MAX; } @@ -400,7 +397,7 @@ uint16_t getRPM_DualWheel() return tempRPM; } -int getCrankAngle_DualWheel(int timePerDegree) +int getCrankAngle_DualWheel() { //This is the current angle ATDC the engine is at. This is the last known position based on what tooth was last 'seen'. It is only accurate to the resolution of the trigger wheel (Eg 36-1 is 10 degrees) unsigned long tempToothLastToothTime; @@ -411,16 +408,16 @@ int getCrankAngle_DualWheel(int timePerDegree) tempToothCurrentCount = toothCurrentCount; tempToothLastToothTime = toothLastToothTime; tempRevolutionOne = revolutionOne; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc = micros(); interrupts(); //Handle case where the secondary tooth was the last one seen if(tempToothCurrentCount == 0) { tempToothCurrentCount = configPage4.triggerTeeth; } int crankAngle = ((tempToothCurrentCount - 1) * triggerToothAngle) + configPage4.triggerAngle; //Number of teeth that have passed since tooth 1, multiplied by the angle each tooth represents, plus the angle that tooth 1 is ATDC. This gives accuracy only to the nearest tooth. - //Estimate the number of degrees travelled since the last tooth} - if(elapsedTime < SHRT_MAX ) { crankAngle += div((int)elapsedTime, timePerDegree).quot; } //This option is much faster, but only available for smaller values of elapsedTime - else { crankAngle += ldiv(elapsedTime, timePerDegree).quot; } + + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); //Sequential check (simply sets whether we're on the first or 2nd revoltuion of the cycle) if (tempRevolutionOne) { crankAngle += 360; } @@ -543,7 +540,7 @@ uint16_t getRPM_BasicDistributor() return tempRPM; } -int getCrankAngle_BasicDistributor(int timePerDegree) +int getCrankAngle_BasicDistributor() { //This is the current angle ATDC the engine is at. This is the last known position based on what tooth was last 'seen'. It is only accurate to the resolution of the trigger wheel (Eg 36-1 is 10 degrees) unsigned long tempToothLastToothTime; @@ -552,13 +549,14 @@ int getCrankAngle_BasicDistributor(int timePerDegree) noInterrupts(); tempToothCurrentCount = toothCurrentCount; tempToothLastToothTime = toothLastToothTime; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc= micros(); //micros() is no longer interrupt safe interrupts(); int crankAngle = ((tempToothCurrentCount - 1) * triggerToothAngle) + configPage4.triggerAngle; //Number of teeth that have passed since tooth 1, multiplied by the angle each tooth represents, plus the angle that tooth 1 is ATDC. This gives accuracy only to the nearest tooth. + //Estimate the number of degrees travelled since the last tooth} - if(elapsedTime < SHRT_MAX ) { crankAngle += div((int)elapsedTime, timePerDegree).quot; } //This option is much faster, but only available for smaller values of elapsedTime - else { crankAngle += ldiv(elapsedTime, timePerDegree).quot; } + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -628,7 +626,7 @@ uint16_t getRPM_GM7X() { return stdGetRPM(360); } -int getCrankAngle_GM7X(int timePerDegree) +int getCrankAngle_GM7X() { //This is the current angle ATDC the engine is at. This is the last known position based on what tooth was last 'seen'. It is only accurate to the resolution of the trigger wheel (Eg 36-1 is 10 degrees) unsigned long tempToothLastToothTime; @@ -637,7 +635,7 @@ int getCrankAngle_GM7X(int timePerDegree) noInterrupts(); tempToothCurrentCount = toothCurrentCount; tempToothLastToothTime = toothLastToothTime; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe interrupts(); //Check if the last tooth seen was the reference tooth (Number 3). All others can be calculated, but tooth 3 has a unique angle @@ -656,8 +654,8 @@ int getCrankAngle_GM7X(int timePerDegree) } //Estimate the number of degrees travelled since the last tooth} - if(elapsedTime < SHRT_MAX ) { crankAngle += div((int)elapsedTime, timePerDegree).quot; } //This option is much faster, but only available for smaller values of elapsedTime - else { crankAngle += ldiv(elapsedTime, timePerDegree).quot; } + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -1043,29 +1041,26 @@ uint16_t getRPM_4G63() return tempRPM; } -int getCrankAngle_4G63(int timePerDegree) +int getCrankAngle_4G63() { int crankAngle = 0; if(currentStatus.hasSync == true) { //This is the current angle ATDC the engine is at. This is the last known position based on what tooth was last 'seen'. It is only accurate to the resolution of the trigger wheel (Eg 36-1 is 10 degrees) - unsigned long tempToothLastToothTime, tempToothLastMinusOneToothTime; + unsigned long tempToothLastToothTime; int tempToothCurrentCount; //Grab some variables that are used in the trigger code and assign them to temp variables. noInterrupts(); tempToothCurrentCount = toothCurrentCount; tempToothLastToothTime = toothLastToothTime; - tempToothLastMinusOneToothTime = toothLastMinusOneToothTime; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe interrupts(); crankAngle = toothAngles[(tempToothCurrentCount - 1)] + configPage4.triggerAngle; //Perform a lookup of the fixed toothAngles array to find what the angle of the last tooth passed was. //Estimate the number of degrees travelled since the last tooth} - //crankAngle += uSToDegrees(elapsedTime); - unsigned long toothTime = tempToothLastToothTime - tempToothLastMinusOneToothTime; - crankAngle += int((elapsedTime * triggerToothAngle) / toothTime); - //timePerDegree = toothTime / tempToothAngle; + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_TOOTH); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -1165,7 +1160,7 @@ uint16_t getRPM_24X() { return stdGetRPM(360); } -int getCrankAngle_24X(int timePerDegree) +int getCrankAngle_24X() { //This is the current angle ATDC the engine is at. This is the last known position based on what tooth was last 'seen'. It is only accurate to the resolution of the trigger wheel (Eg 36-1 is 10 degrees) unsigned long tempToothLastToothTime; @@ -1175,7 +1170,7 @@ int getCrankAngle_24X(int timePerDegree) tempToothCurrentCount = toothCurrentCount; tempToothLastToothTime = toothLastToothTime; tempRevolutionOne = revolutionOne; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe interrupts(); int crankAngle; @@ -1183,8 +1178,8 @@ int getCrankAngle_24X(int timePerDegree) else { crankAngle = toothAngles[(tempToothCurrentCount - 1)] + configPage4.triggerAngle;} //Perform a lookup of the fixed toothAngles array to find what the angle of the last tooth passed was. //Estimate the number of degrees travelled since the last tooth} - if(elapsedTime < SHRT_MAX ) { crankAngle += div((int)elapsedTime, timePerDegree).quot; } //This option is much faster, but only available for smaller values of elapsedTime - else { crankAngle += ldiv(elapsedTime, timePerDegree).quot; } + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); //Sequential check (simply sets whether we're on the first or 2nd revoltuion of the cycle) if (tempRevolutionOne == 1) { crankAngle += 360; } @@ -1271,7 +1266,7 @@ uint16_t getRPM_Jeep2000() { return stdGetRPM(360); } -int getCrankAngle_Jeep2000(int timePerDegree) +int getCrankAngle_Jeep2000() { //This is the current angle ATDC the engine is at. This is the last known position based on what tooth was last 'seen'. It is only accurate to the resolution of the trigger wheel (Eg 36-1 is 10 degrees) unsigned long tempToothLastToothTime; @@ -1280,7 +1275,7 @@ int getCrankAngle_Jeep2000(int timePerDegree) noInterrupts(); tempToothCurrentCount = toothCurrentCount; tempToothLastToothTime = toothLastToothTime; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe interrupts(); int crankAngle; @@ -1288,8 +1283,8 @@ int getCrankAngle_Jeep2000(int timePerDegree) else { crankAngle = toothAngles[(tempToothCurrentCount - 1)] + configPage4.triggerAngle;} //Perform a lookup of the fixed toothAngles array to find what the angle of the last tooth passed was. //Estimate the number of degrees travelled since the last tooth} - if(elapsedTime < SHRT_MAX ) { crankAngle += div((int)elapsedTime, timePerDegree).quot; } //This option is much faster, but only available for smaller values of elapsedTime - else { crankAngle += ldiv(elapsedTime, timePerDegree).quot; } + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -1384,7 +1379,7 @@ uint16_t getRPM_Audi135() return stdGetRPM(360); } -int getCrankAngle_Audi135(int timePerDegree) +int getCrankAngle_Audi135() { //This is the current angle ATDC the engine is at. This is the last known position based on what tooth was last 'seen'. It is only accurate to the resolution of the trigger wheel (Eg 36-1 is 10 degrees) unsigned long tempToothLastToothTime; @@ -1395,16 +1390,17 @@ int getCrankAngle_Audi135(int timePerDegree) tempToothCurrentCount = toothCurrentCount; tempToothLastToothTime = toothLastToothTime; tempRevolutionOne = revolutionOne; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe interrupts(); //Handle case where the secondary tooth was the last one seen if(tempToothCurrentCount == 0) { tempToothCurrentCount = 45; } int crankAngle = ((tempToothCurrentCount - 1) * triggerToothAngle) + configPage4.triggerAngle; //Number of teeth that have passed since tooth 1, multiplied by the angle each tooth represents, plus the angle that tooth 1 is ATDC. This gives accuracy only to the nearest tooth. + //Estimate the number of degrees travelled since the last tooth} - if(elapsedTime < SHRT_MAX ) { crankAngle += div((int)elapsedTime, timePerDegree).quot; } //This option is much faster, but only available for smaller values of elapsedTime - else { crankAngle += ldiv(elapsedTime, timePerDegree).quot; } + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); //Sequential check (simply sets whether we're on the first or 2nd revoltuion of the cycle) if (tempRevolutionOne) { crankAngle += 360; } @@ -1480,7 +1476,7 @@ uint16_t getRPM_HondaD17() { return stdGetRPM(360); } -int getCrankAngle_HondaD17(int timePerDegree) +int getCrankAngle_HondaD17() { //This is the current angle ATDC the engine is at. This is the last known position based on what tooth was last 'seen'. It is only accurate to the resolution of the trigger wheel (Eg 36-1 is 10 degrees) unsigned long tempToothLastToothTime; @@ -1489,7 +1485,7 @@ int getCrankAngle_HondaD17(int timePerDegree) noInterrupts(); tempToothCurrentCount = toothCurrentCount; tempToothLastToothTime = toothLastToothTime; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe interrupts(); //Check if the last tooth seen was the reference tooth 13 (Number 0 here). All others can be calculated, but tooth 3 has a unique angle @@ -1504,8 +1500,8 @@ int getCrankAngle_HondaD17(int timePerDegree) } //Estimate the number of degrees travelled since the last tooth} - if(elapsedTime < SHRT_MAX ) { crankAngle += div((int)elapsedTime, timePerDegree).quot; } //This option is much faster, but only available for smaller values of elapsedTime - else { crankAngle += ldiv(elapsedTime, timePerDegree).quot; } + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -1691,7 +1687,7 @@ uint16_t getRPM_Miata9905() return tempRPM; } -int getCrankAngle_Miata9905(int timePerDegree) +int getCrankAngle_Miata9905() { int crankAngle = 0; //if(currentStatus.hasSync == true) @@ -1703,13 +1699,14 @@ int getCrankAngle_Miata9905(int timePerDegree) noInterrupts(); tempToothCurrentCount = toothCurrentCount; tempToothLastToothTime = toothLastToothTime; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe interrupts(); crankAngle = toothAngles[(tempToothCurrentCount - 1)] + configPage4.triggerAngle; //Perform a lookup of the fixed toothAngles array to find what the angle of the last tooth passed was. //Estimate the number of degrees travelled since the last tooth} - crankAngle += timeToAngle(elapsedTime); + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -1844,7 +1841,7 @@ uint16_t getRPM_MazdaAU() return tempRPM; } -int getCrankAngle_MazdaAU(int timePerDegree) +int getCrankAngle_MazdaAU() { int crankAngle = 0; if(currentStatus.hasSync == true) @@ -1856,14 +1853,14 @@ int getCrankAngle_MazdaAU(int timePerDegree) noInterrupts(); tempToothCurrentCount = toothCurrentCount; tempToothLastToothTime = toothLastToothTime; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe interrupts(); crankAngle = toothAngles[(tempToothCurrentCount - 1)] + configPage4.triggerAngle; //Perform a lookup of the fixed toothAngles array to find what the angle of the last tooth passed was. //Estimate the number of degrees travelled since the last tooth} - if(elapsedTime < SHRT_MAX ) { crankAngle += div((int)elapsedTime, timePerDegree).quot; } //This option is much faster, but only available for smaller values of elapsedTime - else { crankAngle += ldiv(elapsedTime, timePerDegree).quot; } + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -1916,7 +1913,7 @@ uint16_t getRPM_non360() return tempRPM; } -int getCrankAngle_non360(int timePerDegree) +int getCrankAngle_non360() { //This is the current angle ATDC the engine is at. This is the last known position based on what tooth was last 'seen'. It is only accurate to the resolution of the trigger wheel (Eg 36-1 is 10 degrees) unsigned long tempToothLastToothTime; @@ -1925,7 +1922,7 @@ int getCrankAngle_non360(int timePerDegree) noInterrupts(); tempToothCurrentCount = toothCurrentCount; tempToothLastToothTime = toothLastToothTime; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe interrupts(); //Handle case where the secondary tooth was the last one seen @@ -1936,8 +1933,8 @@ int getCrankAngle_non360(int timePerDegree) crankAngle = (crankAngle / configPage4.TrigAngMul) + configPage4.triggerAngle; //Have to divide by the multiplier to get back to actual crank angle. //Estimate the number of degrees travelled since the last tooth} - if(elapsedTime < SHRT_MAX ) { crankAngle += div((int)elapsedTime, timePerDegree).quot; } //This option is much faster, but only available for smaller values of elapsedTime - else { crankAngle += ldiv(elapsedTime, timePerDegree).quot; } + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -2120,7 +2117,7 @@ uint16_t getRPM_Nissan360() return tempRPM; } -int getCrankAngle_Nissan360(int timePerDegree) +int getCrankAngle_Nissan360() { //As each tooth represents 2 crank degrees, we only need to determine whether we're more or less than halfway between teeth to know whether to add another 1 degrees int crankAngle = 0; @@ -2132,7 +2129,7 @@ int getCrankAngle_Nissan360(int timePerDegree) tempToothLastToothTime = toothLastToothTime; tempToothLastMinusOneToothTime = toothLastMinusOneToothTime; tempToothCurrentCount = toothCurrentCount; - unsigned long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe interrupts(); crankAngle = ( (tempToothCurrentCount - 1) * 2) + configPage4.triggerAngle; @@ -2284,7 +2281,7 @@ uint16_t getRPM_Subaru67() return tempRPM; } -int getCrankAngle_Subaru67(int timePerDegree) +int getCrankAngle_Subaru67() { int crankAngle = 0; if( currentStatus.hasSync == true ) @@ -2296,14 +2293,14 @@ int getCrankAngle_Subaru67(int timePerDegree) noInterrupts(); tempToothCurrentCount = toothCurrentCount; tempToothLastToothTime = toothLastToothTime; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe interrupts(); crankAngle = toothAngles[(tempToothCurrentCount - 1)] + configPage4.triggerAngle; //Perform a lookup of the fixed toothAngles array to find what the angle of the last tooth passed was. //Estimate the number of degrees travelled since the last tooth} - if(elapsedTime < SHRT_MAX ) { crankAngle += div((int)elapsedTime, timePerDegree).quot; } //This option is much faster, but only available for smaller values of elapsedTime - else { crankAngle += ldiv(elapsedTime, timePerDegree).quot; } + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -2450,7 +2447,7 @@ uint16_t getRPM_Daihatsu() return tempRPM; } -int getCrankAngle_Daihatsu(int timePerDegree) +int getCrankAngle_Daihatsu() { //This is the current angle ATDC the engine is at. This is the last known position based on what tooth was last 'seen'. It is only accurate to the resolution of the trigger wheel (Eg 36-1 is 10 degrees) unsigned long tempToothLastToothTime; @@ -2460,14 +2457,14 @@ int getCrankAngle_Daihatsu(int timePerDegree) noInterrupts(); tempToothCurrentCount = toothCurrentCount; tempToothLastToothTime = toothLastToothTime; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe interrupts(); crankAngle = toothAngles[tempToothCurrentCount-1] + configPage4.triggerAngle; //Crank angle of the last tooth seen //Estimate the number of degrees travelled since the last tooth} - if(elapsedTime < SHRT_MAX ) { crankAngle += div((int)elapsedTime, timePerDegree).quot; } //This option is much faster, but only available for smaller values of elapsedTime - else { crankAngle += ldiv(elapsedTime, timePerDegree).quot; } + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -2581,7 +2578,7 @@ uint16_t getRPM_Harley() } -int getCrankAngle_Harley(int timePerDegree) +int getCrankAngle_Harley() { //This is the current angle ATDC the engine is at. This is the last known position based on what tooth was last 'seen'. It is only accurate to the resolution of the trigger wheel (Eg 36-1 is 10 degrees) unsigned long tempToothLastToothTime; @@ -2590,7 +2587,7 @@ int getCrankAngle_Harley(int timePerDegree) noInterrupts(); tempToothCurrentCount = toothCurrentCount; tempToothLastToothTime = toothLastToothTime; - long elapsedTime = (micros() - tempToothLastToothTime); //micros() is no longer interrupt safe + lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe interrupts(); //Check if the last tooth seen was the reference tooth (Number 3). All others can be calculated, but tooth 3 has a unique angle @@ -2604,10 +2601,8 @@ int getCrankAngle_Harley(int timePerDegree) } //Estimate the number of degrees travelled since the last tooth} - if (elapsedTime < SHRT_MAX ) { - crankAngle += div((int)elapsedTime, timePerDegree).quot; //This option is much faster, but only available for smaller values of elapsedTime - } - else { crankAngle += ldiv(elapsedTime, timePerDegree).quot; } + elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime); + crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV); if (crankAngle >= 720) { crankAngle -= 720; } if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; } @@ -2726,7 +2721,7 @@ void triggerSec_ThirtySixMinus222() //NOT USED - This pattern uses the missing tooth version of this function } -int getCrankAngle_ThirtySixMinus222(int timePerDegree) +int getCrankAngle_ThirtySixMinus222() { //NOT USED - This pattern uses the missing tooth version of this function return 0; diff --git a/speeduino/scheduler.ino b/speeduino/scheduler.ino index e574643e..b6375e84 100644 --- a/speeduino/scheduler.ino +++ b/speeduino/scheduler.ino @@ -642,6 +642,7 @@ void setIgnitionSchedule1(void (*startCallback)(), unsigned long timeout, unsign //Need to check that the timeout doesn't exceed the overflow uint16_t timeout_timer_compare; + timeout -= (micros() - lastCrankAngleCalc); if (timeout > MAX_TIMER_PERIOD) { timeout_timer_compare = uS_TO_TIMER_COMPARE( (MAX_TIMER_PERIOD - 1) ); } // If the timeout is >4x (Each tick represents 4uS) the maximum allowed value of unsigned int (65535), the timer compare value will overflow when appliedcausing erratic behaviour such as erroneous sparking. else { timeout_timer_compare = uS_TO_TIMER_COMPARE(timeout); } //Normal case @@ -663,7 +664,8 @@ static inline void refreshIgnitionSchedule1(unsigned long timeToEnd) //if( (timeToEnd < ignitionSchedule1.duration) && (timeToEnd > IGNITION_REFRESH_THRESHOLD) ) { noInterrupts(); - ignitionSchedule1.endCompare = IGN1_COUNTER + uS_TO_TIMER_COMPARE(timeToEnd); + unsigned long adjustedTimeToEnd = timeToEnd - (micros() - lastCrankAngleCalc); //Take into account any time that has passed since the last crank angle calculation + ignitionSchedule1.endCompare = IGN1_COUNTER + uS_TO_TIMER_COMPARE(adjustedTimeToEnd); IGN1_COMPARE = ignitionSchedule1.endCompare; interrupts(); } @@ -1180,7 +1182,7 @@ static inline void ignitionSchedule1Interrupt() //Most ARM chips can simply call } else if (ignitionSchedule1.Status == STAGED) { - int16_t crankAngle = getCrankAngle(timePerDegree); + int16_t crankAngle = getCrankAngle(); if(crankAngle > CRANK_ANGLE_MAX_IGN) { crankAngle -= CRANK_ANGLE_MAX_IGN; } if(ignition1EndAngle > crankAngle) { diff --git a/speeduino/speeduino.ino b/speeduino/speeduino.ino index fd252b2e..d759d9b3 100644 --- a/speeduino/speeduino.ino +++ b/speeduino/speeduino.ino @@ -70,7 +70,7 @@ bool fuelPumpOn = false; //The current status of the fuel pump void (*trigger)(); //Pointer for the trigger function (Gets pointed to the relevant decoder) void (*triggerSecondary)(); //Pointer for the secondary trigger function (Gets pointed to the relevant decoder) uint16_t (*getRPM)(); //Pointer to the getRPM function (Gets pointed to the relevant decoder) -int (*getCrankAngle)(int); //Pointer to the getCrank Angle function (Gets pointed to the relevant decoder) +int (*getCrankAngle)(); //Pointer to the getCrank Angle function (Gets pointed to the relevant decoder) void (*triggerSetEndTeeth)(); //Pointer to the triggerSetEndTeeth function of each decoder byte cltCalibrationTable[CALIBRATION_TABLE_SIZE]; @@ -141,7 +141,6 @@ void (*ign7EndFunction)(); void (*ign8StartFunction)(); void (*ign8EndFunction)(); -volatile int timePerDegree; byte degreesPerLoop; //The number of crank degrees that pass for each mainloop of the program volatile bool fpPrimed = false; //Tracks whether or not the fuel pump priming has been completed yet bool initialisationComplete = false; //Tracks whether the setup() functino has run completely @@ -1154,10 +1153,12 @@ void loop() { long elapsedTime = (micros_safe() - toothOneTime); //micros() is no longer interrupt safe long rpm_adjust = (elapsedTime * (long)currentStatus.rpmDOT) / 1000000; //Take into account any likely accleration that has occurred since the last full revolution completed + rpm_adjust = 0; timePerDegree = ldiv( 166666L, currentStatus.RPM + rpm_adjust).quot; //There is a small amount of rounding in this calculation, however it is less than 0.001 of a uS (Faster as ldiv than / ) } } + degreesPeruSx2048 = 2048 / timePerDegree; //Check that the duty cycle of the chosen pulsewidth isn't too high. unsigned long pwLimit = percentage(configPage2.dutyLim, revolutionTime); //The pulsewidth limit is determined to be the duty cycle limit (Eg 85%) by the total time it takes to perform 1 revolution @@ -1329,7 +1330,7 @@ void loop() else { currentStatus.dwell = (configPage4.dwellRun * 100); } currentStatus.dwell = correctionsDwell(currentStatus.dwell); - int dwellAngle = timeToAngle(currentStatus.dwell); //Convert the dwell time to dwell angle based on the current engine speed + int dwellAngle = timeToAngle(currentStatus.dwell, CRANKMATH_METHOD_INTERVAL_REV); //Convert the dwell time to dwell angle based on the current engine speed //Calculate start angle for each channel //1 cylinder (Everyone gets this) @@ -1468,10 +1469,26 @@ void loop() //This may potentially be called a number of times as we get closer and closer to the opening time //Determine the current crank angle - int crankAngle = getCrankAngle(timePerDegree); - //if (crankAngle > CRANK_ANGLE_MAX_INJ ) { crankAngle -= 360; } + int crankAngle = getCrankAngle(); if (crankAngle > CRANK_ANGLE_MAX_INJ ) { crankAngle -= CRANK_ANGLE_MAX_INJ; } + if(Serial && false) + { + if(ignition1StartAngle > crankAngle) + { + noInterrupts(); + Serial.print("Time2LastTooth:"); Serial.println(micros()-toothLastToothTime); + Serial.print("elapsedTime:"); Serial.println(elapsedTime); + Serial.print("CurAngle:"); Serial.println(crankAngle); + Serial.print("RPM:"); Serial.println(currentStatus.RPM); + Serial.print("Tooth:"); Serial.println(toothCurrentCount); + Serial.print("timePerDegree:"); Serial.println(timePerDegree); + Serial.print("IGN1Angle:"); Serial.println(ignition1StartAngle); + Serial.print("TimeToIGN1:"); Serial.println(angleToTime((ignition1StartAngle - crankAngle), CRANKMATH_METHOD_INTERVAL_REV)); + interrupts(); + } + } + #if INJ_CHANNELS >= 1 if (fuelOn && !BIT_CHECK(currentStatus.status1, BIT_STATUS1_BOOSTCUT)) { @@ -1665,7 +1682,7 @@ void loop() { //Refresh the current crank angle info //ignition1StartAngle = 335; - crankAngle = getCrankAngle(timePerDegree); //Refresh with the latest crank angle + crankAngle = getCrankAngle(); //Refresh with the latest crank angle if (crankAngle > CRANK_ANGLE_MAX_IGN ) { crankAngle -= 360; } #if IGN_CHANNELS >= 1 @@ -1682,29 +1699,32 @@ void loop() { setIgnitionSchedule1(ign1StartFunction, //((unsigned long)(ignition1StartAngle - crankAngle) * (unsigned long)timePerDegree), - angleToTime((ignition1StartAngle - crankAngle)), + angleToTime((ignition1StartAngle - crankAngle), CRANKMATH_METHOD_INTERVAL_REV), currentStatus.dwell + fixedCrankingOverride, //((unsigned long)((unsigned long)currentStatus.dwell* currentStatus.RPM) / newRPM) + fixedCrankingOverride, ign1EndFunction ); } } #endif - /* + if( (ignitionSchedule1.Status == RUNNING) && (ignition1EndAngle > crankAngle) && configPage4.StgCycles == 0) { unsigned long uSToEnd = 0; - ONLY ONE OF THE BELOW SHOULD BE USED (PROBABLY THE FIRST): - ********* + crankAngle = getCrankAngle(); //Refresh with the latest crank angle + if (crankAngle > CRANK_ANGLE_MAX_IGN ) { crankAngle -= 360; } + + //ONLY ONE OF THE BELOW SHOULD BE USED (PROBABLY THE FIRST): + //********* if(ignition1EndAngle > crankAngle) { uSToEnd = fastDegreesToUS( (ignition1EndAngle - crankAngle) ); } else { uSToEnd = fastDegreesToUS( (360 + ignition1EndAngle - crankAngle) ); } - ********* - uSToEnd = ((ignition1EndAngle - crankAngle) * (toothLastToothTime - toothLastMinusOneToothTime)) / triggerToothAngle; - ********* + //********* + //uSToEnd = ((ignition1EndAngle - crankAngle) * (toothLastToothTime - toothLastMinusOneToothTime)) / triggerToothAngle; + //********* refreshIgnitionSchedule1( uSToEnd + fixedCrankingOverride ); } - */ + #if IGN_CHANNELS >= 2 @@ -1714,7 +1734,7 @@ void loop() if ( tempStartAngle < 0) { tempStartAngle += CRANK_ANGLE_MAX_IGN; } { unsigned long ignition2StartTime = 0; - if(tempStartAngle > tempCrankAngle) { ignition2StartTime = angleToTime((tempStartAngle - tempCrankAngle)); } + if(tempStartAngle > tempCrankAngle) { ignition2StartTime = angleToTime((tempStartAngle - tempCrankAngle), CRANKMATH_METHOD_INTERVAL_REV); } //else if (tempStartAngle < tempCrankAngle) { ignition2StartTime = ((long)(360 - tempCrankAngle + tempStartAngle) * (long)timePerDegree); } else { ignition2StartTime = 0; } @@ -1737,7 +1757,7 @@ void loop() //if (tempStartAngle > tempCrankAngle) { long ignition3StartTime = 0; - if(tempStartAngle > tempCrankAngle) { ignition3StartTime = angleToTime((tempStartAngle - tempCrankAngle)); } + if(tempStartAngle > tempCrankAngle) { ignition3StartTime = angleToTime((tempStartAngle - tempCrankAngle), CRANKMATH_METHOD_INTERVAL_REV); } //else if (tempStartAngle < tempCrankAngle) { ignition4StartTime = ((long)(360 - tempCrankAngle + tempStartAngle) * (long)timePerDegree); } else { ignition3StartTime = 0; } @@ -1761,7 +1781,7 @@ void loop() { long ignition4StartTime = 0; - if(tempStartAngle > tempCrankAngle) { ignition4StartTime = angleToTime((tempStartAngle - tempCrankAngle)); } + if(tempStartAngle > tempCrankAngle) { ignition4StartTime = angleToTime((tempStartAngle - tempCrankAngle), CRANKMATH_METHOD_INTERVAL_REV); } //else if (tempStartAngle < tempCrankAngle) { ignition4StartTime = ((long)(360 - tempCrankAngle + tempStartAngle) * (long)timePerDegree); } else { ignition4StartTime = 0; } @@ -1785,7 +1805,7 @@ void loop() { long ignition5StartTime = 0; - if(tempStartAngle > tempCrankAngle) { ignition5StartTime = angleToTime((tempStartAngle - tempCrankAngle)); } + if(tempStartAngle > tempCrankAngle) { ignition5StartTime = angleToTime((tempStartAngle - tempCrankAngle), CRANKMATH_METHOD_INTERVAL_REV); } //else if (tempStartAngle < tempCrankAngle) { ignition4StartTime = ((long)(360 - tempCrankAngle + tempStartAngle) * (long)timePerDegree); } else { ignition5StartTime = 0; }