From dd08d52db245d22d33913676c465b181a593d155 Mon Sep 17 00:00:00 2001 From: Josh Stewart Date: Fri, 12 Jun 2015 13:18:36 +1000 Subject: [PATCH] Add duty cycle limit option --- decoders.h | 1 + globals.h | 2 +- math.h | 19 +++++ reference/speeduino.ini | 4 +- speeduino.ino | 155 +++++++++++++++++++++------------------- 5 files changed, 107 insertions(+), 74 deletions(-) diff --git a/decoders.h b/decoders.h index 3ed28d2b..d0dc6c42 100644 --- a/decoders.h +++ b/decoders.h @@ -13,3 +13,4 @@ volatile int toothHistoryIndex = 0; volatile int triggerActualTeeth; unsigned int triggerFilterTime; // The shortest time (in uS) that pulses will be accepted (Used for debounce filtering) unsigned int triggerToothAngle; //The number of crank degrees that elapse per tooth +unsigned long revolutionTime; //The time in uS that one revolution would take at current speed (The time tooth 1 was last seen, minus the time it was seen prior to that) diff --git a/globals.h b/globals.h index d7418703..88522257 100644 --- a/globals.h +++ b/globals.h @@ -148,7 +148,7 @@ struct config1 { byte injTiming : 2; byte primePulse; - byte unused40; + byte dutyLim; byte unused41; byte egoSwitch; byte taeColdM; diff --git a/math.h b/math.h index 53459e98..15dc6321 100644 --- a/math.h +++ b/math.h @@ -60,4 +60,23 @@ int divs100(int n) { // return q + (r > 99); } +//Unsigned divide by 100 +unsigned int divu100(unsigned n) { + unsigned q, r; + q = (n >> 1) + (n >> 3) + (n >> 6) - (n >> 10) + + (n >> 12) + (n >> 13) - (n >> 16); + q = q + (q >> 20); + q = q >> 6; + r = n - q*100; + return q + ((r + 28) >> 7); +// return q + (r > 99); +} + +//Return x percent of y +//This is a relatively fast approximation of a percentage value. +unsigned int percentage(byte x, unsigned int y) +{ + return divu100(y) * x; +} + #endif // MATH_H diff --git a/reference/speeduino.ini b/reference/speeduino.ini index 54e07589..17181412 100644 --- a/reference/speeduino.ini +++ b/reference/speeduino.ini @@ -148,7 +148,7 @@ page = 2 injTiming = bits, U08, 38, [4:5], "Bank", "Semi-Sequential", "INVALID", "INVALID" primePulse = scalar, U08, 39, "ms", 0.1, 0.0, 0.0, 25.5, 1 - unused40 = scalar, U08, 40, "RPM", 100.0, 0.0, 100, 25500, 0 + dutyLim = scalar, U08, 40, "%", 1.0, 0.0, 0.0, 100.0, 0 unused41 = scalar, U08, 41, "RPM", 100.0, 0.0, 100, 25500, 0 egoSwitch = scalar, U08, 42, "v", 0.0196, 0.0, 0.0, 5.0, 3 taeColdM = scalar, U08, 43, "%", 1.0, 0.0, 0.0, 250.0, 0 @@ -366,6 +366,7 @@ page = 7 defaultValue = inj2Ang, 355 defaultValue = inj3Ang, 355 defaultValue = inj4Ang, 355 + defaultValue = dutyLim, 100 [Menu] @@ -503,6 +504,7 @@ page = 7 field = "Channel 2", inj2Ang, { nCylinders > 1 } field = "Channel 3", inj3Ang, { nCylinders > 4 || nCylinders == 3 } field = "Channel 4", inj4Ang, { nCylinders > 6 } + field = "Injector Duty Limit", dutyLim panel = injector_voltage_curve dialog = egoControl, "" diff --git a/speeduino.ino b/speeduino.ino index ca5a720c..3bf0e3a9 100644 --- a/speeduino.ino +++ b/speeduino.ino @@ -53,7 +53,8 @@ int req_fuel_uS, inj_opentime_uS; #define MAX_RPM 10000 //This is the maximum rpm that the ECU will attempt to run at. It is NOT related to the rev limiter, but is instead dictates how fast certain operations will be allowed to run. Lower number gives better performance volatile byte startRevolutions = 0; //A counter for how many revolutions have been completed since sync was achieved. -volatile bool ignitionOn = true; //The current state of the ignition system +bool ignitionOn = false; //The current state of the ignition system +bool fuelOn = false; //The current state of the ignition system 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) @@ -320,6 +321,9 @@ void loop() currentStatus.hasSync = false; currentStatus.runSecs = 0; //Reset the counter for number of seconds running. secCounter = 0; //Reset our seconds counter. + startRevolutions = 0; + ignitionOn = false; + fuelOn = false; digitalWrite(pinFuelPump, LOW); //Turn off the fuel pump } @@ -369,6 +373,7 @@ void loop() { //Sets the engine running bit, clears the engine cranking bit BIT_SET(currentStatus.engine, BIT_ENGINE_RUN); BIT_CLEAR(currentStatus.engine, BIT_ENGINE_CRANK); + if(startRevolutions > configPage2.StgCycles) { ignitionOn = true; fuelOn = true;} } else { //Sets the engine cranking bit, clears the engine running bit @@ -376,8 +381,7 @@ void loop() BIT_CLEAR(currentStatus.engine, BIT_ENGINE_RUN); currentStatus.runSecs = 0; //We're cranking (hopefully), so reset the engine run time to prompt ASE. //Check whether enough cranking revolutions have been performed to turn the ignition on - if(startRevolutions > configPage2.StgCycles) - {ignitionOn = true;} + if(startRevolutions > configPage2.StgCycles) { ignitionOn = true; fuelOn = true;} } //END SETTING STATUSES @@ -424,6 +428,10 @@ void loop() //How fast are we going? Need to know how long (uS) it will take to get from one tooth to the next. We then use that to estimate how far we are between the last tooth and the next one timePerDegree = ldiv( 166666L, currentStatus.RPM ).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 / ) + //Check that the duty cycle of the chosen pulsewidth isn't too high + unsigned int pwLimit = percentage(configPage1.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 + if (currentStatus.PW > pwLimit) { currentStatus.PW = pwLimit; } + //*********************************************************************************************** //BEGIN INJECTION TIMING //Determine next firing angles @@ -542,86 +550,89 @@ void loop() //Determine the current crank angle int crankAngle = getCrankAngle(timePerDegree); - if (injector1StartAngle > crankAngle) - { - if (configPage1.injTiming == 1) - { - setFuelSchedule1(openInjector1and4, - ((unsigned long)(injector1StartAngle - crankAngle) * (unsigned long)timePerDegree), - (unsigned long)currentStatus.PW, - closeInjector1and4 - ); + if (fuelOn) + { + if (injector1StartAngle > crankAngle) + { + if (configPage1.injTiming == 1) + { + setFuelSchedule1(openInjector1and4, + ((unsigned long)(injector1StartAngle - crankAngle) * (unsigned long)timePerDegree), + (unsigned long)currentStatus.PW, + closeInjector1and4 + ); + } + else + { + setFuelSchedule1(openInjector1, + ((unsigned long)(injector1StartAngle - crankAngle) * (unsigned long)timePerDegree), + (unsigned long)currentStatus.PW, + closeInjector1 + ); + } } - else - { - setFuelSchedule1(openInjector1, - ((unsigned long)(injector1StartAngle - crankAngle) * (unsigned long)timePerDegree), - (unsigned long)currentStatus.PW, - closeInjector1 - ); + + /*----------------------------------------------------------------------------------------- + | A Note on tempCrankAngle and tempStartAngle: + | The use of tempCrankAngle/tempStartAngle is described below. It is then used in the same way for channels 2, 3 and 4 on both injectors and ignition + | Essentially, these 2 variables are used to realign the current crank and and the desired start angle around 0 degrees for the given cylinder/output + | Eg: If cylinder 2 TDC is 180 degrees after cylinder 1 (Eg a standard 4 cylidner engine), then tempCrankAngle is 180* less than the current crank angle and + | tempStartAngle is the desired open time less 180*. Thus the cylinder is being treated relative to its own TDC, regardless of its offset + | + | This is done to avoid problems with very short of very long times until tempStartAngle. + | This will very likely need to be rewritten when sequential is enabled + |------------------------------------------------------------------------------------------ + */ + tempCrankAngle = crankAngle - channel2Degrees; + if( tempCrankAngle < 0) { tempCrankAngle += 360; } + tempStartAngle = injector2StartAngle - channel2Degrees; + if ( tempStartAngle < 0) { tempStartAngle += 360; } + if (tempStartAngle > tempCrankAngle) + { + if (configPage1.injTiming == 1) + { + setFuelSchedule2(openInjector2and3, + ((unsigned long)(tempStartAngle - tempCrankAngle) * (unsigned long)timePerDegree), + (unsigned long)currentStatus.PW, + closeInjector2and3 + ); + } + else + { + setFuelSchedule2(openInjector2, + ((unsigned long)(tempStartAngle - tempCrankAngle) * (unsigned long)timePerDegree), + (unsigned long)currentStatus.PW, + closeInjector2 + ); + } } - } - - /*----------------------------------------------------------------------------------------- - | A Note on tempCrankAngle and tempStartAngle: - | The use of tempCrankAngle/tempStartAngle is described below. It is then used in the same way for channels 2, 3 and 4 on both injectors and ignition - | Essentially, these 2 variables are used to realign the current crank and and the desired start angle around 0 degrees for the given cylinder/output - | Eg: If cylinder 2 TDC is 180 degrees after cylinder 1 (Eg a standard 4 cylidner engine), then tempCrankAngle is 180* less than the current crank angle and - | tempStartAngle is the desired open time less 180*. Thus the cylinder is being treated relative to its own TDC, regardless of its offset - | - | This is done to avoid problems with very short of very long times until tempStartAngle. - | This will very likely need to be rewritten when sequential is enabled - |------------------------------------------------------------------------------------------ - */ - tempCrankAngle = crankAngle - channel2Degrees; - if( tempCrankAngle < 0) { tempCrankAngle += 360; } - tempStartAngle = injector2StartAngle - channel2Degrees; - if ( tempStartAngle < 0) { tempStartAngle += 360; } - if (tempStartAngle > tempCrankAngle) - { - if (configPage1.injTiming == 1) - { - setFuelSchedule2(openInjector2and3, + + tempCrankAngle = crankAngle - channel3Degrees; + if( tempCrankAngle < 0) { tempCrankAngle += 360; } + tempStartAngle = injector3StartAngle - channel3Degrees; + if ( tempStartAngle < 0) { tempStartAngle += 360; } + if (tempStartAngle > tempCrankAngle) + { + setFuelSchedule3(openInjector3, ((unsigned long)(tempStartAngle - tempCrankAngle) * (unsigned long)timePerDegree), (unsigned long)currentStatus.PW, - closeInjector2and3 + closeInjector3 ); } - else - { - setFuelSchedule2(openInjector2, + + tempCrankAngle = crankAngle - channel4Degrees; + if( tempCrankAngle < 0) { tempCrankAngle += 360; } + tempStartAngle = injector4StartAngle - channel4Degrees; + if ( tempStartAngle < 0) { tempStartAngle += 360; } + if (tempStartAngle > tempCrankAngle) + { + setFuelSchedule4(openInjector4, ((unsigned long)(tempStartAngle - tempCrankAngle) * (unsigned long)timePerDegree), (unsigned long)currentStatus.PW, - closeInjector2 + closeInjector4 ); } } - - tempCrankAngle = crankAngle - channel3Degrees; - if( tempCrankAngle < 0) { tempCrankAngle += 360; } - tempStartAngle = injector3StartAngle - channel3Degrees; - if ( tempStartAngle < 0) { tempStartAngle += 360; } - if (tempStartAngle > tempCrankAngle) - { - setFuelSchedule3(openInjector3, - ((unsigned long)(tempStartAngle - tempCrankAngle) * (unsigned long)timePerDegree), - (unsigned long)currentStatus.PW, - closeInjector3 - ); - } - - tempCrankAngle = crankAngle - channel4Degrees; - if( tempCrankAngle < 0) { tempCrankAngle += 360; } - tempStartAngle = injector4StartAngle - channel4Degrees; - if ( tempStartAngle < 0) { tempStartAngle += 360; } - if (tempStartAngle > tempCrankAngle) - { - setFuelSchedule4(openInjector4, - ((unsigned long)(tempStartAngle - tempCrankAngle) * (unsigned long)timePerDegree), - (unsigned long)currentStatus.PW, - closeInjector4 - ); - } //*********************************************************************************************** //| BEGIN IGNITION SCHEDULES //Likewise for the ignition