rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
Functions
spark_logic.h File Reference

Functions

void onTriggerEventSparkLogic (float rpm, efitick_t edgeTimestamp, float currentPhase, float nextPhase)
 
void turnSparkPinHighStartCharging (IgnitionEvent *event)
 
void fireSparkAndPrepareNextSchedule (IgnitionEvent *event)
 
int getNumberOfSparks (ignition_mode_e mode)
 
percent_t getCoilDutyCycle (float rpm)
 
void initializeIgnitionActions ()
 

Function Documentation

◆ fireSparkAndPrepareNextSchedule()

void fireSparkAndPrepareNextSchedule ( IgnitionEvent event)

TL,DR: each IgnitionEvent is in charge of it's own scheduling forever, we plant next event while finishing handling of the current one

ratio of desired dwell duration to actual dwell duration gives us some idea of how good is input trigger jitter

Definition at line 209 of file spark_logic.cpp.

209 {
210#if EFI_UNIT_TEST
211 if (engine->onIgnitionEvent) {
212 engine->onIgnitionEvent(event, false);
213 }
214#endif
215
216 for (int i = 0; i< MAX_OUTPUTS_FOR_IGNITION;i++) {
217 IgnitionOutputPin *output = event->outputs[i];
218
219 if (output) {
220 fireSparkBySettingPinLow(event, output);
221 }
222 }
223
224 efitick_t nowNt = getTimeNowNt();
225
226#if EFI_TOOTH_LOGGER
227 LogTriggerCoilState(nowNt, false, event->coilIndex);
228#endif // EFI_TOOTH_LOGGER
229 if (!event->wasSparkLimited) {
230 /**
231 * ratio of desired dwell duration to actual dwell duration gives us some idea of how good is input trigger jitter
232 */
233 float actualDwellMs = event->actualDwellTimer.getElapsedSeconds(nowNt) * 1e3;
234 float ratio = actualDwellMs / event->sparkDwell;
235
236 if (ratio > 1.2) {
238 } else if (ratio < 0.8) {
240 }
242 }
243
244 // now that we've just fired a coil let's prepare the new schedule for the next engine revolution
245
246 angle_t dwellAngleDuration = engine->ignitionState.dwellDurationAngle;
248 if (std::isnan(dwellAngleDuration) || std::isnan(sparkDwell)) {
249 // we are here if engine has just stopped
250 return;
251 }
252
253 // If there are more sparks to fire, schedule them
254 if (event->sparksRemaining > 0) {
255 event->sparksRemaining--;
256
257 efitick_t nextDwellStart = nowNt + engine->engineState.multispark.delay;
258 efitick_t nextFiring = nextDwellStart + engine->engineState.multispark.dwell;
259#if SPARK_EXTREME_LOGGING
260 efiPrintf("schedule multispark");
261#endif /* SPARK_EXTREME_LOGGING */
262
263 // We can schedule both of these right away, since we're going for "asap" not "particular angle"
264 engine->scheduler.schedule("dwell", &event->dwellStartTimer, nextDwellStart, action_s::make<turnSparkPinHighStartCharging>( event ));
265 engine->scheduler.schedule("firing", &event->sparkEvent.eventScheduling, nextFiring, action_s::make<fireSparkAndPrepareNextSchedule>( event ));
266 } else {
268#if SPARK_EXTREME_LOGGING
269 efiPrintf("scheduleByAngle TrailingSparks");
270#endif /* SPARK_EXTREME_LOGGING */
271
272 // Trailing sparks are enabled - schedule an event for the corresponding trailing coil
275 action_s::make<fireTrailingSpark>( &enginePins.trailingCoils[event->coilIndex] )
276 );
277 }
278
279 // If all events have been scheduled, prepare for next time.
280 prepareCylinderIgnitionSchedule(dwellAngleDuration, sparkDwell, event);
281 }
282
284}
IgnitionState ignitionState
Definition engine.h:239
SingleTimerExecutor scheduler
Definition engine.h:271
EngineState engineState
Definition engine.h:344
std::function< void(IgnitionEvent *, bool)> onIgnitionEvent
Definition engine.h:280
void onSparkFireKnockSense(uint8_t cylinderIndex, efitick_t nowNt)
IgnitionOutputPin trailingCoils[MAX_CYLINDER_COUNT]
Definition efi_gpio.h:130
multispark_state multispark
scheduling_s trailingSparkFire
uint8_t sparksRemaining
scheduling_s dwellStartTimer
AngleBasedEvent sparkEvent
floatms_t getDwell() const
void schedule(const char *msg, scheduling_s *scheduling, efitick_t timeNt, action_s const &action) override
Schedule an action to be executed in the future.
EnginePins enginePins
Definition efi_gpio.cpp:24
efitick_t getTimeNowNt()
Definition efitime.cpp:19
static EngineAccessor engine
Definition engine.h:413
static constexpr engine_configuration_s * engineConfiguration
efitick_t scheduleByAngle(scheduling_s *timer, efitick_t nowNt, angle_t angle, action_s const &action)
float floatms_t
float angle_t
sparkDwell("Ignition: coil charge time", SensorCategory.SENSOR_INPUTS, FieldType.INT, 940, 1.0, 0.0, 30.0, "ms")
static void prepareCylinderIgnitionSchedule(angle_t dwellAngleDuration, floatms_t sparkDwell, IgnitionEvent *event)
static void fireSparkBySettingPinLow(IgnitionEvent *event, IgnitionOutputPin *output)
scheduling_s eventScheduling
scaled_channel< int16_t, 100, 1 > trailingSparkAngle
void LogTriggerCoilState(efitick_t timestamp, size_t index, bool state)

Referenced by overFireSparkAndPrepareNextSchedule().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getCoilDutyCycle()

percent_t getCoilDutyCycle ( float  rpm)
See also
getInjectorDutyCycle

Definition at line 681 of file spark_logic.cpp.

681 {
684 return 100 * totalPerCycle / engineCycleDuration;
685}
virtual operation_mode_e getOperationMode() const =0
EngineRotationState * getEngineRotationState()
Definition engine.cpp:574
floatms_t getCrankshaftRevolutionTimeMs(float rpm)
ignition_mode_e getCurrentIgnitionMode()
@ TWO_STROKE
int getNumberOfSparks(ignition_mode_e mode)

Referenced by populateFrame(), and updateIgnition().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getNumberOfSparks()

int getNumberOfSparks ( ignition_mode_e  mode)

Number of sparks per physical coil

See also
getNumberOfInjections

Definition at line 662 of file spark_logic.cpp.

662 {
663 switch (mode) {
664 case IM_ONE_COIL:
666 case IM_TWO_COILS:
668 case IM_INDIVIDUAL_COILS:
669 return 1;
670 case IM_WASTED_SPARK:
671 return 2;
672 default:
673 firmwareError(ObdCode::CUSTOM_ERR_IGNITION_MODE, "Unexpected ignition_mode_e %d", mode);
674 return 1;
675 }
676}
void firmwareError(ObdCode code, const char *fmt,...)
@ CUSTOM_ERR_IGNITION_MODE

Referenced by getCoilDutyCycle().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ initializeIgnitionActions()

void initializeIgnitionActions ( )

Definition at line 502 of file spark_logic.cpp.

502 {
506 if (std::isnan(engine->engineState.timingAdvance[0]) || std::isnan(dwellAngle)) {
507 // error should already be reported
508 // need to invalidate previous ignition schedule
509 list->isReady = false;
510 return;
511 }
512 efiAssertVoid(ObdCode::CUSTOM_ERR_6592, engineConfiguration->cylindersCount > 0, "cylindersCount");
513
514 for (size_t cylinderIndex = 0; cylinderIndex < engineConfiguration->cylindersCount; cylinderIndex++) {
515 list->elements[cylinderIndex].cylinderIndex = cylinderIndex;
516 prepareCylinderIgnitionSchedule(dwellAngle, sparkDwell, &list->elements[cylinderIndex]);
517 }
518 list->isReady = true;
519}
IgnitionEventList ignitionEvents
Definition engine.h:289
angle_t timingAdvance[MAX_CYLINDER_COUNT]
IgnitionEvent elements[MAX_CYLINDER_COUNT]
@ CUSTOM_ERR_6592

Referenced by prepareIgnitionSchedule().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ onTriggerEventSparkLogic()

void onTriggerEventSparkLogic ( float  rpm,
efitick_t  edgeTimestamp,
float  currentPhase,
float  nextPhase 
)

Ignition schedule is defined once per revolution See initializeIgnitionActions()

Definition at line 545 of file spark_logic.cpp.

545 {
547
549 return;
550 }
551
552 LimpState limitedSparkState = getLimpManager()->allowIgnition();
553
554 // todo: eliminate state copy logic by giving limpManager it's owm limp_manager.txt and leveraging LiveData
555 engine->outputChannels.sparkCutReason = (int8_t)limitedSparkState.reason;
556 bool limitedSpark = !limitedSparkState.value;
557
558 const floatms_t dwellMs = engine->ignitionState.getDwell();
559 if (std::isnan(dwellMs) || dwellMs <= 0) {
560 warning(ObdCode::CUSTOM_DWELL, "invalid dwell to handle: %.2f", dwellMs);
561 return;
562 }
563
566 }
567
568
569 /**
570 * Ignition schedule is defined once per revolution
571 * See initializeIgnitionActions()
572 */
573
574
575 // Only apply odd cylinder count wasted logic if:
576 // - odd cyl count
577 // - current mode is wasted spark
578 // - four stroke
579 bool enableOddCylinderWastedSpark =
581 && getCurrentIgnitionMode() == IM_WASTED_SPARK;
582
584 for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) {
586
587 angle_t dwellAngle = event->dwellAngle;
588
589 angle_t sparkAngle = event->sparkAngle;
590 if (std::isnan(sparkAngle)) {
592 continue;
593 }
594
595 bool isOddCylWastedEvent = false;
596 if (enableOddCylinderWastedSpark) {
597 auto dwellAngleWastedEvent = dwellAngle + 360;
598 if (dwellAngleWastedEvent > 720) {
599 dwellAngleWastedEvent -= 720;
600 }
601
602 // Check whether this event hits 360 degrees out from now (ie, wasted spark),
603 // and if so, twiddle the dwell and spark angles so it happens now instead
604 isOddCylWastedEvent = isPhaseInRange(dwellAngleWastedEvent, currentPhase, nextPhase);
605
606 if (isOddCylWastedEvent) {
607 dwellAngle = dwellAngleWastedEvent;
608
609 sparkAngle += 360;
610 if (sparkAngle > 720) {
611 sparkAngle -= 720;
612 }
613 }
614 }
615
616 if (!isOddCylWastedEvent && !isPhaseInRange(dwellAngle, currentPhase, nextPhase)) {
617 continue;
618 }
619
620 if (i == 0 && engineConfiguration->artificialTestMisfire && (getRevolutionCounter() % ((int)engineConfiguration->scriptSetting[5]) == 0)) {
621 // artificial misfire on cylinder #1 for testing purposes
622 // enable artificialMisfire
623 warning(ObdCode::CUSTOM_ARTIFICIAL_MISFIRE, "artificial misfire on cylinder #1 for testing purposes %d", engine->engineState.globalSparkCounter);
624 continue;
625 }
626#if EFI_LAUNCH_CONTROL
628 engine->ignitionState.luaIgnitionSkip = sparkLimited;
629 if (sparkLimited) {
630 continue;
631 }
632#endif // EFI_LAUNCH_CONTROL
633
634#if EFI_ANTILAG_SYSTEM && EFI_LAUNCH_CONTROL
635/*
636 if (engine->antilagController.isAntilagCondition) {
637 if (engine->ALSsoftSparkLimiter.shouldSkip()) {
638 continue;
639 }
640 }
641 float throttleIntent = Sensor::getOrZero(SensorType::DriverThrottleIntent);
642 engine->antilagController.timingALSSkip = interpolate3d(
643 config->ALSIgnSkipTable,
644 config->alsIgnSkipLoadBins, throttleIntent,
645 config->alsIgnSkiprpmBins, rpm
646 );
647
648 auto ALSSkipRatio = engine->antilagController.timingALSSkip;
649 engine->ALSsoftSparkLimiter.setTargetSkipRatio(ALSSkipRatio/100);
650*/
651#endif // EFI_ANTILAG_SYSTEM
652
653 scheduleSparkEvent(limitedSpark, event, rpm, dwellMs, dwellAngle, sparkAngle, edgeTimestamp, currentPhase, nextPhase);
654 }
655 }
656}
SoftSparkLimiter softSparkLimiter
Definition engine.h:222
TunerStudioOutputChannels outputChannels
Definition engine.h:109
SoftSparkLimiter hardSparkLimiter
Definition engine.h:224
bool useOddFireWastedSpark
LimpState allowIgnition() const
bool isPhaseInRange(float test, float current, float next)
Definition efilib.cpp:176
LimpManager * getLimpManager()
Definition engine.cpp:597
bool warning(ObdCode code, const char *fmt,...)
@ CUSTOM_ADVANCE_SPARK
@ CUSTOM_ARTIFICIAL_MISFIRE
@ CUSTOM_DWELL
@ OnTriggerEventSparkLogic
static void prepareIgnitionSchedule()
static void scheduleSparkEvent(bool limitedSpark, IgnitionEvent *event, float rpm, float dwellMs, float dwellAngle, float sparkAngle, efitick_t edgeTimestamp, float currentPhase, float nextPhase)
const bool value
const ClearReason reason

Referenced by mainTriggerCallback().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ turnSparkPinHighStartCharging()

void turnSparkPinHighStartCharging ( IgnitionEvent event)

Definition at line 332 of file spark_logic.cpp.

332 {
333 efitick_t nowNt = getTimeNowNt();
334
335 event->actualDwellTimer.reset(nowNt);
336
337 bool skippedDwellDueToTriggerNoised = false;
338 for (int i = 0; i< MAX_OUTPUTS_FOR_IGNITION;i++) {
339 IgnitionOutputPin *output = event->outputs[i];
340 if (output != NULL) {
341 // at the moment we have a funny xor as if outputs could have different destiny. That's probably an over exaggeration,
342 // realistically it should be enough to check the sequencing of only the first output but that would be less elegant
343 //
344 // maybe it would have need nicer if instead of an array of outputs we had a linked list of outputs? but that's just daydreaming.
345 skippedDwellDueToTriggerNoised |= startDwellByTurningSparkPinHigh(event, output);
346 }
347 }
348
349#if EFI_UNIT_TEST
351#endif
352
353
354 if (!skippedDwellDueToTriggerNoised) {
355
356#if EFI_UNIT_TEST
357 if (engine->onIgnitionEvent) {
358 engine->onIgnitionEvent(event, true);
359 }
360#endif
361
362#if EFI_TOOTH_LOGGER
363 LogTriggerCoilState(nowNt, true, event->coilIndex);
364#endif // EFI_TOOTH_LOGGER
365 }
366
367
369 IgnitionOutputPin *output = &enginePins.trailingCoils[event->coilIndex];
370 // Trailing sparks are enabled - schedule an event for the corresponding trailing coil
373 action_s::make<chargeTrailingSpark>( output )
374 );
375 }
376}
void incrementBailedOnDwellCount()
Definition engine.h:262
scheduling_s trailingSparkCharge
static bool startDwellByTurningSparkPinHigh(IgnitionEvent *event, IgnitionOutputPin *output)
Here is the call graph for this function:

Go to the source code of this file.