Initial commit of new rolling rev limiter

This commit is contained in:
Josh Stewart 2023-06-09 16:10:43 +10:00
parent 6c6cd9488d
commit 9c3e993b53
12 changed files with 200 additions and 100 deletions

View File

@ -1449,7 +1449,11 @@ page = 15
airConUnused4 = bits, U08, 95, [6:7], "0", "1", "2", "3" airConUnused4 = bits, U08, 95, [6:7], "0", "1", "2", "3"
airConIdleUpRPMAdder = scalar, U08, 96, "Added Target RPM", 10.0, 0.0, 0.0, 250.0, 0 airConIdleUpRPMAdder = scalar, U08, 96, "Added Target RPM", 10.0, 0.0, 0.0, 250.0, 0
airConPwmFanMinDuty = scalar, U08, 97, "%", 0.5, 0.0, 0.0, 100.0, 1 airConPwmFanMinDuty = scalar, U08, 97, "%", 0.5, 0.0, 0.0, 100.0, 1
Unused15_98_255 = array, U08, 98, [158], "%", 1.0, 0.0, 0.0, 255, 0
rollingProtRPMDelta = array, S08, 98, [4], "RPM", 10.0, 0, -1000, 0, 0
rollingProtCutPercent = array, U08, 102, [4], "%", 1.0, 0, 0, 100, 0
Unused15_98_255 = array, U08, 98, [150], "%", 1.0, 0.0, 0.0, 255, 0
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
@ -1810,6 +1814,9 @@ page = 15
defaultValue = batlow, 11.8 defaultValue = batlow, 11.8
defaultValue = bathigh, 15 defaultValue = bathigh, 15
defaultValue = rollingProtRPMDelta, -300 -200 -100 -50
defaultValue = rollingProtCutPercent, 50 65 80 95
#if LAMBDA #if LAMBDA
defaultValue = wueAFR, -0.136 -0.102 -0.082 -0.068 -0.054 -0.041 -0.027 -0.014 -0.007 0.000 defaultValue = wueAFR, -0.136 -0.102 -0.082 -0.068 -0.054 -0.041 -0.027 -0.014 -0.007 0.000
#else #else
@ -2986,14 +2993,16 @@ menuDialog = main
indicator = { engineProtectAFR }, "AFR Protect OFF", "AFR Protect ON", green, black, red, black indicator = { engineProtectAFR }, "AFR Protect OFF", "AFR Protect ON", green, black, red, black
indicator = { engineProtectCoolant }, "Coolant Protect OFF", "Coolant Protect ON", green, black, red, black indicator = { engineProtectCoolant }, "Coolant Protect OFF", "Coolant Protect ON", green, black, red, black
dialog = engineProtectionWest, "" dialog = engineProtectionWest, "Engine Protection"
field = "Protection Cut", engineProtectType field = "Protection RPM Limit ('Limp Home')", engineProtectMaxRPM, { engineProtectType }
field = "Engine Protection RPM min", engineProtectMaxRPM, { engineProtectType }
field = "Cut method", hardCutType, { engineProtectType == 1 || engineProtectType == 3 } ;Only available for the protection modes that include ignition.
panel = protectIndicatorPanel, { engineProtectType } panel = protectIndicatorPanel, { engineProtectType }
dialog = engineProtection, "Engine Protection and Limiters", xAxis dialog = engineProtection, "Hard Limit Configuration", yAxis
topicHelp = "http://wiki.speeduino.com/en/configuration/Rev_Limits" topicHelp = "http://wiki.speeduino.com/en/configuration/Rev_Limits"
field = "Protection Cut", engineProtectType
field = "!It is recommended to use Spark Cut on non-sequential fuel configurations", {}, {}, { engineProtectType > 1 && injLayout != 3 }
field = "Cut method", hardCutType, { engineProtectType > 0 } ;Only available for the protection modes that include ignition.
panel = rolling_prot_curve, { hardCutType == 1 }
panel = engineProtectionWest panel = engineProtectionWest
dialog = clutchInput, "Clutch input" dialog = clutchInput, "Clutch input"
@ -4668,6 +4677,15 @@ cmdVSSratio6 = "E\x99\x06"
yBins = coolantProtRPM yBins = coolantProtRPM
size = 200, 200 size = 200, 200
; Rolling engine protection curve
curve = rolling_prot_curve, "Rolling Cut Percent"
columnLabel = "RPM Delta", "Event Cut"
xAxis = -300, 0, 6
yAxis = 0, 125, 6
xBins = rollingProtRPMDelta
yBins = rollingProtCutPercent
size = 200, 200
; Warmup enrichment VEAL AFR adjustment curves ; Warmup enrichment VEAL AFR adjustment curves
curve = warmup_afr_curve, "Target Adjustment" curve = warmup_afr_curve, "Target Adjustment"
columnLabel = "Coolant", "Offset" columnLabel = "Coolant", "Offset"

View File

@ -7,7 +7,7 @@ byte oilProtStartTime = 0;
byte checkEngineProtect(void) byte checkEngineProtect(void)
{ {
byte protectActive = 0; byte protectActive = 0;
if(checkRevLimit() || checkBoostLimit() || checkOilPressureLimit() || checkAFRLimit() ) if(checkBoostLimit() || checkOilPressureLimit() || checkAFRLimit() )
{ {
if( currentStatus.RPMdiv100 > configPage4.engineProtectMaxRPM ) { protectActive = 1; } if( currentStatus.RPMdiv100 > configPage4.engineProtectMaxRPM ) { protectActive = 1; }
} }

View File

@ -156,6 +156,8 @@
#define BIT_TOGGLE(var,pos) ((var)^= 1UL << (pos)) #define BIT_TOGGLE(var,pos) ((var)^= 1UL << (pos))
#define BIT_WRITE(var, pos, bitvalue) ((bitvalue) ? BIT_SET((var), (pos)) : bitClear((var), (pos))) #define BIT_WRITE(var, pos, bitvalue) ((bitvalue) ? BIT_SET((var), (pos)) : bitClear((var), (pos)))
#define CRANK_ANGLE_MAX (max(CRANK_ANGLE_MAX_IGN, CRANK_ANGLE_MAX_INJ))
#define interruptSafe(c) (noInterrupts(); {c} interrupts();) //Wraps any code between nointerrupt and interrupt calls #define interruptSafe(c) (noInterrupts(); {c} interrupts();) //Wraps any code between nointerrupt and interrupt calls
#define MS_IN_MINUTE 60000 #define MS_IN_MINUTE 60000
@ -497,6 +499,7 @@ extern struct table2D oilPressureProtectTable;
extern struct table2D wmiAdvTable; //6 bin wmi correction table for timing advance (2D) extern struct table2D wmiAdvTable; //6 bin wmi correction table for timing advance (2D)
extern struct table2D coolantProtectTable; //6 bin coolant temperature protection table for engine protection (2D) extern struct table2D coolantProtectTable; //6 bin coolant temperature protection table for engine protection (2D)
extern struct table2D fanPWMTable; extern struct table2D fanPWMTable;
extern struct table2D rollingCutTable;
//These are for the direct port manipulation of the injectors, coils and aux outputs //These are for the direct port manipulation of the injectors, coils and aux outputs
extern volatile PORT_TYPE *inj1_pin_port; extern volatile PORT_TYPE *inj1_pin_port;
@ -600,7 +603,6 @@ extern volatile uint16_t ignitionCount; /**< The count of ignition events that h
extern byte secondaryTriggerEdge; extern byte secondaryTriggerEdge;
extern byte tertiaryTriggerEdge; extern byte tertiaryTriggerEdge;
#endif #endif
extern int CRANK_ANGLE_MAX;
extern int CRANK_ANGLE_MAX_IGN; extern int CRANK_ANGLE_MAX_IGN;
extern int CRANK_ANGLE_MAX_INJ; ///< The number of crank degrees that the system track over. 360 for wasted / timed batch and 720 for sequential extern int CRANK_ANGLE_MAX_INJ; ///< The number of crank degrees that the system track over. 360 for wasted / timed batch and 720 for sequential
extern volatile uint32_t runSecsX10; /**< Counter of seconds since cranking commenced (similar to runSecs) but in increments of 0.1 seconds */ extern volatile uint32_t runSecsX10; /**< Counter of seconds since cranking commenced (similar to runSecs) but in increments of 0.1 seconds */
@ -1374,7 +1376,7 @@ struct config10 {
byte oilPressureProtTime; byte oilPressureProtTime;
byte unused11_191_191; //Bytes 187-191 byte unused11_191_191;
#if defined(CORE_AVR) #if defined(CORE_AVR)
}; };
@ -1480,9 +1482,12 @@ struct config15 {
byte airConUnused4 : 2; byte airConUnused4 : 2;
byte airConIdleUpRPMAdder; byte airConIdleUpRPMAdder;
byte airConPwmFanMinDuty; byte airConPwmFanMinDuty;
int8_t rollingProtRPMDelta[4]; // Signed RPM value representing how much below the RPM limit. Divided by 10
byte rollingProtCutPercent[4];
//Bytes 98-255 //Bytes 98-255
byte Unused15_98_255[158]; byte Unused15_98_255[150];
#if defined(CORE_AVR) #if defined(CORE_AVR)
}; };

View File

@ -54,6 +54,7 @@ struct table2D oilPressureProtectTable;
struct table2D wmiAdvTable; //6 bin wmi correction table for timing advance (2D) struct table2D wmiAdvTable; //6 bin wmi correction table for timing advance (2D)
struct table2D coolantProtectTable; struct table2D coolantProtectTable;
struct table2D fanPWMTable; struct table2D fanPWMTable;
struct table2D rollingCutTable;
/// volatile inj*_pin_port and inj*_pin_mask vars are for the direct port manipulation of the injectors, coils and aux outputs. /// volatile inj*_pin_port and inj*_pin_mask vars are for the direct port manipulation of the injectors, coils and aux outputs.
volatile PORT_TYPE *inj1_pin_port; volatile PORT_TYPE *inj1_pin_port;
@ -144,7 +145,6 @@ volatile uint16_t ignitionCount; /**< The count of ignition events that have tak
byte secondaryTriggerEdge; byte secondaryTriggerEdge;
byte tertiaryTriggerEdge; byte tertiaryTriggerEdge;
#endif #endif
int CRANK_ANGLE_MAX = 720;
int CRANK_ANGLE_MAX_IGN = 360; int CRANK_ANGLE_MAX_IGN = 360;
int CRANK_ANGLE_MAX_INJ = 360; ///< The number of crank degrees that the system track over. 360 for wasted / timed batch and 720 for sequential int CRANK_ANGLE_MAX_INJ = 360; ///< The number of crank degrees that the system track over. 360 for wasted / timed batch and 720 for sequential
volatile uint32_t runSecsX10; volatile uint32_t runSecsX10;

View File

@ -259,6 +259,12 @@ void initialiseAll(void)
fanPWMTable.values = configPage9.PWMFanDuty; fanPWMTable.values = configPage9.PWMFanDuty;
fanPWMTable.axisX = configPage6.fanPWMBins; fanPWMTable.axisX = configPage6.fanPWMBins;
rollingCutTable.valueSize = SIZE_BYTE;
rollingCutTable.axisSize = SIZE_SIGNED_BYTE; //X axis is SIGNED for this table.
rollingCutTable.xSize = 4;
rollingCutTable.values = configPage15.rollingProtCutPercent;
rollingCutTable.axisX = configPage15.rollingProtRPMDelta;
wmiAdvTable.valueSize = SIZE_BYTE; wmiAdvTable.valueSize = SIZE_BYTE;
wmiAdvTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins wmiAdvTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins
wmiAdvTable.xSize = 6; wmiAdvTable.xSize = 6;
@ -449,7 +455,6 @@ void initialiseAll(void)
//Calculate the number of degrees between cylinders //Calculate the number of degrees between cylinders
//Set some default values. These will be updated below if required. //Set some default values. These will be updated below if required.
CRANK_ANGLE_MAX = 720;
CRANK_ANGLE_MAX_IGN = 360; CRANK_ANGLE_MAX_IGN = 360;
CRANK_ANGLE_MAX_INJ = 360; CRANK_ANGLE_MAX_INJ = 360;
@ -540,24 +545,24 @@ void initialiseAll(void)
maxIgnOutputs = 3; maxIgnOutputs = 3;
if (configPage2.engineType == EVEN_FIRE ) if (configPage2.engineType == EVEN_FIRE )
{ {
//Sequential and Single channel modes both run over 720 crank degrees, but only on 4 stroke engines. //Sequential and Single channel modes both run over 720 crank degrees, but only on 4 stroke engines.
if( ( (configPage4.sparkMode == IGN_MODE_SEQUENTIAL) || (configPage4.sparkMode == IGN_MODE_SINGLE) ) && (configPage2.strokes == FOUR_STROKE) ) if( ( (configPage4.sparkMode == IGN_MODE_SEQUENTIAL) || (configPage4.sparkMode == IGN_MODE_SINGLE) ) && (configPage2.strokes == FOUR_STROKE) )
{ {
channel2IgnDegrees = 240; channel2IgnDegrees = 240;
channel3IgnDegrees = 480; channel3IgnDegrees = 480;
CRANK_ANGLE_MAX_IGN = 720; CRANK_ANGLE_MAX_IGN = 720;
}
else
{
channel2IgnDegrees = 120;
channel3IgnDegrees = 240;
}
} }
else else
{ {
channel2IgnDegrees = 120; channel2IgnDegrees = configPage2.oddfire2;
channel3IgnDegrees = 240; channel3IgnDegrees = configPage2.oddfire3;
}
}
else
{
channel2IgnDegrees = configPage2.oddfire2;
channel3IgnDegrees = configPage2.oddfire3;
} }
//For alternating injection, the squirt occurs at different times for each channel //For alternating injection, the squirt occurs at different times for each channel
@ -976,9 +981,6 @@ void initialiseAll(void)
break; break;
} }
if(CRANK_ANGLE_MAX_IGN == CRANK_ANGLE_MAX_INJ) { CRANK_ANGLE_MAX = CRANK_ANGLE_MAX_IGN; } //If both the injector max and ignition max angles are the same, make the overall system max this value
else if (CRANK_ANGLE_MAX_IGN > CRANK_ANGLE_MAX_INJ) { CRANK_ANGLE_MAX = CRANK_ANGLE_MAX_IGN; }
else { CRANK_ANGLE_MAX = CRANK_ANGLE_MAX_INJ; }
currentStatus.status3 |= currentStatus.nSquirts << BIT_STATUS3_NSQUIRTS1; //Top 3 bits of the status3 variable are the number of squirts. This must be done after the above section due to nSquirts being forced to 1 for sequential currentStatus.status3 |= currentStatus.nSquirts << BIT_STATUS3_NSQUIRTS1; //Top 3 bits of the status3 variable are the number of squirts. This must be done after the above section due to nSquirts being forced to 1 for sequential
//Special case: //Special case:
@ -986,7 +988,7 @@ void initialiseAll(void)
//This is ONLY the case on 4 stroke systems //This is ONLY the case on 4 stroke systems
if( (currentStatus.nSquirts == 3) || (currentStatus.nSquirts == 5) ) if( (currentStatus.nSquirts == 3) || (currentStatus.nSquirts == 5) )
{ {
if(configPage2.strokes == FOUR_STROKE) { CRANK_ANGLE_MAX = 720; } if(configPage2.strokes == FOUR_STROKE) { CRANK_ANGLE_MAX_INJ = 720; }
} }
switch(configPage2.injLayout) switch(configPage2.injLayout)

View File

@ -4,6 +4,7 @@
unsigned long percentage(uint8_t x, unsigned long y); unsigned long percentage(uint8_t x, unsigned long y);
unsigned long halfPercentage(uint8_t x, unsigned long y); unsigned long halfPercentage(uint8_t x, unsigned long y);
inline long powint(int factor, unsigned int exponent); inline long powint(int factor, unsigned int exponent);
uint8_t random1to100();
#ifdef USE_LIBDIVIDE #ifdef USE_LIBDIVIDE
#include "src/libdivide/libdivide.h" #include "src/libdivide/libdivide.h"

View File

@ -54,3 +54,30 @@ inline long powint(int factor, unsigned int exponent)
while ( (counter--) > 0) { product *= factor; } while ( (counter--) > 0) { product *= factor; }
return product; return product;
} }
//Generates a random number from 1 to 100 (inclusive).
//The initial seed used is always based on micros(), though this is unlikely to cause an issue as the first run is nearly random itself
//Function requires 4 bytes to store state and seed, but operates very quickly (around 4uS per call)
uint8_t a, x, y, z;
uint8_t random1to100()
{
//Check if this is the first time being run. If so, seed the random number generator with micros()
if( (a == 0) && (x == 0) && (y == 0) && (z == 0) )
{
x = micros() >> 24;
y = micros() >> 16;
z = micros() >> 8;
a = micros();
}
do
{
unsigned char t = x ^ (x << 4);
x=y;
y=z;
z=a;
a = z ^ t ^ ( z >> 1) ^ (t << 1);
}
while(a >= 100);
return (a+1);
}

View File

@ -24,13 +24,6 @@ byte getAdvance1(void);
extern uint16_t req_fuel_uS; /**< The required fuel variable (As calculated by TunerStudio) in uS */ extern uint16_t req_fuel_uS; /**< The required fuel variable (As calculated by TunerStudio) in uS */
extern uint16_t inj_opentime_uS; /**< The injector opening time. This is set within Tuner Studio, but stored here in uS rather than mS */ extern uint16_t inj_opentime_uS; /**< The injector opening time. This is set within Tuner Studio, but stored here in uS rather than mS */
extern bool ignitionOn; /**< The current state of the ignition system (on or off) */
extern bool fuelOn; /**< The current state of the fuel system (on or off) */
extern byte curRollingCut; /**< Rolling rev limiter, current ignition channel being cut */
extern byte rollingCutCounter; /**< how many times (revolutions) the ignition has been cut in a row */
extern uint32_t rollingCutLastRev; /**< Tracks whether we're on the same or a different rev for the rolling cut */
/** @name Staging /** @name Staging
* These values are a percentage of the total (Combined) req_fuel value that would be required for each injector channel to deliver that much fuel. * These values are a percentage of the total (Combined) req_fuel value that would be required for each injector channel to deliver that much fuel.
* *

View File

@ -52,11 +52,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
uint16_t req_fuel_uS = 0; /**< The required fuel variable (As calculated by TunerStudio) in uS */ uint16_t req_fuel_uS = 0; /**< The required fuel variable (As calculated by TunerStudio) in uS */
uint16_t inj_opentime_uS = 0; uint16_t inj_opentime_uS = 0;
bool ignitionOn = false; /**< The current state of the ignition system (on or off) */ uint8_t ignitionChannelsOn; /**< The current state of the ignition system (on or off) */
bool fuelOn = false; /**< The current state of the fuel system (on or off) */ uint8_t fuelChannelsOn; /**< The current state of the fuel system (on or off) */
byte curRollingCut = 0; /**< Rolling rev limiter, current ignition channel being cut */ //byte curRollingCut = 0; /**< Rolling rev limiter, current ignition channel being cut */
byte rollingCutCounter = 0; /**< how many times (revolutions) the ignition has been cut in a row */ //byte rollingCutCounter = 0; /**< how many times (revolutions) the ignition has been cut in a row */
uint32_t rollingCutLastRev = 0; /**< Tracks whether we're on the same or a different rev for the rolling cut */ uint32_t rollingCutLastRev = 0; /**< Tracks whether we're on the same or a different rev for the rolling cut */
uint16_t staged_req_fuel_mult_pri = 0; uint16_t staged_req_fuel_mult_pri = 0;
@ -175,8 +175,8 @@ void loop(void)
currentStatus.rpmDOT = 0; currentStatus.rpmDOT = 0;
AFRnextCycle = 0; AFRnextCycle = 0;
ignitionCount = 0; ignitionCount = 0;
ignitionOn = false; ignitionChannelsOn = 0;
fuelOn = false; fuelChannelsOn = 0;
if (fpPrimed == true) { FUEL_PUMP_OFF(); currentStatus.fuelPumpOn = false; } //Turn off the fuel pump, but only if the priming is complete if (fpPrimed == true) { FUEL_PUMP_OFF(); currentStatus.fuelPumpOn = false; } //Turn off the fuel pump, but only if the priming is complete
if (configPage6.iacPWMrun == false) { disableIdle(); } //Turn off the idle PWM if (configPage6.iacPWMrun == false) { disableIdle(); } //Turn off the idle PWM
BIT_CLEAR(currentStatus.engine, BIT_ENGINE_CRANK); //Clear cranking bit (Can otherwise get stuck 'on' even with 0 rpm) BIT_CLEAR(currentStatus.engine, BIT_ENGINE_CRANK); //Clear cranking bit (Can otherwise get stuck 'on' even with 0 rpm)
@ -423,7 +423,6 @@ void loop(void)
//Main loop runs within this clause //Main loop runs within this clause
if ((currentStatus.hasSync || BIT_CHECK(currentStatus.status3, BIT_STATUS3_HALFSYNC)) && (currentStatus.RPM > 0)) if ((currentStatus.hasSync || BIT_CHECK(currentStatus.status3, BIT_STATUS3_HALFSYNC)) && (currentStatus.RPM > 0))
{ {
if(currentStatus.startRevolutions >= configPage4.StgCycles) { ignitionOn = true; fuelOn = true; } //Enable the fuel and ignition, assuming staging revolutions are complete
//Check whether running or cranking //Check whether running or cranking
if(currentStatus.RPM > currentStatus.crankRPM) //Crank RPM in the config is stored as a x10. currentStatus.crankRPM is set in timers.ino and represents the true value if(currentStatus.RPM > currentStatus.crankRPM) //Crank RPM in the config is stored as a x10. currentStatus.crankRPM is set in timers.ino and represents the true value
{ {
@ -987,61 +986,102 @@ void loop(void)
// } // }
//Check for any of the engine protections or rev limiters being turned on //Check for any of the engine protections or rev limiters being turned on
if(checkEngineProtect() || currentStatus.launchingHard || currentStatus.flatShiftingHard) int16_t maxAllowedRPM = configPage4.HardRevLim; //The maximum RPM allowed by all the potential limiters (Engine protection, 2-step, flat shift etc). Divided by 100. Use RPM hard limit as the default as it's the highest that is ever permitted
//Check each of the functions that has an RPM limit. Update the max allowed RPM if the function is active and has a lower RPM than already set
checkRevLimit();
if( checkEngineProtect() && (configPage4.engineProtectMaxRPM < maxAllowedRPM)) { maxAllowedRPM = configPage4.engineProtectMaxRPM; }
if ( (currentStatus.launchingHard == true) && (configPage6.lnchHardLim < maxAllowedRPM) ) { maxAllowedRPM = configPage6.lnchHardLim; }
if ( (currentStatus.flatShiftingHard == true) && (configPage6.flatSArm < maxAllowedRPM) ) { maxAllowedRPM = configPage6.flatSArm; }
maxAllowedRPM = maxAllowedRPM * 100; //All of the above limits are divided by 100, convert back to RPM
if( (configPage2.hardCutType == HARD_CUT_FULL) && (currentStatus.RPM > maxAllowedRPM) )
{ {
if( (currentStatus.RPMdiv100 > configPage4.engineProtectMaxRPM) || currentStatus.launchingHard || currentStatus.flatShiftingHard) //Ugly, but the launch/flat shift check needs to be here as well to prevent these limiters not happening when under the the Engine Protection min rpm //Full hard cut turns outputs off completely.
switch(configPage6.engineProtectType)
{ {
if( (configPage2.hardCutType == HARD_CUT_FULL) || (configPage6.engineProtectType == PROTECT_CUT_FUEL) ) case PROTECT_CUT_OFF:
{ //Make sure all channels are turned on
//Full hard cut turns outputs off completely. Note that hard cut is ALWAYS used on fuel cut only. ignitionChannelsOn = 0xFF;
switch(configPage6.engineProtectType) fuelChannelsOn = 0xFF;
currentStatus.engineProtectStatus = 0;
break;
case PROTECT_CUT_IGN:
ignitionChannelsOn = 0;
break;
case PROTECT_CUT_FUEL:
fuelChannelsOn = 0;
break;
case PROTECT_CUT_BOTH:
ignitionChannelsOn = 0;
fuelChannelsOn = 0;
break;
default:
ignitionChannelsOn = 0;
fuelChannelsOn = 0;
break;
}
} //Hard cut check
else if( (configPage2.hardCutType == HARD_CUT_ROLLING) && (currentStatus.RPM > (maxAllowedRPM + (configPage15.rollingProtRPMDelta[0] * 10))) ) //Limit for rolling is the max allowed RPM minus the lowest value in the delta table (Delta values are negative!)
{
uint8_t revolutionsToCut = 1;
if(configPage2.strokes == FOUR_STROKE) { revolutionsToCut *= 2; } //4 stroke needs to cut for at least 2 revolutions
//if( (configPage4.sparkMode != IGN_MODE_SEQUENTIAL) || (configPage2.injLayout != INJ_SEQUENTIAL) ) { revolutionsToCut *= 2; } //4 stroke and non-sequential will cut for 4 revolutions minimum. This is to ensure no half fuel ignition cycles take place
if(rollingCutLastRev == 0) { rollingCutLastRev = currentStatus.startRevolutions; } //First time check
if (currentStatus.startRevolutions > (rollingCutLastRev + revolutionsToCut) )
{
uint8_t cutPercent = 0;
int16_t rpmDelta = currentStatus.RPM - maxAllowedRPM;
if(rpmDelta >= 0) { cutPercent = 100; } //If the current RPM is over the max allowed RPM then cut is full (100%)
else { cutPercent = table2D_getValue(&rollingCutTable, (rpmDelta / 10) ); } //
//max(maxIgnOutputs, maxInjOutputs)
for(uint8_t x=0; x<8; x++)
{
if( (configPage6.engineProtectType != PROTECT_CUT_OFF) && ( (cutPercent == 100) || (random1to100() < cutPercent) ) )
{ {
case PROTECT_CUT_OFF: switch(configPage6.engineProtectType)
ignitionOn = true; {
fuelOn = true; case PROTECT_CUT_IGN:
currentStatus.engineProtectStatus = 0; BIT_CLEAR(ignitionChannelsOn, x); //Turn off this ignition channel
break; break;
case PROTECT_CUT_IGN: case PROTECT_CUT_FUEL:
ignitionOn = false; BIT_CLEAR(fuelChannelsOn, x); //Turn off this fuel channel
break; break;
case PROTECT_CUT_FUEL: case PROTECT_CUT_BOTH:
fuelOn = false; BIT_CLEAR(ignitionChannelsOn, x); //Turn off this ignition channel
break; BIT_CLEAR(fuelChannelsOn, x); //Turn off this fuel channel
case PROTECT_CUT_BOTH: break;
ignitionOn = false; default:
fuelOn = false; BIT_CLEAR(ignitionChannelsOn, x); //Turn off this ignition channel
break; BIT_CLEAR(fuelChannelsOn, x); //Turn off this fuel channel
default: break;
ignitionOn = false; }
fuelOn = false; }
break; else
{
BIT_SET(ignitionChannelsOn, x); //Turn on this ignition channel
BIT_SET(fuelChannelsOn, x); //Turn on this fuel channel
} }
} }
else rollingCutLastRev = currentStatus.startRevolutions;
{ }
//if(rollingCutCounter >= 2) //Vary this number to change the intensity of the roll. The higher the number, the closer is it to full cut } //Rolling cut check
if(rollingCutLastRev == 0) { rollingCutLastRev = currentStatus.startRevolutions; } // else
if (currentStatus.startRevolutions > (rollingCutLastRev+1) ) {
{ currentStatus.engineProtectStatus = 0;
//Rolls through each of the active ignition channels based on how many revolutions have taken place //No engine protection active, so turn all the channels on
//curRollingCut = ( (currentStatus.startRevolutions / 2) % maxIgnOutputs) + 1; if(currentStatus.startRevolutions >= configPage4.StgCycles)
curRollingCut = 0; {
rollingCutCounter += 1; //Enable the fuel and ignition, assuming staging revolutions are complete
if(rollingCutCounter > (maxIgnOutputs-1)) { rollingCutCounter = 0; } ignitionChannelsOn = 0xff;
BIT_SET(curRollingCut, rollingCutCounter); fuelChannelsOn = 0xff;
}
}
ignitionOn = true;
rollingCutLastRev = currentStatus.startRevolutions;
//curRollingCut = 0;
}
} //Hard/Rolling cut check
} //RPM Check
else { currentStatus.engineProtectStatus = 0; } //Force all engine protection flags to be off as we're below the minimum RPM
} //Protection active check
else { curRollingCut = 0; } //Disables the rolling hard cut
#if INJ_CHANNELS >= 1 #if INJ_CHANNELS >= 1
if (fuelOn && !BIT_CHECK(currentStatus.status1, BIT_STATUS1_BOOSTCUT)) if( (BIT_CHECK(fuelChannelsOn, INJ1_CMD_BIT)) && !BIT_CHECK(currentStatus.status1, BIT_STATUS1_BOOSTCUT))
{ {
if(currentStatus.PW1 >= inj_opentime_uS) if(currentStatus.PW1 >= inj_opentime_uS)
{ {
@ -1187,7 +1227,7 @@ void loop(void)
} }
else { fixedCrankingOverride = 0; } else { fixedCrankingOverride = 0; }
if(ignitionOn) if(ignitionChannelsOn > 0)
{ {
//Refresh the current crank angle info //Refresh the current crank angle info
//ignition1StartAngle = 335; //ignition1StartAngle = 335;
@ -1196,7 +1236,7 @@ void loop(void)
#if IGN_CHANNELS >= 1 #if IGN_CHANNELS >= 1
uint32_t timeOut = calculateIgnition1Timeout(crankAngle); uint32_t timeOut = calculateIgnition1Timeout(crankAngle);
if ( (timeOut > 0U) && (!BIT_CHECK(curRollingCut, IGN1_CMD_BIT)) ) if ( (timeOut > 0U) && (BIT_CHECK(ignitionChannelsOn, IGN1_CMD_BIT)) )
{ {
setIgnitionSchedule1(ign1StartFunction, setIgnitionSchedule1(ign1StartFunction,
@ -1233,7 +1273,7 @@ void loop(void)
{ {
unsigned long ignition2StartTime = calculateIgnitionNTimeout(ignitionSchedule2, ignition2StartAngle, channel2IgnDegrees, crankAngle); unsigned long ignition2StartTime = calculateIgnitionNTimeout(ignitionSchedule2, ignition2StartAngle, channel2IgnDegrees, crankAngle);
if ( (ignition2StartTime > 0) && (!BIT_CHECK(curRollingCut, IGN2_CMD_BIT)) ) if ( (ignition2StartTime > 0) && (BIT_CHECK(ignitionChannelsOn, IGN2_CMD_BIT)) )
{ {
setIgnitionSchedule2(ign2StartFunction, setIgnitionSchedule2(ign2StartFunction,
ignition2StartTime, ignition2StartTime,
@ -1249,7 +1289,7 @@ void loop(void)
{ {
unsigned long ignition3StartTime = calculateIgnitionNTimeout(ignitionSchedule3, ignition3StartAngle, channel3IgnDegrees, crankAngle); unsigned long ignition3StartTime = calculateIgnitionNTimeout(ignitionSchedule3, ignition3StartAngle, channel3IgnDegrees, crankAngle);
if ( (ignition3StartTime > 0) && (!BIT_CHECK(curRollingCut, IGN3_CMD_BIT)) ) if ( (ignition3StartTime > 0) && (BIT_CHECK(ignitionChannelsOn, IGN3_CMD_BIT)) )
{ {
setIgnitionSchedule3(ign3StartFunction, setIgnitionSchedule3(ign3StartFunction,
ignition3StartTime, ignition3StartTime,
@ -1265,7 +1305,7 @@ void loop(void)
{ {
unsigned long ignition4StartTime = calculateIgnitionNTimeout(ignitionSchedule4, ignition4StartAngle, channel4IgnDegrees, crankAngle); unsigned long ignition4StartTime = calculateIgnitionNTimeout(ignitionSchedule4, ignition4StartAngle, channel4IgnDegrees, crankAngle);
if ( (ignition4StartTime > 0) && (!BIT_CHECK(curRollingCut, IGN4_CMD_BIT)) ) if ( (ignition4StartTime > 0) && (BIT_CHECK(ignitionChannelsOn, IGN4_CMD_BIT)) )
{ {
setIgnitionSchedule4(ign4StartFunction, setIgnitionSchedule4(ign4StartFunction,
ignition4StartTime, ignition4StartTime,
@ -1281,7 +1321,7 @@ void loop(void)
{ {
unsigned long ignition5StartTime = calculateIgnitionNTimeout(ignitionSchedule5, ignition5StartAngle, channel5IgnDegrees, crankAngle); unsigned long ignition5StartTime = calculateIgnitionNTimeout(ignitionSchedule5, ignition5StartAngle, channel5IgnDegrees, crankAngle);
if ( (ignition5StartTime > 0) && (!BIT_CHECK(curRollingCut, IGN5_CMD_BIT)) ) if ( (ignition5StartTime > 0) && (BIT_CHECK(ignitionChannelsOn, IGN5_CMD_BIT)) )
{ {
setIgnitionSchedule5(ign5StartFunction, setIgnitionSchedule5(ign5StartFunction,
ignition5StartTime, ignition5StartTime,
@ -1297,7 +1337,7 @@ void loop(void)
{ {
unsigned long ignition6StartTime = calculateIgnitionNTimeout(ignitionSchedule6, ignition6StartAngle, channel6IgnDegrees, crankAngle); unsigned long ignition6StartTime = calculateIgnitionNTimeout(ignitionSchedule6, ignition6StartAngle, channel6IgnDegrees, crankAngle);
if ( (ignition6StartTime > 0) && (!BIT_CHECK(curRollingCut, IGN6_CMD_BIT)) ) if ( (ignition6StartTime > 0) && (BIT_CHECK(ignitionChannelsOn, IGN6_CMD_BIT)) )
{ {
setIgnitionSchedule6(ign6StartFunction, setIgnitionSchedule6(ign6StartFunction,
ignition6StartTime, ignition6StartTime,
@ -1313,7 +1353,7 @@ void loop(void)
{ {
unsigned long ignition7StartTime = calculateIgnitionNTimeout(ignitionSchedule7, ignition7StartAngle, channel7IgnDegrees, crankAngle); unsigned long ignition7StartTime = calculateIgnitionNTimeout(ignitionSchedule7, ignition7StartAngle, channel7IgnDegrees, crankAngle);
if ( (ignition7StartTime > 0) && (!BIT_CHECK(curRollingCut, IGN7_CMD_BIT)) ) if ( (ignition7StartTime > 0) && (BIT_CHECK(ignitionChannelsOn, IGN7_CMD_BIT)) )
{ {
setIgnitionSchedule7(ign7StartFunction, setIgnitionSchedule7(ign7StartFunction,
ignition7StartTime, ignition7StartTime,
@ -1329,7 +1369,7 @@ void loop(void)
{ {
unsigned long ignition8StartTime = calculateIgnitionNTimeout(ignitionSchedule8, ignition8StartAngle, channel8IgnDegrees, crankAngle); unsigned long ignition8StartTime = calculateIgnitionNTimeout(ignitionSchedule8, ignition8StartAngle, channel8IgnDegrees, crankAngle);
if ( (ignition8StartTime > 0) && (!BIT_CHECK(curRollingCut, IGN8_CMD_BIT)) ) if ( (ignition8StartTime > 0) && (BIT_CHECK(ignitionChannelsOn, IGN8_CMD_BIT)) )
{ {
setIgnitionSchedule8(ign8StartFunction, setIgnitionSchedule8(ign8StartFunction,
ignition8StartTime, ignition8StartTime,

View File

@ -4,6 +4,7 @@ This file is used for everything related to maps/tables including their definiti
#ifndef TABLE_H #ifndef TABLE_H
#define TABLE_H #define TABLE_H
#define SIZE_SIGNED_BYTE 4
#define SIZE_BYTE 8 #define SIZE_BYTE 8
#define SIZE_INT 16 #define SIZE_INT 16

View File

@ -136,6 +136,8 @@ int16_t table2D_getAxisValue(struct table2D *fromTable, byte X_in)
if(fromTable->axisSize == SIZE_INT) { returnValue = ((int16_t*)fromTable->axisX)[X_in]; } if(fromTable->axisSize == SIZE_INT) { returnValue = ((int16_t*)fromTable->axisX)[X_in]; }
else if(fromTable->axisSize == SIZE_BYTE) { returnValue = ((uint8_t*)fromTable->axisX)[X_in]; } else if(fromTable->axisSize == SIZE_BYTE) { returnValue = ((uint8_t*)fromTable->axisX)[X_in]; }
else if(fromTable->axisSize == SIZE_SIGNED_BYTE) { returnValue = ((int8_t*)fromTable->axisX)[X_in]; }
return returnValue; return returnValue;
} }
@ -153,6 +155,7 @@ int16_t table2D_getRawValue(struct table2D *fromTable, byte X_index)
if(fromTable->valueSize == SIZE_INT) { returnValue = ((int16_t*)fromTable->values)[X_index]; } if(fromTable->valueSize == SIZE_INT) { returnValue = ((int16_t*)fromTable->values)[X_index]; }
else if(fromTable->valueSize == SIZE_BYTE) { returnValue = ((uint8_t*)fromTable->values)[X_index]; } else if(fromTable->valueSize == SIZE_BYTE) { returnValue = ((uint8_t*)fromTable->values)[X_index]; }
else if(fromTable->valueSize == SIZE_SIGNED_BYTE) { returnValue = ((int8_t*)fromTable->values)[X_index]; }
return returnValue; return returnValue;
} }

View File

@ -707,6 +707,16 @@ void doUpdates(void)
{ {
//202306 //202306
//Rolling cut curve added. Default values
configPage15.rollingProtRPMDelta[0] = -30;
configPage15.rollingProtRPMDelta[1] = -20;
configPage15.rollingProtRPMDelta[2] = -10;
configPage15.rollingProtRPMDelta[3] = -5;
configPage15.rollingProtCutPercent[0] = 50;
configPage15.rollingProtCutPercent[1] = 65;
configPage15.rollingProtCutPercent[2] = 80;
configPage15.rollingProtCutPercent[3] = 95;
writeAllConfig(); writeAllConfig();
storeEEPROMVersion(22); storeEEPROMVersion(22);
} }