/* * @file spark_logic.cpp * * @date Sep 15, 2016 * @author Andrey Belomutskiy, (c) 2012-2016 */ #include "engine_math.h" #include "utlist.h" #include "event_queue.h" EXTERN_ENGINE; static cyclic_buffer ignitionErrorDetection; static Logging *logger; int isInjectionEnabled(engine_configuration_s *engineConfiguration) { // todo: is this worth a method? should this be inlined? return engineConfiguration->isInjectionEnabled; } int isIgnitionTimingError(void) { return ignitionErrorDetection.sum(6) > 4; } void turnSparkPinLow(NamedOutputPin *output) { turnPinLow(output); #if EFI_PROD_CODE || defined(__DOXYGEN__) if (CONFIG(dizzySparkOutputPin) != GPIO_UNASSIGNED) { turnPinLow(&enginePins.dizzyOutput); } #endif /* EFI_PROD_CODE */ } void turnSparkPinHigh(NamedOutputPin *output) { turnPinHigh(output); #if EFI_PROD_CODE || defined(__DOXYGEN__) if (CONFIG(dizzySparkOutputPin) != GPIO_UNASSIGNED) { turnPinHigh(&enginePins.dizzyOutput); } #endif /* EFI_PROD_CODE */ } static ALWAYS_INLINE void handleSparkEvent(bool limitedSpark, uint32_t trgEventIndex, IgnitionEvent *iEvent, int rpm DECLARE_ENGINE_PARAMETER_S) { float dwellMs = ENGINE(engineState.sparkDwell); if (cisnan(dwellMs) || dwellMs < 0) { warning(CUSTOM_OBD_45, "invalid dwell: %f at %d", dwellMs, rpm); return; } floatus_t chargeDelayUs = ENGINE(rpmCalculator.oneDegreeUs) * iEvent->dwellPosition.angleOffset; int isIgnitionError = chargeDelayUs < 0; ignitionErrorDetection.add(isIgnitionError); if (isIgnitionError) { #if EFI_PROD_CODE || defined(__DOXYGEN__) scheduleMsg(logger, "Negative spark delay=%f", chargeDelayUs); #endif chargeDelayUs = 0; return; } if (cisnan(dwellMs)) { firmwareError("NaN in scheduleOutput", dwellMs); return; } /** * We are alternating two event lists in order to avoid a potential issue around revolution boundary * when an event is scheduled within the next revolution. */ scheduling_s * sUp = &iEvent->signalTimerUp; scheduling_s * sDown = &iEvent->signalTimerDown; /** * The start of charge is always within the current trigger event range, so just plain time-based scheduling */ if (!limitedSpark) { #if EFI_UNIT_TEST || defined(__DOXYGEN__) printf("spark charge delay=%f\r\n", chargeDelayUs); #endif /** * 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. */ scheduleTask("spark up", sUp, chargeDelayUs, (schfunc_t) &turnSparkPinHigh, iEvent->output); } /** * Spark event is often happening during a later trigger event timeframe * TODO: improve precision */ findTriggerPosition(&iEvent->sparkPosition, iEvent->advance PASS_ENGINE_PARAMETER); if (iEvent->sparkPosition.eventIndex == trgEventIndex) { /** * Spark should be fired before the next trigger event - time-based delay is best precision possible */ float timeTillIgnitionUs = ENGINE(rpmCalculator.oneDegreeUs) * iEvent->sparkPosition.angleOffset; #if EFI_UNIT_TEST || defined(__DOXYGEN__) printf("spark delay=%f angle=%f\r\n", timeTillIgnitionUs, iEvent->sparkPosition.angleOffset); #endif scheduleTask("spark1 down", sDown, (int) timeTillIgnitionUs, (schfunc_t) &turnSparkPinLow, iEvent->output); } else { /** * Spark should be scheduled in relation to some future trigger event, this way we get better firing precision */ bool isPending = assertNotInList(ENGINE(iHead), iEvent); if (isPending) return; LL_APPEND(ENGINE(iHead), iEvent); } } void handleSpark(bool limitedSpark, uint32_t trgEventIndex, int rpm, IgnitionEventList *list DECLARE_ENGINE_PARAMETER_S) { if (!isValidRpm(rpm) || !CONFIG(isIgnitionEnabled)) { // this might happen for instance in case of a single trigger event after a pause return; } /** * Ignition schedule is defined once per revolution * See initializeIgnitionActions() */ IgnitionEvent *current, *tmp; LL_FOREACH_SAFE(ENGINE(iHead), current, tmp) { if (current->sparkPosition.eventIndex == trgEventIndex) { // time to fire a spark which was scheduled previously LL_DELETE(ENGINE(iHead), current); scheduling_s * sDown = ¤t->signalTimerDown; float timeTillIgnitionUs = ENGINE(rpmCalculator.oneDegreeUs) * current->sparkPosition.angleOffset; scheduleTask("spark 2down", sDown, (int) timeTillIgnitionUs, (schfunc_t) &turnSparkPinLow, current->output); } } // scheduleSimpleMsg(&logger, "eventId spark ", eventIndex); for (int i = 0; i < list->size; i++) { IgnitionEvent *event = &list->elements[i]; if (event->dwellPosition.eventIndex != trgEventIndex) continue; handleSparkEvent(limitedSpark, trgEventIndex, event, rpm PASS_ENGINE_PARAMETER); } } void initSparkLogic(Logging *sharedLogger) { logger = sharedLogger; }