diff --git a/firmware/CHANGELOG.md b/firmware/CHANGELOG.md index fad5759c9a..323e1d0be8 100644 --- a/firmware/CHANGELOG.md +++ b/firmware/CHANGELOG.md @@ -32,6 +32,7 @@ Release template (copy/paste this for new release): - Log per-cylinder true ignition timing (includes trim, knock retard, etc) #76 - Add mode for CLT/IAT sensors that are installed "high side" instead of typical "low side" #116 - Automatic supply voltage compensation for VVT solenoids, giving more stable control as battery voltage changes #209 + - Configurable ignition timing limits, adding safety particularly for engines running wasted spark with a large cam #211 ### Fixed - Improved bench test resolution (more usable for testing injectors, dwell, etc) diff --git a/firmware/controllers/algo/defaults/default_ignition.cpp b/firmware/controllers/algo/defaults/default_ignition.cpp index 96acbf7446..e97a0dcb6c 100644 --- a/firmware/controllers/algo/defaults/default_ignition.cpp +++ b/firmware/controllers/algo/defaults/default_ignition.cpp @@ -75,6 +75,9 @@ void setDefaultIgnition() { engineConfiguration->timingMode = TM_DYNAMIC; engineConfiguration->fixedModeTiming = 50; + engineConfiguration->minimumIgnitionTiming = -10; + engineConfiguration->maximumIgnitionTiming = 60; + // Dwell table setConstantDwell(4); diff --git a/firmware/controllers/engine_cycle/spark_logic.cpp b/firmware/controllers/engine_cycle/spark_logic.cpp index ccbf7bb4f0..2d506e8299 100644 --- a/firmware/controllers/engine_cycle/spark_logic.cpp +++ b/firmware/controllers/engine_cycle/spark_logic.cpp @@ -80,6 +80,17 @@ static void prepareCylinderIgnitionSchedule(angle_t dwellAngleDuration, floatms_ // Pull any extra timing for knock retard - engine->module()->getKnockRetard(); + // 10 ATDC ends up as 710, convert it to -10 so we can log and clamp correctly + if (finalIgnitionTiming > 360) { + finalIgnitionTiming -= 720; + } + + // Clamp the final ignition timing to the configured limits + // finalIgnitionTiming is deg BTDC + // minimumIgnitionTiming limits maximium retard + // maximumIgnitionTiming limits maximum advance + finalIgnitionTiming = clampF(engineConfiguration->minimumIgnitionTiming, finalIgnitionTiming, engineConfiguration->maximumIgnitionTiming); + engine->outputChannels.ignitionAdvanceCyl[event->cylinderNumber] = finalIgnitionTiming; angle_t sparkAngle = diff --git a/firmware/integration/rusefi_config.txt b/firmware/integration/rusefi_config.txt index 9c20f91879..f1b484d2b3 100644 --- a/firmware/integration/rusefi_config.txt +++ b/firmware/integration/rusefi_config.txt @@ -92,7 +92,7 @@ ! Any time an incompatible change is made to the configuration format stored in flash, ! update this string to the current date! It is required to also update TS_SIGNATURE above ! when this happens. -#define FLASH_DATA_VERSION 20009 +#define FLASH_DATA_VERSION 20010 ! this offset is part of console compatibility mechanism, please DO NOT change this offset #define TS_FILE_VERSION_OFFSET 124 @@ -1169,7 +1169,8 @@ int16_t tps2Max;;"ADC", 1, 0, 0, 1023, 0 custom pwm_freq_t 2 scalar, U16, @OFFSET@, "Hz", 1, 0, 0, 3000, 0 pwm_freq_t vvtOutputFrequency - uint16_t unused1538 + int8_t minimumIgnitionTiming;Minimim timing advance allowed. No spark on any cylinder will ever fire after this angle BTDC. For example, setting -10 here means no spark ever fires later than 10 deg ATDC. Note that this only concerns the primary spark: any trailing sparks or multispark may violate this constraint.;"deg BTDC", 1, 0, -90, 90, 0 + int8_t maximumIgnitionTiming;Maximum timing advance allowed. No spark on any cylinder will ever fire before this angle BTDC. For example, setting 45 here means no spark ever fires earlier than 45 deg BTDC;"deg BTDC", 1, 0, -90, 90, 0 int alternatorPwmFrequency;;"Hz", 1, 0, 0, 3000, 0 diff --git a/firmware/tunerstudio/rusefi.input b/firmware/tunerstudio/rusefi.input index 52294210f6..e0d459e5ac 100644 --- a/firmware/tunerstudio/rusefi.input +++ b/firmware/tunerstudio/rusefi.input @@ -2207,10 +2207,12 @@ cmd_set_engine_type_default = "@@TS_IO_TEST_COMMAND_char@@@@ts_command_e_TS_ dialog = ignitionBasic, "" field = "Enabled", isIgnitionEnabled - field = "Mode", ignitionMode, {isIgnitionEnabled == 1} + field = "Mode", ignitionMode, {isIgnitionEnabled} + field = "Maximum timing advance", maximumIgnitionTiming, {isIgnitionEnabled} + field = "Minimum timing advance", minimumIgnitionTiming, {isIgnitionEnabled} field = "Override ignition table load axis", ignOverrideMode, {isIgnitionEnabled} field = "#Use fixed timing while validating with a timing gun" - field = "Timing Mode", timingMode, {isIgnitionEnabled == 1} + field = "Timing Mode", timingMode, {isIgnitionEnabled} field = "Fixed Timing", fixedTiming, {isIgnitionEnabled == 1 && timingMode == 1} dialog = ignitionSettings, "", xAxis diff --git a/unit_tests/tests/trigger/test_trigger_decoder.cpp b/unit_tests/tests/trigger/test_trigger_decoder.cpp index b8dd025e97..61d0b49d66 100644 --- a/unit_tests/tests/trigger/test_trigger_decoder.cpp +++ b/unit_tests/tests/trigger/test_trigger_decoder.cpp @@ -84,6 +84,8 @@ TEST(trigger, test1995FordInline6TriggerDecoder) { EngineTestHelper eth(engine_type_e::FORD_INLINE_6_1995); engineConfiguration->isFasterEngineSpinUpEnabled = false; + + engineConfiguration->minimumIgnitionTiming = -15; setWholeTimingTable(-13); Sensor::setMockValue(SensorType::Iat, 49.579071f); @@ -104,7 +106,9 @@ TEST(trigger, test1995FordInline6TriggerDecoder) { ASSERT_EQ(true, ecl->isReady) << "ford inline ignition events size"; EXPECT_NEAR(ecl->elements[0].dwellAngle, 8.960f, 1e-3); + EXPECT_NEAR(ecl->elements[0].sparkAngle, 14.96f, 1e-3); EXPECT_NEAR(ecl->elements[5].dwellAngle, 608.960f, 1e-3); + EXPECT_NEAR(ecl->elements[5].sparkAngle, 614.960f, 1e-3); ASSERT_FLOAT_EQ(0.5, engine->ignitionState.getSparkDwell(2000)) << "running dwell"; } @@ -188,6 +192,7 @@ TEST(misc, testRpmCalculator) { efiAssertVoid(ObdCode::CUSTOM_ERR_6670, engineConfiguration!=NULL, "null config in engine"); + engineConfiguration->minimumIgnitionTiming = -15; setWholeTimingTable(-13); engineConfiguration->trigger.customTotalToothCount = 8; @@ -237,6 +242,7 @@ TEST(misc, testRpmCalculator) { assertEqualsM("one degree", 111.1111, engine->rpmCalculator.oneDegreeUs); ASSERT_EQ( 1, ilist->isReady) << "size #2"; EXPECT_NEAR(ilist->elements[0].dwellAngle, 8.5f, 1e-3); + EXPECT_NEAR(ilist->elements[0].sparkAngle, 13.0f, 1e-3); ASSERT_EQ( 0, eth.engine.triggerCentral.triggerState.getCurrentIndex()) << "index #2"; ASSERT_EQ( 4, engine->executor.size()) << "queue size/2";