2016-09-15 15:02:36 -07:00
|
|
|
/*
|
|
|
|
* @file spark_logic.cpp
|
|
|
|
*
|
|
|
|
* @date Sep 15, 2016
|
2020-01-07 21:02:40 -08:00
|
|
|
* @author Andrey Belomutskiy, (c) 2012-2020
|
2016-09-15 15:02:36 -07:00
|
|
|
*/
|
|
|
|
|
2021-07-25 22:05:17 -07:00
|
|
|
#include "pch.h"
|
|
|
|
|
2019-12-02 19:28:32 -08:00
|
|
|
#include "spark_logic.h"
|
2019-07-03 18:48:04 -07:00
|
|
|
#include "os_access.h"
|
|
|
|
|
2016-09-21 20:03:22 -07:00
|
|
|
#include "utlist.h"
|
|
|
|
#include "event_queue.h"
|
2020-05-25 21:07:18 -07:00
|
|
|
#include "tooth_logger.h"
|
2016-09-15 15:02:36 -07:00
|
|
|
|
2021-09-21 14:39:21 -07:00
|
|
|
#include "knock_logic.h"
|
2021-03-27 11:12:49 -07:00
|
|
|
|
2020-02-26 15:16:35 -08:00
|
|
|
#if EFI_ENGINE_CONTROL
|
|
|
|
|
2019-08-24 23:01:09 -07:00
|
|
|
#if EFI_UNIT_TEST
|
|
|
|
extern bool verboseMode;
|
|
|
|
#endif /* EFI_UNIT_TEST */
|
2016-09-15 15:02:36 -07:00
|
|
|
|
2020-07-20 10:38:33 -07:00
|
|
|
#if EFI_PRINTF_FUEL_DETAILS || FUEL_MATH_EXTREME_LOGGING
|
|
|
|
extern bool printFuelDebug;
|
|
|
|
#endif // EFI_PRINTF_FUEL_DETAILS
|
|
|
|
|
2016-09-21 20:03:22 -07:00
|
|
|
static cyclic_buffer<int> ignitionErrorDetection;
|
2016-09-15 15:02:36 -07:00
|
|
|
|
2019-09-22 05:22:35 -07:00
|
|
|
static const char *prevSparkName = nullptr;
|
2016-11-02 20:01:48 -07:00
|
|
|
|
2016-09-21 20:03:22 -07:00
|
|
|
int isIgnitionTimingError(void) {
|
|
|
|
return ignitionErrorDetection.sum(6) > 4;
|
|
|
|
}
|
|
|
|
|
2019-05-07 14:10:47 -07:00
|
|
|
static void fireSparkBySettingPinLow(IgnitionEvent *event, IgnitionOutputPin *output) {
|
2019-10-14 03:18:08 -07:00
|
|
|
#if EFI_UNIT_TEST
|
|
|
|
Engine *engine = event->engine;
|
|
|
|
#endif /* EFI_UNIT_TEST */
|
|
|
|
|
2021-07-17 13:12:54 -07:00
|
|
|
efitick_t nowNt = getTimeNowNt();
|
|
|
|
engine->mostRecentTimeBetweenSparkEvents = nowNt - engine->mostRecentSparkEvent;
|
|
|
|
engine->mostRecentSparkEvent = nowNt;
|
|
|
|
|
2019-04-12 19:07:03 -07:00
|
|
|
#if SPARK_EXTREME_LOGGING
|
2021-04-21 09:53:13 -07:00
|
|
|
efiPrintf("spark goes low %d %s %d current=%d cnt=%d id=%d", getRevolutionCounter(), output->name, (int)getTimeNowUs(),
|
2016-11-01 20:01:54 -07:00
|
|
|
output->currentLogicValue, output->outOfOrder, event->sparkId);
|
2020-07-20 10:38:33 -07:00
|
|
|
#endif /* SPARK_EXTREME_LOGGING */
|
2016-10-31 19:02:12 -07:00
|
|
|
|
2016-11-01 18:03:07 -07:00
|
|
|
/**
|
|
|
|
* there are two kinds of 'out-of-order'
|
|
|
|
* 1) low goes before high, everything is fine after words
|
|
|
|
*
|
|
|
|
* 2) we have an un-matched low followed by legit pairs
|
|
|
|
*/
|
|
|
|
|
2016-11-01 20:01:54 -07:00
|
|
|
output->signalFallSparkId = event->sparkId;
|
2016-11-01 18:03:07 -07:00
|
|
|
|
2016-10-31 19:02:12 -07:00
|
|
|
if (!output->currentLogicValue) {
|
2019-05-15 01:26:41 -07:00
|
|
|
warning(CUSTOM_OUT_OF_ORDER_COIL, "out-of-order coil off %s", output->getName());
|
2016-11-01 20:01:54 -07:00
|
|
|
output->outOfOrder = true;
|
2016-10-31 19:02:12 -07:00
|
|
|
}
|
2020-12-26 13:16:40 -08:00
|
|
|
#if HW_CHECK_SPARK_FSIO
|
|
|
|
enginePins.fsioOutputs[event->cylinderIndex].setValue(0);
|
|
|
|
#endif // HW_CHECK_SPARK_FSIO
|
2017-04-21 16:23:20 -07:00
|
|
|
output->setLow();
|
2016-09-21 20:03:22 -07:00
|
|
|
}
|
|
|
|
|
2018-07-24 17:15:38 -07:00
|
|
|
// todo: make this a class method?
|
|
|
|
#define assertPinAssigned(output) { \
|
|
|
|
if (!output->isInitialized()) { \
|
2021-06-20 14:25:40 -07:00
|
|
|
warning(CUSTOM_OBD_COIL_PIN_NOT_ASSIGNED, "Pin Not Assigned check configuration #%s", (output)->getName()); \
|
2018-07-24 17:15:38 -07:00
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
2019-11-23 20:49:39 -08:00
|
|
|
static void prepareCylinderIgnitionSchedule(angle_t dwellAngleDuration, floatms_t sparkDwell, IgnitionEvent *event DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2018-07-24 17:15:38 -07:00
|
|
|
// todo: clean up this implementation? does not look too nice as is.
|
|
|
|
|
2019-05-07 14:22:26 -07:00
|
|
|
// let's save planned duration so that we can later compare it with reality
|
|
|
|
event->sparkDwell = sparkDwell;
|
|
|
|
|
2018-07-24 17:15:38 -07:00
|
|
|
// change of sign here from 'before TDC' to 'after TDC'
|
2018-07-24 17:40:44 -07:00
|
|
|
angle_t ignitionPositionWithinEngineCycle = ENGINE(ignitionPositionWithinEngineCycle[event->cylinderIndex]);
|
|
|
|
assertAngleRange(ignitionPositionWithinEngineCycle, "aPWEC", CUSTOM_ERR_6566);
|
2019-11-23 20:49:39 -08:00
|
|
|
// this correction is usually zero (not used)
|
2021-05-27 05:23:28 -07:00
|
|
|
float perCylinderCorrection = CONFIG(timing_offset_cylinder[event->cylinderIndex]);
|
2019-11-23 20:49:39 -08:00
|
|
|
const angle_t sparkAngle = -ENGINE(engineState.timingAdvance) + ignitionPositionWithinEngineCycle + perCylinderCorrection;
|
2019-12-02 17:16:41 -08:00
|
|
|
efiAssertVoid(CUSTOM_SPARK_ANGLE_9, !cisnan(sparkAngle), "findAngle#9");
|
2018-07-24 17:40:44 -07:00
|
|
|
|
2019-12-02 17:16:41 -08:00
|
|
|
efiAssertVoid(CUSTOM_SPARK_ANGLE_1, !cisnan(sparkAngle), "sparkAngle#1");
|
2018-07-24 17:15:38 -07:00
|
|
|
const int index = ENGINE(ignitionPin[event->cylinderIndex]);
|
|
|
|
const int coilIndex = ID2INDEX(getCylinderId(index PASS_ENGINE_PARAMETER_SUFFIX));
|
|
|
|
IgnitionOutputPin *output = &enginePins.coils[coilIndex];
|
|
|
|
|
|
|
|
IgnitionOutputPin *secondOutput;
|
2019-01-20 08:33:40 -08:00
|
|
|
if (getCurrentIgnitionMode(PASS_ENGINE_PARAMETER_SIGNATURE) == IM_WASTED_SPARK && CONFIG(twoWireBatchIgnition)) {
|
2018-07-24 17:15:38 -07:00
|
|
|
int secondIndex = index + CONFIG(specs.cylindersCount) / 2;
|
|
|
|
int secondCoilIndex = ID2INDEX(getCylinderId(secondIndex PASS_ENGINE_PARAMETER_SUFFIX));
|
|
|
|
secondOutput = &enginePins.coils[secondCoilIndex];
|
|
|
|
assertPinAssigned(secondOutput);
|
|
|
|
} else {
|
2019-09-22 05:22:35 -07:00
|
|
|
secondOutput = nullptr;
|
2018-07-24 17:15:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
assertPinAssigned(output);
|
|
|
|
|
|
|
|
event->outputs[0] = output;
|
|
|
|
event->outputs[1] = secondOutput;
|
2019-11-23 20:49:39 -08:00
|
|
|
event->sparkAngle = sparkAngle;
|
2020-08-31 18:05:33 -07:00
|
|
|
// Stash which cylinder we're scheduling so that knock sensing knows which
|
|
|
|
// cylinder just fired
|
|
|
|
event->cylinderNumber = coilIndex;
|
2018-07-24 17:15:38 -07:00
|
|
|
|
2019-11-23 20:49:39 -08:00
|
|
|
angle_t dwellStartAngle = sparkAngle - dwellAngleDuration;
|
|
|
|
efiAssertVoid(CUSTOM_ERR_6590, !cisnan(dwellStartAngle), "findAngle#5");
|
|
|
|
assertAngleRange(dwellStartAngle, "findAngle#a6", CUSTOM_ERR_6550);
|
2019-11-23 21:15:44 -08:00
|
|
|
event->dwellPosition.setAngle(dwellStartAngle PASS_ENGINE_PARAMETER_SUFFIX);
|
2018-07-24 17:15:38 -07:00
|
|
|
|
2019-04-12 19:07:03 -07:00
|
|
|
#if FUEL_MATH_EXTREME_LOGGING
|
2020-07-20 10:38:33 -07:00
|
|
|
if (printFuelDebug) {
|
|
|
|
printf("addIgnitionEvent %s ind=%d\n", output->name, event->dwellPosition.triggerEventIndex);
|
|
|
|
}
|
2021-04-21 09:53:13 -07:00
|
|
|
// efiPrintf("addIgnitionEvent %s ind=%d", output->name, event->dwellPosition->eventIndex);
|
2018-07-24 17:15:38 -07:00
|
|
|
#endif /* FUEL_MATH_EXTREME_LOGGING */
|
|
|
|
}
|
|
|
|
|
2021-07-09 05:37:46 -07:00
|
|
|
static void chargeTrailingSpark(IgnitionOutputPin* pin) {
|
2021-10-01 20:50:32 -07:00
|
|
|
#if SPARK_EXTREME_LOGGING
|
|
|
|
efiPrintf("chargeTrailingSpark %s", pin->name);
|
|
|
|
#endif /* SPARK_EXTREME_LOGGING */
|
2021-07-21 20:36:46 -07:00
|
|
|
pin->setHigh();
|
2021-07-09 05:37:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void fireTrailingSpark(IgnitionOutputPin* pin) {
|
2021-10-01 20:50:32 -07:00
|
|
|
#if SPARK_EXTREME_LOGGING
|
|
|
|
efiPrintf("fireTrailingSpark %s", pin->name);
|
|
|
|
#endif /* SPARK_EXTREME_LOGGING */
|
2021-07-21 20:36:46 -07:00
|
|
|
pin->setLow();
|
2021-07-09 05:37:46 -07:00
|
|
|
}
|
|
|
|
|
2019-05-07 14:10:47 -07:00
|
|
|
void fireSparkAndPrepareNextSchedule(IgnitionEvent *event) {
|
2020-11-15 21:06:11 -08:00
|
|
|
for (int i = 0; i< MAX_OUTPUTS_FOR_IGNITION;i++) {
|
|
|
|
IgnitionOutputPin *output = event->outputs[i];
|
|
|
|
|
|
|
|
if (output) {
|
|
|
|
fireSparkBySettingPinLow(event, output);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-25 21:07:18 -07:00
|
|
|
efitick_t nowNt = getTimeNowNt();
|
|
|
|
|
2020-07-19 19:03:30 -07:00
|
|
|
#if EFI_UNIT_TEST
|
|
|
|
Engine *engine = event->engine;
|
|
|
|
EXPAND_Engine;
|
|
|
|
#endif // EFI_UNIT_TEST
|
|
|
|
|
2020-05-25 21:07:18 -07:00
|
|
|
#if EFI_TOOTH_LOGGER
|
|
|
|
LogTriggerCoilState(nowNt, false PASS_ENGINE_PARAMETER_SUFFIX);
|
|
|
|
#endif // EFI_TOOTH_LOGGER
|
|
|
|
|
2019-05-07 16:16:01 -07:00
|
|
|
#if !EFI_UNIT_TEST
|
|
|
|
if (engineConfiguration->debugMode == DBG_DWELL_METRIC) {
|
2019-11-06 15:26:50 -08:00
|
|
|
#if EFI_TUNER_STUDIO
|
2019-10-07 23:01:41 -07:00
|
|
|
uint32_t actualDwellDurationNt = getTimeNowLowerNt() - event->actualStartOfDwellNt;
|
2019-05-07 18:39:11 -07:00
|
|
|
/**
|
|
|
|
* ratio of desired dwell duration to actual dwell duration gives us some idea of how good is input trigger jitter
|
|
|
|
*/
|
|
|
|
float ratio = NT2US(actualDwellDurationNt) / 1000.0 / event->sparkDwell;
|
|
|
|
|
|
|
|
// todo: smarted solution for index to field mapping
|
2020-02-21 16:35:11 -08:00
|
|
|
switch (event->cylinderIndex) {
|
|
|
|
case 0:
|
2019-05-07 18:39:11 -07:00
|
|
|
tsOutputChannels.debugFloatField1 = ratio;
|
2020-02-21 16:35:11 -08:00
|
|
|
break;
|
|
|
|
case 1:
|
2019-05-07 18:39:11 -07:00
|
|
|
tsOutputChannels.debugFloatField2 = ratio;
|
2020-02-21 16:35:11 -08:00
|
|
|
break;
|
|
|
|
case 2:
|
2019-05-07 18:39:11 -07:00
|
|
|
tsOutputChannels.debugFloatField3 = ratio;
|
2020-02-21 16:35:11 -08:00
|
|
|
break;
|
|
|
|
case 3:
|
2019-05-07 18:39:11 -07:00
|
|
|
tsOutputChannels.debugFloatField4 = ratio;
|
2020-02-21 16:35:11 -08:00
|
|
|
break;
|
2019-05-07 18:39:11 -07:00
|
|
|
}
|
|
|
|
#endif
|
2019-05-07 16:16:01 -07:00
|
|
|
|
2020-01-10 13:01:54 -08:00
|
|
|
}
|
2019-05-07 16:16:01 -07:00
|
|
|
#endif /* EFI_UNIT_TEST */
|
2016-11-29 17:02:41 -08:00
|
|
|
// now that we've just fired a coil let's prepare the new schedule for the next engine revolution
|
2018-07-24 17:40:44 -07:00
|
|
|
|
2019-11-23 20:49:39 -08:00
|
|
|
angle_t dwellAngleDuration = ENGINE(engineState.dwellAngle);
|
2019-05-07 14:22:26 -07:00
|
|
|
floatms_t sparkDwell = ENGINE(engineState.sparkDwell);
|
2019-11-23 20:49:39 -08:00
|
|
|
if (cisnan(dwellAngleDuration) || cisnan(sparkDwell)) {
|
2018-07-24 17:40:44 -07:00
|
|
|
// we are here if engine has just stopped
|
|
|
|
return;
|
|
|
|
}
|
2020-03-25 22:49:36 -07:00
|
|
|
|
|
|
|
// If there are more sparks to fire, schedule them
|
2021-07-09 05:37:46 -07:00
|
|
|
if (event->sparksRemaining > 0) {
|
2020-03-25 22:49:36 -07:00
|
|
|
event->sparksRemaining--;
|
|
|
|
|
|
|
|
efitick_t nextDwellStart = nowNt + engine->engineState.multispark.delay;
|
|
|
|
efitick_t nextFiring = nextDwellStart + engine->engineState.multispark.dwell;
|
|
|
|
|
|
|
|
// We can schedule both of these right away, since we're going for "asap" not "particular angle"
|
2021-07-14 13:03:00 -07:00
|
|
|
engine->executor.scheduleByTimestampNt("dwell", &event->dwellStartTimer, nextDwellStart, { &turnSparkPinHigh, event });
|
|
|
|
engine->executor.scheduleByTimestampNt("firing", &event->sparkEvent.scheduling, nextFiring, { fireSparkAndPrepareNextSchedule, event });
|
2021-07-09 05:37:46 -07:00
|
|
|
} else {
|
|
|
|
if (CONFIG(enableTrailingSparks)) {
|
2021-10-01 20:50:32 -07:00
|
|
|
#if SPARK_EXTREME_LOGGING
|
|
|
|
efiPrintf("scheduleByAngle TrailingSparks");
|
|
|
|
#endif /* SPARK_EXTREME_LOGGING */
|
|
|
|
|
2021-07-09 05:37:46 -07:00
|
|
|
// Trailing sparks are enabled - schedule an event for the corresponding trailing coil
|
|
|
|
scheduleByAngle(
|
|
|
|
&event->trailingSparkFire, nowNt, ENGINE(engineState.trailingSparkAngle),
|
|
|
|
{ &fireTrailingSpark, &enginePins.trailingCoils[event->cylinderNumber] }
|
|
|
|
PASS_ENGINE_PARAMETER_SUFFIX
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-03-25 22:49:36 -07:00
|
|
|
// If all events have been scheduled, prepare for next time.
|
|
|
|
prepareCylinderIgnitionSchedule(dwellAngleDuration, sparkDwell, event PASS_ENGINE_PARAMETER_SUFFIX);
|
|
|
|
}
|
2020-08-28 18:13:50 -07:00
|
|
|
|
2021-09-21 14:39:21 -07:00
|
|
|
engine->onSparkFireKnockSense(event->cylinderNumber, nowNt);
|
2016-11-26 21:01:22 -08:00
|
|
|
}
|
|
|
|
|
2019-05-07 14:10:47 -07:00
|
|
|
static void startDwellByTurningSparkPinHigh(IgnitionEvent *event, IgnitionOutputPin *output) {
|
2019-10-14 03:18:08 -07:00
|
|
|
#if EFI_UNIT_TEST
|
|
|
|
Engine *engine = event->engine;
|
|
|
|
EXPAND_Engine;
|
|
|
|
#endif /* EFI_UNIT_TEST */
|
2016-11-07 19:02:21 -08:00
|
|
|
|
2019-10-14 03:18:08 -07:00
|
|
|
// todo: no reason for this to be disabled in unit_test mode?!
|
2019-04-12 19:07:03 -07:00
|
|
|
#if ! EFI_UNIT_TEST
|
2019-05-07 18:39:11 -07:00
|
|
|
|
2020-09-03 16:29:15 -07:00
|
|
|
if (GET_RPM() > 2 * engineConfiguration->cranking.rpm) {
|
2019-05-15 01:26:41 -07:00
|
|
|
const char *outputName = output->getName();
|
2019-01-20 08:33:40 -08:00
|
|
|
if (prevSparkName == outputName && getCurrentIgnitionMode(PASS_ENGINE_PARAMETER_SIGNATURE) != IM_ONE_COIL) {
|
2016-11-07 19:02:21 -08:00
|
|
|
warning(CUSTOM_OBD_SKIPPED_SPARK, "looks like skipped spark event %d %s", getRevolutionCounter(), outputName);
|
|
|
|
}
|
|
|
|
prevSparkName = outputName;
|
|
|
|
}
|
|
|
|
#endif /* EFI_UNIT_TEST */
|
|
|
|
|
|
|
|
|
2019-04-12 19:07:03 -07:00
|
|
|
#if SPARK_EXTREME_LOGGING
|
2021-04-21 09:53:13 -07:00
|
|
|
efiPrintf("spark goes high %d %s %d current=%d cnt=%d id=%d", getRevolutionCounter(), output->name, (int)getTimeNowUs(),
|
2016-11-01 20:01:54 -07:00
|
|
|
output->currentLogicValue, output->outOfOrder, event->sparkId);
|
2020-07-20 10:38:33 -07:00
|
|
|
#endif /* SPARK_EXTREME_LOGGING */
|
2016-10-31 19:02:12 -07:00
|
|
|
|
2016-11-01 20:01:54 -07:00
|
|
|
if (output->outOfOrder) {
|
|
|
|
output->outOfOrder = false;
|
|
|
|
if (output->signalFallSparkId == event->sparkId) {
|
2016-11-01 18:03:07 -07:00
|
|
|
// let's save this coil if things do not look right
|
|
|
|
return;
|
|
|
|
}
|
2016-10-31 19:02:12 -07:00
|
|
|
}
|
|
|
|
|
2020-12-26 13:16:40 -08:00
|
|
|
#if HW_CHECK_SPARK_FSIO
|
|
|
|
enginePins.fsioOutputs[event->cylinderIndex].setValue(1);
|
|
|
|
#endif // HW_CHECK_SPARK_FSIO
|
2021-04-04 19:41:38 -07:00
|
|
|
INJECT_ENGINE_REFERENCE(output);
|
2017-04-21 16:23:20 -07:00
|
|
|
output->setHigh();
|
2016-09-21 20:03:22 -07:00
|
|
|
}
|
|
|
|
|
2016-11-26 21:01:22 -08:00
|
|
|
void turnSparkPinHigh(IgnitionEvent *event) {
|
2019-10-07 23:01:41 -07:00
|
|
|
event->actualStartOfDwellNt = getTimeNowLowerNt();
|
2020-05-25 21:07:18 -07:00
|
|
|
|
|
|
|
efitick_t nowNt = getTimeNowNt();
|
|
|
|
|
|
|
|
#if EFI_TOOTH_LOGGER
|
2020-07-19 19:03:30 -07:00
|
|
|
#if EFI_UNIT_TEST
|
|
|
|
Engine *engine = event->engine;
|
|
|
|
EXPAND_Engine;
|
|
|
|
#endif // EFI_UNIT_TEST
|
2020-05-25 21:07:18 -07:00
|
|
|
LogTriggerCoilState(nowNt, true PASS_ENGINE_PARAMETER_SUFFIX);
|
|
|
|
#endif // EFI_TOOTH_LOGGER
|
|
|
|
|
2016-11-27 18:04:45 -08:00
|
|
|
for (int i = 0; i< MAX_OUTPUTS_FOR_IGNITION;i++) {
|
|
|
|
IgnitionOutputPin *output = event->outputs[i];
|
|
|
|
if (output != NULL) {
|
2019-05-07 14:10:47 -07:00
|
|
|
startDwellByTurningSparkPinHigh(event, output);
|
2016-11-27 18:04:45 -08:00
|
|
|
}
|
|
|
|
}
|
2021-07-09 05:37:46 -07:00
|
|
|
|
|
|
|
if (CONFIG(enableTrailingSparks)) {
|
2021-07-21 21:01:54 -07:00
|
|
|
IgnitionOutputPin *output = &enginePins.trailingCoils[event->cylinderNumber];
|
|
|
|
INJECT_ENGINE_REFERENCE(output);
|
2021-07-09 05:37:46 -07:00
|
|
|
// Trailing sparks are enabled - schedule an event for the corresponding trailing coil
|
|
|
|
scheduleByAngle(
|
|
|
|
&event->trailingSparkCharge, nowNt, ENGINE(engineState.trailingSparkAngle),
|
2021-07-21 21:01:54 -07:00
|
|
|
{ &chargeTrailingSpark, output }
|
2021-07-09 05:37:46 -07:00
|
|
|
PASS_ENGINE_PARAMETER_SUFFIX
|
|
|
|
);
|
|
|
|
}
|
2016-11-26 21:01:22 -08:00
|
|
|
}
|
|
|
|
|
2019-11-23 17:36:40 -08:00
|
|
|
static bool assertNotInIgnitionList(AngleBasedEvent *head, AngleBasedEvent *element) {
|
|
|
|
assertNotInListMethodBody(AngleBasedEvent, head, element, nextToothEvent)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true if event corresponds to current tooth and was time-based scheduler
|
|
|
|
* false if event was put into queue for scheduling at a later tooth
|
|
|
|
*/
|
2019-12-02 19:28:32 -08:00
|
|
|
bool scheduleOrQueue(AngleBasedEvent *event,
|
|
|
|
uint32_t trgEventIndex,
|
2020-01-10 13:01:54 -08:00
|
|
|
efitick_t edgeTimestamp,
|
2019-12-02 19:28:32 -08:00
|
|
|
angle_t angle,
|
2020-01-06 21:41:18 -08:00
|
|
|
action_s action
|
|
|
|
DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2019-11-23 21:15:44 -08:00
|
|
|
event->position.setAngle(angle PASS_ENGINE_PARAMETER_SUFFIX);
|
2019-11-23 17:36:40 -08:00
|
|
|
|
|
|
|
/**
|
2020-01-10 13:01:54 -08:00
|
|
|
* Here's the status as of Jan 2020:
|
|
|
|
* Once we hit the last trigger tooth prior to needed event, schedule it by time. We use as much trigger position angle as possible
|
2019-11-23 17:36:40 -08:00
|
|
|
* and only use less precise RPM-based time calculation for the last portion of the angle, the one between two teeth closest to the
|
|
|
|
* desired angle moment.
|
|
|
|
*/
|
2019-12-02 19:28:32 -08:00
|
|
|
if (trgEventIndex != TRIGGER_EVENT_UNDEFINED && event->position.triggerEventIndex == trgEventIndex) {
|
2019-11-23 17:36:40 -08:00
|
|
|
/**
|
|
|
|
* Spark should be fired before the next trigger event - time-based delay is best precision possible
|
|
|
|
*/
|
|
|
|
scheduling_s * sDown = &event->scheduling;
|
|
|
|
|
2020-01-10 13:01:54 -08:00
|
|
|
scheduleByAngle(
|
|
|
|
sDown,
|
|
|
|
edgeTimestamp,
|
|
|
|
event->position.angleOffsetFromTriggerEvent,
|
|
|
|
action
|
|
|
|
PASS_ENGINE_PARAMETER_SUFFIX
|
|
|
|
);
|
|
|
|
|
2019-11-23 17:36:40 -08:00
|
|
|
return true;
|
|
|
|
} else {
|
2020-01-06 21:41:18 -08:00
|
|
|
event->action = action;
|
2019-11-23 17:36:40 -08:00
|
|
|
/**
|
|
|
|
* Spark should be scheduled in relation to some future trigger event, this way we get better firing precision
|
|
|
|
*/
|
2019-12-03 11:39:11 -08:00
|
|
|
bool isPending = assertNotInIgnitionList(ENGINE(angleBasedEventsHead), event);
|
2019-11-23 17:36:40 -08:00
|
|
|
if (isPending) {
|
|
|
|
#if SPARK_EXTREME_LOGGING
|
2021-04-21 09:53:13 -07:00
|
|
|
efiPrintf("isPending thus not adding to queue index=%d rev=%d now=%d", trgEventIndex, getRevolutionCounter(), (int)getTimeNowUs());
|
2020-07-20 10:38:33 -07:00
|
|
|
#endif /* SPARK_EXTREME_LOGGING */
|
2019-11-23 17:36:40 -08:00
|
|
|
} else {
|
2019-12-03 11:39:11 -08:00
|
|
|
LL_APPEND2(ENGINE(angleBasedEventsHead), event, nextToothEvent);
|
2019-11-23 17:36:40 -08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2019-11-23 14:04:51 -08:00
|
|
|
}
|
|
|
|
|
2020-11-23 17:10:17 -08:00
|
|
|
static void handleSparkEvent(bool limitedSpark, uint32_t trgEventIndex, IgnitionEvent *event,
|
2020-01-10 13:01:54 -08:00
|
|
|
int rpm, efitick_t edgeTimestamp DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2016-09-21 20:03:22 -07:00
|
|
|
|
2020-02-21 16:35:11 -08:00
|
|
|
angle_t sparkAngle = event->sparkAngle;
|
2016-10-09 16:03:51 -07:00
|
|
|
const floatms_t dwellMs = ENGINE(engineState.sparkDwell);
|
|
|
|
if (cisnan(dwellMs) || dwellMs <= 0) {
|
2018-01-23 09:05:14 -08:00
|
|
|
warning(CUSTOM_DWELL, "invalid dwell to handle: %.2f at %d", dwellMs, rpm);
|
2016-09-21 20:03:22 -07:00
|
|
|
return;
|
|
|
|
}
|
2019-11-23 20:49:39 -08:00
|
|
|
if (cisnan(sparkAngle)) {
|
2018-07-26 14:11:47 -07:00
|
|
|
warning(CUSTOM_ERR_6688, "NaN advance");
|
|
|
|
return;
|
|
|
|
}
|
2016-09-21 20:03:22 -07:00
|
|
|
|
2020-11-23 21:06:12 -08:00
|
|
|
float angleOffset = event->dwellPosition.angleOffsetFromTriggerEvent;
|
|
|
|
int isIgnitionError = angleOffset < 0;
|
2016-09-21 20:03:22 -07:00
|
|
|
ignitionErrorDetection.add(isIgnitionError);
|
|
|
|
if (isIgnitionError) {
|
2019-04-12 19:07:03 -07:00
|
|
|
#if EFI_PROD_CODE
|
2021-04-21 09:53:13 -07:00
|
|
|
efiPrintf("Negative spark delay=%.1f deg", angleOffset);
|
2017-06-08 21:05:41 -07:00
|
|
|
#endif /* EFI_PROD_CODE */
|
2016-09-21 20:03:22 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-21 16:35:11 -08:00
|
|
|
event->sparkId = engine->globalSparkIdCounter++;
|
2016-11-01 18:03:07 -07:00
|
|
|
|
2021-10-01 22:10:24 -07:00
|
|
|
efitick_t chargeTime = 0;
|
|
|
|
|
2016-09-21 20:03:22 -07:00
|
|
|
/**
|
|
|
|
* The start of charge is always within the current trigger event range, so just plain time-based scheduling
|
|
|
|
*/
|
|
|
|
if (!limitedSpark) {
|
2019-04-12 19:07:03 -07:00
|
|
|
#if SPARK_EXTREME_LOGGING
|
2021-04-21 09:53:13 -07:00
|
|
|
efiPrintf("scheduling sparkUp ind=%d %d %s now=%d %d later id=%d", trgEventIndex, getRevolutionCounter(), event->getOutputForLoggins()->name, (int)getTimeNowUs(), (int)angleOffset,
|
2020-02-21 16:35:11 -08:00
|
|
|
event->sparkId);
|
2020-07-20 10:38:33 -07:00
|
|
|
#endif /* SPARK_EXTREME_LOGGING */
|
2016-10-29 14:03:45 -07:00
|
|
|
|
2016-11-02 20:01:48 -07:00
|
|
|
|
2016-10-29 14:03:45 -07:00
|
|
|
/**
|
2016-09-21 20:03:22 -07:00
|
|
|
* Note how we do not check if spark is limited or not while scheduling 'spark down'
|
|
|
|
* This way we make sure that coil dwell started while spark was enabled would fire and not burn
|
|
|
|
* the coil.
|
|
|
|
*/
|
2021-10-01 22:10:24 -07:00
|
|
|
chargeTime = scheduleByAngle(&event->dwellStartTimer, edgeTimestamp, angleOffset, { &turnSparkPinHigh, event } PASS_ENGINE_PARAMETER_SUFFIX);
|
2020-03-25 22:49:36 -07:00
|
|
|
|
|
|
|
event->sparksRemaining = ENGINE(engineState.multispark.count);
|
|
|
|
} else {
|
|
|
|
// don't fire multispark if spark is cut completely!
|
|
|
|
event->sparksRemaining = 0;
|
2016-09-21 20:03:22 -07:00
|
|
|
}
|
2020-03-25 22:49:36 -07:00
|
|
|
|
2016-09-21 20:03:22 -07:00
|
|
|
/**
|
|
|
|
* Spark event is often happening during a later trigger event timeframe
|
|
|
|
*/
|
2018-07-26 14:11:47 -07:00
|
|
|
|
2019-11-23 20:49:39 -08:00
|
|
|
efiAssertVoid(CUSTOM_ERR_6591, !cisnan(sparkAngle), "findAngle#4");
|
|
|
|
assertAngleRange(sparkAngle, "findAngle#a5", CUSTOM_ERR_6549);
|
2016-09-21 20:03:22 -07:00
|
|
|
|
2020-02-21 16:35:11 -08:00
|
|
|
bool scheduled = scheduleOrQueue(&event->sparkEvent, trgEventIndex, edgeTimestamp, sparkAngle, { fireSparkAndPrepareNextSchedule, event } PASS_ENGINE_PARAMETER_SUFFIX);
|
2016-09-21 20:03:22 -07:00
|
|
|
|
2019-11-23 17:36:40 -08:00
|
|
|
if (scheduled) {
|
2019-04-12 19:07:03 -07:00
|
|
|
#if SPARK_EXTREME_LOGGING
|
2021-04-21 09:53:13 -07:00
|
|
|
efiPrintf("scheduling sparkDown ind=%d %d %s now=%d later id=%d", trgEventIndex, getRevolutionCounter(), event->getOutputForLoggins()->name, (int)getTimeNowUs(), event->sparkId);
|
2016-10-29 14:03:45 -07:00
|
|
|
#endif /* FUEL_MATH_EXTREME_LOGGING */
|
2016-09-21 20:03:22 -07:00
|
|
|
} else {
|
2019-04-12 19:07:03 -07:00
|
|
|
#if SPARK_EXTREME_LOGGING
|
2021-04-21 09:53:13 -07:00
|
|
|
efiPrintf("to queue sparkDown ind=%d %d %s now=%d for id=%d", trgEventIndex, getRevolutionCounter(), event->getOutputForLoggins()->name, (int)getTimeNowUs(), event->sparkEvent.position.triggerEventIndex);
|
2020-07-20 10:38:33 -07:00
|
|
|
#endif /* SPARK_EXTREME_LOGGING */
|
2019-11-23 17:36:40 -08:00
|
|
|
|
2021-10-01 22:10:24 -07:00
|
|
|
if (!limitedSpark && engine->enableOverdwellProtection) {
|
|
|
|
// auto fire spark at 1.5x nominal dwell
|
|
|
|
efitick_t fireTime = chargeTime + MSF2NT(1.5f * dwellMs);
|
|
|
|
engine->executor.scheduleByTimestampNt("overdwell", &event->sparkEvent.scheduling, fireTime, { fireSparkAndPrepareNextSchedule, event });
|
|
|
|
}
|
|
|
|
}
|
2016-09-21 20:03:22 -07:00
|
|
|
|
2019-11-23 17:36:40 -08:00
|
|
|
#if EFI_UNIT_TEST
|
|
|
|
if (verboseMode) {
|
2020-02-21 16:35:11 -08:00
|
|
|
printf("spark dwell@ %d/%d spark@ %d/%d id=%d\r\n", event->dwellPosition.triggerEventIndex, (int)event->dwellPosition.angleOffsetFromTriggerEvent,
|
|
|
|
event->sparkEvent.position.triggerEventIndex, (int)event->sparkEvent.position.angleOffsetFromTriggerEvent,
|
|
|
|
event->sparkId);
|
2016-09-21 20:03:22 -07:00
|
|
|
}
|
2019-11-23 17:36:40 -08:00
|
|
|
#endif
|
2016-09-21 20:03:22 -07:00
|
|
|
}
|
|
|
|
|
2019-11-17 06:32:12 -08:00
|
|
|
void initializeIgnitionActions(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2019-11-17 06:02:49 -08:00
|
|
|
IgnitionEventList *list = &engine->ignitionEvents;
|
2018-07-24 17:40:44 -07:00
|
|
|
angle_t dwellAngle = ENGINE(engineState.dwellAngle);
|
2019-05-07 14:22:26 -07:00
|
|
|
floatms_t sparkDwell = ENGINE(engineState.sparkDwell);
|
2018-07-24 17:40:44 -07:00
|
|
|
if (cisnan(ENGINE(engineState.timingAdvance)) || cisnan(dwellAngle)) {
|
2016-12-18 07:02:38 -08:00
|
|
|
// error should already be reported
|
|
|
|
// need to invalidate previous ignition schedule
|
|
|
|
list->isReady = false;
|
|
|
|
return;
|
|
|
|
}
|
2018-07-25 20:03:04 -07:00
|
|
|
efiAssertVoid(CUSTOM_ERR_6592, engineConfiguration->specs.cylindersCount > 0, "cylindersCount");
|
2016-11-27 19:01:36 -08:00
|
|
|
|
2021-03-20 05:40:36 -07:00
|
|
|
for (size_t cylinderIndex = 0; cylinderIndex < CONFIG(specs.cylindersCount); cylinderIndex++) {
|
2016-11-28 11:01:52 -08:00
|
|
|
list->elements[cylinderIndex].cylinderIndex = cylinderIndex;
|
2019-04-12 19:07:03 -07:00
|
|
|
#if EFI_UNIT_TEST
|
2016-11-28 20:02:02 -08:00
|
|
|
list->elements[cylinderIndex].engine = engine;
|
2017-11-26 19:30:37 -08:00
|
|
|
#endif /* EFI_UNIT_TEST */
|
2019-05-07 14:22:26 -07:00
|
|
|
prepareCylinderIgnitionSchedule(dwellAngle, sparkDwell, &list->elements[cylinderIndex] PASS_ENGINE_PARAMETER_SUFFIX);
|
2016-11-25 11:03:06 -08:00
|
|
|
}
|
2016-11-28 09:03:02 -08:00
|
|
|
list->isReady = true;
|
2016-11-25 11:03:06 -08:00
|
|
|
}
|
|
|
|
|
2020-11-23 17:10:17 -08:00
|
|
|
static void prepareIgnitionSchedule(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2020-03-24 17:19:19 -07:00
|
|
|
ScopePerf perf(PE::PrepareIgnitionSchedule);
|
|
|
|
|
2016-09-21 21:03:00 -07:00
|
|
|
/**
|
|
|
|
* TODO: warning. there is a bit of a hack here, todo: improve.
|
2019-10-07 23:01:41 -07:00
|
|
|
* currently output signals/times dwellStartTimer from the previous revolutions could be
|
2016-09-21 21:03:00 -07:00
|
|
|
* still used because they have crossed the revolution boundary
|
|
|
|
* but we are already re-purposing the output signals, but everything works because we
|
|
|
|
* are not affecting that space in memory. todo: use two instances of 'ignitionSignals'
|
|
|
|
*/
|
2019-08-07 21:19:09 -07:00
|
|
|
operation_mode_e operationMode = engine->getOperationMode(PASS_ENGINE_PARAMETER_SIGNATURE);
|
|
|
|
float maxAllowedDwellAngle = (int) (getEngineCycle(operationMode) / 2); // the cast is about making Coverity happy
|
2016-09-21 21:03:00 -07:00
|
|
|
|
2019-01-20 08:33:40 -08:00
|
|
|
if (getCurrentIgnitionMode(PASS_ENGINE_PARAMETER_SIGNATURE) == IM_ONE_COIL) {
|
2019-08-07 21:19:09 -07:00
|
|
|
maxAllowedDwellAngle = getEngineCycle(operationMode) / engineConfiguration->specs.cylindersCount / 1.1;
|
2016-09-21 21:03:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (engine->engineState.dwellAngle == 0) {
|
2017-01-22 06:03:08 -08:00
|
|
|
warning(CUSTOM_ZERO_DWELL, "dwell is zero?");
|
2016-09-21 21:03:00 -07:00
|
|
|
}
|
|
|
|
if (engine->engineState.dwellAngle > maxAllowedDwellAngle) {
|
2018-01-23 09:05:14 -08:00
|
|
|
warning(CUSTOM_DWELL_TOO_LONG, "dwell angle too long: %.2f", engine->engineState.dwellAngle);
|
2016-09-21 21:03:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// todo: add some check for dwell overflow? like 4 times 6 ms while engine cycle is less then that
|
|
|
|
|
2019-11-17 06:02:49 -08:00
|
|
|
initializeIgnitionActions(PASS_ENGINE_PARAMETER_SIGNATURE);
|
2016-09-21 21:03:00 -07:00
|
|
|
}
|
|
|
|
|
2020-01-10 13:01:54 -08:00
|
|
|
static void scheduleAllSparkEventsUntilNextTriggerTooth(uint32_t trgEventIndex, efitick_t edgeTimestamp DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2019-11-23 17:36:40 -08:00
|
|
|
AngleBasedEvent *current, *tmp;
|
2016-09-21 20:03:22 -07:00
|
|
|
|
2019-12-03 11:39:11 -08:00
|
|
|
LL_FOREACH_SAFE2(ENGINE(angleBasedEventsHead), current, tmp, nextToothEvent)
|
2016-09-21 20:03:22 -07:00
|
|
|
{
|
2019-11-23 17:36:40 -08:00
|
|
|
if (current->position.triggerEventIndex == trgEventIndex) {
|
2016-09-21 20:03:22 -07:00
|
|
|
// time to fire a spark which was scheduled previously
|
2019-12-03 11:39:11 -08:00
|
|
|
LL_DELETE2(ENGINE(angleBasedEventsHead), current, nextToothEvent);
|
2016-09-21 20:03:22 -07:00
|
|
|
|
2019-11-23 17:36:40 -08:00
|
|
|
scheduling_s * sDown = ¤t->scheduling;
|
2016-09-21 20:03:22 -07:00
|
|
|
|
2019-04-12 19:07:03 -07:00
|
|
|
#if SPARK_EXTREME_LOGGING
|
2021-04-21 09:53:13 -07:00
|
|
|
efiPrintf("time to invoke ind=%d %d %d", trgEventIndex, getRevolutionCounter(), (int)getTimeNowUs());
|
2020-07-20 10:38:33 -07:00
|
|
|
#endif /* SPARK_EXTREME_LOGGING */
|
2016-10-29 14:03:45 -07:00
|
|
|
|
2021-10-01 22:10:24 -07:00
|
|
|
// In case this event was scheduled by overdwell protection, cancel it so we can re-schedule at the correct time
|
|
|
|
engine->executor.cancel(sDown);
|
|
|
|
|
2020-01-10 13:01:54 -08:00
|
|
|
scheduleByAngle(
|
|
|
|
sDown,
|
|
|
|
edgeTimestamp,
|
|
|
|
current->position.angleOffsetFromTriggerEvent,
|
|
|
|
current->action
|
|
|
|
PASS_ENGINE_PARAMETER_SUFFIX
|
|
|
|
);
|
2016-09-21 20:03:22 -07:00
|
|
|
}
|
|
|
|
}
|
2019-05-07 13:45:29 -07:00
|
|
|
}
|
|
|
|
|
2020-01-10 13:01:54 -08:00
|
|
|
void onTriggerEventSparkLogic(bool limitedSpark, uint32_t trgEventIndex, int rpm, efitick_t edgeTimestamp
|
2019-05-07 13:45:29 -07:00
|
|
|
DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
|
|
|
|
2019-10-13 13:14:08 -07:00
|
|
|
ScopePerf perf(PE::OnTriggerEventSparkLogic);
|
|
|
|
|
2019-05-07 13:45:29 -07:00
|
|
|
if (!isValidRpm(rpm) || !CONFIG(isIgnitionEnabled)) {
|
|
|
|
// this might happen for instance in case of a single trigger event after a pause
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ENGINE(ignitionEvents.isReady)) {
|
|
|
|
prepareIgnitionSchedule(PASS_ENGINE_PARAMETER_SIGNATURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ignition schedule is defined once per revolution
|
|
|
|
* See initializeIgnitionActions()
|
|
|
|
*/
|
2020-01-10 13:01:54 -08:00
|
|
|
scheduleAllSparkEventsUntilNextTriggerTooth(trgEventIndex, edgeTimestamp PASS_ENGINE_PARAMETER_SUFFIX);
|
2019-05-07 13:45:29 -07:00
|
|
|
|
2016-09-21 20:03:22 -07:00
|
|
|
|
|
|
|
// scheduleSimpleMsg(&logger, "eventId spark ", eventIndex);
|
2017-06-08 21:05:41 -07:00
|
|
|
if (ENGINE(ignitionEvents.isReady)) {
|
2021-03-20 05:40:36 -07:00
|
|
|
for (size_t i = 0; i < CONFIG(specs.cylindersCount); i++) {
|
2017-06-08 21:05:41 -07:00
|
|
|
IgnitionEvent *event = &ENGINE(ignitionEvents.elements[i]);
|
2019-10-07 21:54:19 -07:00
|
|
|
if (event->dwellPosition.triggerEventIndex != trgEventIndex)
|
2016-11-28 09:03:02 -08:00
|
|
|
continue;
|
2021-09-06 07:35:26 -07:00
|
|
|
|
2021-09-06 12:29:36 -07:00
|
|
|
if (i == 0 && CONFIG(artificialTestMisfire) && (getRevolutionCounter() % ((int)engineConfiguration->fsio_setting[5]) == 0)) {
|
2021-09-06 07:35:26 -07:00
|
|
|
// artificial misfire on cylinder #1 for testing purposes
|
2021-09-06 08:10:11 -07:00
|
|
|
// enable artificialMisfire
|
|
|
|
// set_fsio_setting 6 20
|
2021-09-08 17:32:50 -07:00
|
|
|
warning(CUSTOM_ARTIFICIAL_MISFIRE, "artificial misfire on cylinder #1 for testing purposes %d", engine->globalSparkIdCounter);
|
2021-09-06 07:35:26 -07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-01-10 13:01:54 -08:00
|
|
|
handleSparkEvent(limitedSpark, trgEventIndex, event, rpm, edgeTimestamp PASS_ENGINE_PARAMETER_SUFFIX);
|
2016-11-28 09:03:02 -08:00
|
|
|
}
|
2016-09-21 20:03:22 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-06 22:26:58 -08:00
|
|
|
/**
|
|
|
|
* Number of sparks per physical coil
|
|
|
|
* @see getNumberOfInjections
|
|
|
|
*/
|
2017-05-15 20:28:49 -07:00
|
|
|
int getNumberOfSparks(ignition_mode_e mode DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2017-03-06 22:26:58 -08:00
|
|
|
switch (mode) {
|
|
|
|
case IM_ONE_COIL:
|
|
|
|
return engineConfiguration->specs.cylindersCount;
|
2019-11-16 13:00:50 -08:00
|
|
|
case IM_TWO_COILS:
|
|
|
|
return engineConfiguration->specs.cylindersCount / 2;
|
2017-03-06 22:26:58 -08:00
|
|
|
case IM_INDIVIDUAL_COILS:
|
|
|
|
return 1;
|
|
|
|
case IM_WASTED_SPARK:
|
|
|
|
return 2;
|
|
|
|
default:
|
2017-04-12 06:26:22 -07:00
|
|
|
firmwareError(CUSTOM_ERR_IGNITION_MODE, "Unexpected ignition_mode_e %d", mode);
|
2017-03-06 22:26:58 -08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @see getInjectorDutyCycle
|
|
|
|
*/
|
2017-05-15 20:28:49 -07:00
|
|
|
percent_t getCoilDutyCycle(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2019-10-15 02:24:33 -07:00
|
|
|
floatms_t totalPerCycle = ENGINE(engineState.sparkDwell) * getNumberOfSparks(getCurrentIgnitionMode(PASS_ENGINE_PARAMETER_SIGNATURE) PASS_ENGINE_PARAMETER_SUFFIX);
|
2019-08-07 21:19:09 -07:00
|
|
|
floatms_t engineCycleDuration = getCrankshaftRevolutionTimeMs(rpm) * (engine->getOperationMode(PASS_ENGINE_PARAMETER_SIGNATURE) == TWO_STROKE ? 1 : 2);
|
2017-03-06 22:26:58 -08:00
|
|
|
return 100 * totalPerCycle / engineCycleDuration;
|
|
|
|
}
|
2020-02-26 15:16:35 -08:00
|
|
|
|
|
|
|
#endif // EFI_ENGINE_CONTROL
|