From 9f53002b9d5b6121ff078bdb952cb5e8361a6873 Mon Sep 17 00:00:00 2001 From: Josh Stewart Date: Wed, 3 Jan 2018 17:27:22 +1100 Subject: [PATCH 1/5] Adjust injector setting priority so that sequential will override the Simultaneous/Alternating option --- reference/speeduino.ini | 2 +- speeduino/speeduino.ino | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/reference/speeduino.ini b/reference/speeduino.ini index 009ff753..52aecfcf 100644 --- a/reference/speeduino.ini +++ b/reference/speeduino.ini @@ -930,7 +930,7 @@ menuDialog = main ; constantName = "Help Text" ; These provide the context help in the dialog when these variables are used nCylinders = "Cylinder count" - alternate = "" + alternate = "Whether or not the injectors should be fired at the same time. This setting is ignored when Sequential is selected below, however it will still affect the req_fuel value." engineType = "Engines with an equal number of degrees between all firings (This is most engines) should select Even fire. Some 2 and 6 cylinder engines are Odd fire however." twoStroke = "Four-Stroke (most engines), Two-stroke." nInjectors = "Number of primary injectors." diff --git a/speeduino/speeduino.ino b/speeduino/speeduino.ino index 094391bb..2027c528 100644 --- a/speeduino/speeduino.ino +++ b/speeduino/speeduino.ino @@ -345,12 +345,21 @@ void setup() else { channel2IgnDegrees = configPage1.oddfire2; } //For alternating injection, the squirt occurs at different times for each channel - if(configPage1.injLayout == INJ_SEMISEQUENTIAL) + if(configPage1.injLayout == INJ_SEMISEQUENTIAL || configPage1.injLayout == INJ_PAIRED) { channel1InjDegrees = 0; channel2InjDegrees = channel2IgnDegrees; //Set to the same as the ignition degrees (Means there's no need for another if to check for oddfire) + + if (!configPage1.injTiming) { channel1InjDegrees = channel2InjDegrees = 0; } //For simultaneous, all squirts happen at the same time + } + else if (configPage1.injLayout == INJ_SEQUENTIAL) + { + channel1InjDegrees = 0; + channel2InjDegrees = channel2IgnDegrees; + + CRANK_ANGLE_MAX_INJ = 720; + req_fuel_uS = req_fuel_uS * 2; } - if (!configPage1.injTiming) { channel1InjDegrees = channel2InjDegrees = 0; } //For simultaneous, all squirts happen at the same time channel1InjEnabled = true; channel2InjEnabled = true; @@ -381,11 +390,13 @@ void setup() } //For alternatiing injection, the squirt occurs at different times for each channel - if(configPage1.injLayout == INJ_SEMISEQUENTIAL || configPage1.injLayout == INJ_PAIRED) + if(configPage1.injLayout == INJ_SEMISEQUENTIAL || configPage1.injLayout == INJ_PAIRED) { channel1InjDegrees = 0; channel2InjDegrees = 120; channel3InjDegrees = 240; + + if (!configPage1.injTiming) { channel1InjDegrees = channel2InjDegrees = channel3InjDegrees = 0; } //For simultaneous, all squirts happen at the same time } else if (configPage1.injLayout == INJ_SEQUENTIAL) { @@ -395,7 +406,6 @@ void setup() CRANK_ANGLE_MAX_INJ = 720; req_fuel_uS = req_fuel_uS * 2; } - if (!configPage1.injTiming) { channel1InjDegrees = channel2InjDegrees = channel3InjDegrees = 0; } //For simultaneous, all squirts happen at the same time channel1InjEnabled = true; channel2InjEnabled = true; @@ -436,6 +446,8 @@ void setup() { channel1InjDegrees = 0; channel2InjDegrees = 180; + + if (!configPage1.injTiming) { channel1InjDegrees = channel2InjDegrees = 0; } //For simultaneous, all squirts happen at the same time } else if (configPage1.injLayout == INJ_SEQUENTIAL) { @@ -450,7 +462,6 @@ void setup() CRANK_ANGLE_MAX_INJ = 720; req_fuel_uS = req_fuel_uS * 2; } - if (!configPage1.injTiming) { channel1InjDegrees = channel2InjDegrees = 0; } //For simultaneous, all squirts happen at the same time //Check if injector staging is enabled if(configPage11.stagingEnabled == true) @@ -549,6 +560,8 @@ void setup() 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 + switch(configPage2.sparkMode) { case IGN_MODE_WASTED: From 3a98d08c1a110a9f5facba42e15a94843c74bc47 Mon Sep 17 00:00:00 2001 From: Josh Stewart Date: Wed, 3 Jan 2018 20:12:37 +1100 Subject: [PATCH 2/5] Prevent staging and sequential trying to function at once --- speeduino/speeduino.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/speeduino/speeduino.ino b/speeduino/speeduino.ino index 2027c528..984da79c 100644 --- a/speeduino/speeduino.ino +++ b/speeduino/speeduino.ino @@ -1126,14 +1126,14 @@ void loop() if (pw4percent != 100) { currentStatus.PW4 = (pw4percent * currentStatus.PW4) / 100; } } } - if(configPage11.stagingEnabled == true) + else if(configPage11.stagingEnabled == true) { PWdivTimerPerDegree = div(currentStatus.PW3, timePerDegree).quot; //Need to redo this for PW3 as it will be dramatically different to PW1 when staging injector3StartAngle = configPage1.inj3Ang - ( PWdivTimerPerDegree ); //This is a little primitive, but is based on the idea that all fuel needs to be delivered before the inlet valve opens. See http://www.extraefi.co.uk/sequential_fuel.html for more detail if(injector3StartAngle < 0) {injector3StartAngle += CRANK_ANGLE_MAX_INJ;} if(injector3StartAngle > CRANK_ANGLE_MAX_INJ) {injector3StartAngle -= CRANK_ANGLE_MAX_INJ;} - injector4StartAngle = injector3StartAngle + (CRANK_ANGLE_MAX_INJ / 2); + injector4StartAngle = injector3StartAngle + (CRANK_ANGLE_MAX_INJ / 2); //Phase this either 180 or 360 degrees out from inj3 (In reality this will always be 180 as you can't have sequential and staged currently) if(injector4StartAngle < 0) {injector4StartAngle += CRANK_ANGLE_MAX_INJ;} if(injector4StartAngle > CRANK_ANGLE_MAX_INJ) {injector4StartAngle -= CRANK_ANGLE_MAX_INJ;} } From 654aa728d59fc90695b78307747b9afd12b5e894 Mon Sep 17 00:00:00 2001 From: Josh Stewart Date: Wed, 3 Jan 2018 20:29:13 +1100 Subject: [PATCH 3/5] Disable testing on Bluepill until ROM size issue is resolved --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e818dfea..957097c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,7 +60,8 @@ script: - git clone --depth=20 https://github.com/noisymime/cppcheck.git noisymime/cppcheck - cd noisymime/speeduino - platformio update - - platformio run -e megaatmega2560 -e teensy35 -e bluepill_f103c8 -e genericSTM32F103RB +# - platformio run -e megaatmega2560 -e teensy35 -e bluepill_f103c8 -e genericSTM32F103RB + - platformio run -e megaatmega2560 -e teensy35 -e genericSTM32F103RB - cd .. - chmod +x speeduino/misra/check_misra.sh - speeduino/misra/check_misra.sh From a2e1379fd6f39804beaf7166914797947ca95ec2 Mon Sep 17 00:00:00 2001 From: Josh Stewart Date: Wed, 3 Jan 2018 20:48:35 +1100 Subject: [PATCH 4/5] MISRA cleanup --- speeduino/decoders.h | 4 ++-- speeduino/decoders.ino | 7 ++----- speeduino/scheduler.ino | 17 +++++++++-------- speeduino/speeduino.ino | 12 ++++++++---- speeduino/utils.ino | 2 +- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/speeduino/decoders.h b/speeduino/decoders.h index c5c30cdc..5ece0970 100644 --- a/speeduino/decoders.h +++ b/speeduino/decoders.h @@ -58,8 +58,8 @@ volatile int triggerActualTeeth; volatile unsigned long triggerFilterTime; // The shortest time (in uS) that pulses will be accepted (Used for debounce filtering) unsigned long triggerSecFilterTime; // The shortest time (in uS) that pulses will be accepted (Used for debounce filtering) for the secondary input unsigned int triggerSecFilterTime_duration; // The shortest valid time (in uS) pulse DURATION -volatile int triggerToothAngle; //The number of crank degrees that elapse per tooth -volatile bool triggerToothAngleIsCorrect = false; //Whether or not the triggerToothAngle variable is currently accurate. Some patterns have times when the triggerToothAngle variable cannot be accurately set. +volatile uint16_t triggerToothAngle; //The number of crank degrees that elapse per tooth +volatile bool triggerToothAngleIsCorrect = false; //Whether or not the triggerToothAngle variable is currently accurate. Some patterns have times when the triggerToothAngle variable cannot be accurately set. bool secondDerivEnabled; //The use of the 2nd derivative calculation is limited to certain decoders. This is set to either true or false in each decoders setup routine bool decoderIsSequential; //Whether or not the decoder supports sequential operation bool decoderIsLowRes = false; //Is set true, certain extra calculations are performed for better timing accuracy diff --git a/speeduino/decoders.ino b/speeduino/decoders.ino index 467a821a..51ea82ab 100644 --- a/speeduino/decoders.ino +++ b/speeduino/decoders.ino @@ -926,13 +926,12 @@ int getCrankAngle_4G63(int timePerDegree) { //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; - int tempToothCurrentCount, tempToothAngle; + 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; - tempToothAngle = triggerToothAngle; interrupts(); crankAngle = toothAngles[(tempToothCurrentCount - 1)] + configPage2.triggerAngle; //Perform a lookup of the fixed toothAngles array to find what the angle of the last tooth passed was. @@ -941,7 +940,7 @@ int getCrankAngle_4G63(int timePerDegree) unsigned long elapsedTime = micros() - tempToothLastToothTime; //crankAngle += uSToDegrees(elapsedTime); unsigned long toothTime = tempToothLastToothTime - tempToothLastMinusOneToothTime; - crankAngle += (elapsedTime * triggerToothAngle) / toothTime; + crankAngle += int16_t((elapsedTime * triggerToothAngle) / toothTime); //timePerDegree = toothTime / tempToothAngle; if (crankAngle >= 720) { crankAngle -= 720; } @@ -2527,8 +2526,6 @@ void triggerPri_ThirtySixMinus222() if ( (curGap > targetGap) ) { - //if(toothCurrentCount < (triggerActualTeeth) && (currentStatus.hasSync == true) ) { currentStatus.hasSync = false; } //This occurs when we're at tooth #1, but haven't seen all the other teeth. This indicates a signal issue so we flag lost sync so this will attempt to resync on the next revolution. - //else { if(toothSystemCount == 1) { diff --git a/speeduino/scheduler.ino b/speeduino/scheduler.ino index 7cfb7ab4..7e452d78 100644 --- a/speeduino/scheduler.ino +++ b/speeduino/scheduler.ino @@ -725,21 +725,22 @@ static inline void ignitionSchedule1Interrupt() //Most ARM chips can simply call ignitionSchedule1.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback) ignitionSchedule1.startTime = micros(); IGN1_COMPARE = ignitionSchedule1.endCompare; - } - /* - This code is all to do with the staged ignition timing testing. That is, calling this interrupt slightly before the true ignition point and recalculating the end time for more accuracy - IGN1_COMPARE = ignitionSchedule1.endCompare - 20; - ignitionSchedule1.Status = STAGED; - } + //This code is all to do with the staged ignition timing testing. That is, calling this interrupt slightly before the true ignition point and recalculating the end time for more accuracy + //IGN1_COMPARE = ignitionSchedule1.endCompare - 50; + //ignitionSchedule1.Status = STAGED; + } else if (ignitionSchedule1.Status == STAGED) { int16_t crankAngle = getCrankAngle(timePerDegree); + if(crankAngle > CRANK_ANGLE_MAX_IGN) { crankAngle -= CRANK_ANGLE_MAX_IGN; } if(ignition1EndAngle > crankAngle) - { IGN1_COMPARE = IGN1_COUNTER + uS_TO_TIMER_COMPARE( (ignition1EndAngle - crankAngle) * timePerDegree ); } + { + IGN1_COMPARE = IGN1_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS((ignition1EndAngle - crankAngle)) ); + } else { IGN1_COMPARE = ignitionSchedule1.endCompare; } ignitionSchedule1.Status = RUNNING; - }*/ + } else if (ignitionSchedule1.Status == RUNNING) { ignitionSchedule1.EndCallback(); diff --git a/speeduino/speeduino.ino b/speeduino/speeduino.ino index 984da79c..2421334f 100644 --- a/speeduino/speeduino.ino +++ b/speeduino/speeduino.ino @@ -1452,10 +1452,14 @@ void loop() if( (ignitionSchedule1.Status == RUNNING) && (ignition1EndAngle > crankAngle) && configPage2.StgCycles == 0) { unsigned long uSToEnd = 0; - //if(ignition1EndAngle > crankAngle) { uSToEnd = fastDegreesToUS( (ignition1EndAngle - crankAngle) ); } - //else { uSToEnd = fastDegreesToUS( (360 + ignition1EndAngle - crankAngle) ); } - uSToEnd = fastDegreesToUS( (ignition1EndAngle - crankAngle) ); - //uSToEnd = ((ignition1EndAngle - crankAngle) * (toothLastToothTime - toothLastMinusOneToothTime)) / triggerToothAngle; + + 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; + ********* refreshIgnitionSchedule1( uSToEnd + fixedCrankingOverride ); } diff --git a/speeduino/utils.ino b/speeduino/utils.ino index 593e18ad..5108b94e 100644 --- a/speeduino/utils.ino +++ b/speeduino/utils.ino @@ -315,7 +315,7 @@ void setPinMapping(byte boardID) pinCoil2 = 24; //Done pinCoil3 = 51; //Won't work (No mapping for pin 32) pinCoil4 = 52; //Won't work (No mapping for pin 33) - pinFuelPump = 53; //Won't work (No mapping for pin 37) + pinFuelPump = 26; //Requires PVT4 adapter or above pinFan = 50; //Won't work (No mapping for pin 35) pinTachOut = 28; //Done #endif From f474a95dc5850047accb93f09b118d0d6538864e Mon Sep 17 00:00:00 2001 From: Josh Stewart Date: Wed, 3 Jan 2018 21:52:25 +1100 Subject: [PATCH 5/5] December 2017 signature and base tunes --- reference/Base Tunes/NA6 PNP base tune.msq | 42 +++- reference/Speeduino base tune.msq | 234 ++++++++++++++++++++- reference/speeduino.ini | 2 +- speeduino/comms.ino | 4 +- speeduino/decoders.ino | 2 +- 5 files changed, 264 insertions(+), 20 deletions(-) diff --git a/reference/Base Tunes/NA6 PNP base tune.msq b/reference/Base Tunes/NA6 PNP base tune.msq index aad7b9b0..4cec1979 100644 --- a/reference/Base Tunes/NA6 PNP base tune.msq +++ b/reference/Base Tunes/NA6 PNP base tune.msq @@ -1,7 +1,7 @@ - - + + "CAN ID 0" @@ -113,12 +113,14 @@ "Four-stroke" "Port" "4" +"None" +"None" "4" "Even fire" "Off" "Speed Density" "Off" -"Semi-Sequential" +"Paired" "No" "Off" 2.0 @@ -207,7 +209,7 @@ 77.0 - 77.0 + 255.0 @@ -274,7 +276,6 @@ "Leading" "Crank Speed" "Going Low" -"Yes" "4G63 / Miata / 3000GT" "Trailing" "Board Default" @@ -420,7 +421,7 @@ 1.0 - 134.0 + 255.0 @@ -526,8 +527,8 @@ 20.0 35.0 50.0 - 60.0 - 90.0 + 62.0 + 91.0 120.0 @@ -537,7 +538,7 @@ 100.0 95.0 91.0 - 88.0 + 83.0 81.0 74.0 @@ -860,6 +861,22 @@ "Off" "Off" "Off" +"0x100" +"0x100" +"0x100" +"0x100" +"0x100" +"0x100" +"0x100" +"0x100" +"0x100" +"0x100" +"0x100" +"0x100" +"0x100" +"0x100" +"0x100" +"0x100" "7" "1" "1" @@ -926,6 +943,9 @@ 0.0 0.0 "CAN ID 0" +"0x100" +"0x100" +"0x100" 0.0 0.0 0.0 @@ -946,7 +966,7 @@ 0.0 0.0 0.0 -132.0 +1.0 @@ -1154,7 +1174,7 @@ 0.0 - 0.0 + 255.0 diff --git a/reference/Speeduino base tune.msq b/reference/Speeduino base tune.msq index 86bff3c7..c5ee8b72 100644 --- a/reference/Speeduino base tune.msq +++ b/reference/Speeduino base tune.msq @@ -1,7 +1,7 @@ - - + + "CAN ID 0" @@ -144,6 +144,70 @@ 0.0 0.0 20.0 +1.0 +315.0 + + 62.0 + 62.0 + 60.0 + 61.0 + 64.0 + 65.0 + 66.0 + 68.0 + 68.0 + 70.0 + 71.0 + 71.0 + 71.0 + 71.0 + 66.0 + 59.0 + 60.0 + 60.0 + 61.0 + 63.0 + 64.0 + 66.0 + 67.0 + 68.0 + 69.0 + 70.0 + 71.0 + 72.0 + 74.0 + 69.0 + 61.0 + 59.0 + 60.0 + 61.0 + 62.0 + 64.0 + 66.0 + 67.0 + 69.0 + 70.0 + 71.0 + 72.0 + 72.0 + 73.0 + 74.0 + 64.0 + 59.0 + 61.0 + 60.0 + 61.0 + 64.0 + 65.0 + 67.0 + 68.0 + 68.0 + 70.0 + 71.0 + 71.0 + 71.0 + 71.0 + 255.0 @@ -212,7 +276,6 @@ "Leading" "Crank Speed" "Going Low" -"No" "Missing Tooth" "Leading" "Board Default" @@ -621,8 +684,8 @@ 100.0 - 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 - 11.0 10.0 6.0 6.0 6.0 6.0 7.0 7.0 + 175.0 0.0 0.0 27.0 4.0 4.0 0.0 0.0 + 99.0 8.0 6.0 0.0 6.0 6.0 0.0 1.0 24.0 22.0 20.0 18.0 18.0 18.0 19.0 20.0 32.0 30.0 29.0 28.0 27.0 27.0 27.0 29.0 39.0 39.0 38.0 38.0 37.0 37.0 36.0 36.0 @@ -933,6 +996,167 @@ 20.0 200.0 600.0 + + 700.0 + 25500.0 + 700.0 + 700.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 700.0 + 700.0 + 700.0 + 700.0 + 700.0 + 700.0 + 0.0 + 0.0 + 25500.0 + 25500.0 + 100.0 + 100.0 + 100.0 + 100.0 + 100.0 + 100.0 + 100.0 + 100.0 + 100.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 700.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 100.0 + 0.0 + 200.0 + 200.0 + 100.0 + 100.0 + 100.0 + 200.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 0.0 + 100.0 + 0.0 + 100.0 + 100.0 + 25500.0 + 100.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 25500.0 + 1000.0 + 5000.0 + 7500.0 + 11000.0 + 15000.0 + 12000.0 + 10900.0 + 9600.0 + 0.0 + 1200.0 + 1000.0 + 600.0 + 500.0 + 1000.0 + 1000.0 + 1300.0 + 1500.0 + 1500.0 + 2500.0 + 3800.0 + 4500.0 + 5000.0 + 6500.0 + 9000.0 + 12500.0 + 21600.0 + 100.0 + 3000.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 255.0 diff --git a/reference/speeduino.ini b/reference/speeduino.ini index 52aecfcf..702c52e5 100644 --- a/reference/speeduino.ini +++ b/reference/speeduino.ini @@ -6,7 +6,7 @@ MTversion = 2.25 queryCommand = "Q" - signature = "speeduino 201712-dev" + signature = "speeduino 201712" versionInfo = "S" ;This info is what is displayed to user [TunerStudio] diff --git a/speeduino/comms.ino b/speeduino/comms.ino index 294be1a4..d3f719de 100644 --- a/speeduino/comms.ino +++ b/speeduino/comms.ino @@ -132,7 +132,7 @@ void command() break; case 'Q': // send code version - Serial.print("speeduino 201712-dev"); + Serial.print("speeduino 201712"); break; case 'r': //New format for the optimised OutputChannels @@ -162,7 +162,7 @@ void command() break; case 'S': // send code version - Serial.print("Speeduino 2017.12-dev"); + Serial.print("Speeduino 2017.12"); currentStatus.secl = 0; //This is required in TS3 due to its stricter timings break; diff --git a/speeduino/decoders.ino b/speeduino/decoders.ino index 51ea82ab..abe970d1 100644 --- a/speeduino/decoders.ino +++ b/speeduino/decoders.ino @@ -940,7 +940,7 @@ int getCrankAngle_4G63(int timePerDegree) unsigned long elapsedTime = micros() - tempToothLastToothTime; //crankAngle += uSToDegrees(elapsedTime); unsigned long toothTime = tempToothLastToothTime - tempToothLastMinusOneToothTime; - crankAngle += int16_t((elapsedTime * triggerToothAngle) / toothTime); + crankAngle += int((elapsedTime * triggerToothAngle) / toothTime); //timePerDegree = toothTime / tempToothAngle; if (crankAngle >= 720) { crankAngle -= 720; }