| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /* | |||
| 2 | * @file prime_injection.cpp | |||
| 3 | */ | |||
| 4 | ||||
| 5 | #include "pch.h" | |||
| 6 | #include "prime_injection.h" | |||
| 7 | #include "injection_gpio.h" | |||
| 8 | #include "sensor.h" | |||
| 9 | #include "backup_ram.h" | |||
| 10 | #if EFI_PROD_CODE | |||
| 11 | #include "microsecond_timer.h" | |||
| 12 | #endif | |||
| 13 | ||||
| 14 | 3 | floatms_t PrimeController::getPrimeDuration() const { | ||
| 15 |
1/1✓ Branch 2 taken 3 times.
|
3 | auto clt = Sensor::get(SensorType::Clt); | |
| 16 | ||||
| 17 | // If the coolant sensor is dead, skip the prime. The engine will still start fine, but may take a little longer. | |||
| 18 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 2 times.
|
3 | if (!clt) { |
| 19 | 1 | return 0; | ||
| 20 | } | |||
| 21 | ||||
| 22 | auto primeMass = | |||
| 23 | 0.001f * // convert milligram to gram | |||
| 24 |
1/1✓ Branch 1 taken 2 times.
|
2 | interpolate2d(clt.Value, engineConfiguration->primeBins, engineConfiguration->primeValues); | |
| 25 | ||||
| 26 |
1/1✓ Branch 1 taken 2 times.
|
2 | efiPrintf("Priming pulse mass: %.4f g", primeMass); | |
| 27 | ||||
| 28 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 5 taken 2 times.
|
2 | return engine->module<InjectorModelPrimary>()->getInjectionDuration(primeMass); | |
| 29 | } | |||
| 30 | ||||
| 31 | // Check if the engine is not stopped or cylinder cleanup is activated | |||
| 32 | 2 | static bool isPrimeInjectionPulseSkipped() { | ||
| 33 | // Skip if the engine is already spinning | |||
| 34 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 2 times.
|
2 | if (!getEngineRotationState()->isStopped()) { |
| 35 | ✗ | return true; | ||
| 36 | } | |||
| 37 | ||||
| 38 | // Skip if cylinder cleanup is active | |||
| 39 |
2/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
|
2 | return engineConfiguration->isCylinderCleanupEnabled && (Sensor::getOrZero(SensorType::Tps1) > CLEANUP_MODE_TPS); | |
| 40 | } | |||
| 41 | ||||
| 42 | 2 | void PrimeController::onIgnitionStateChanged(bool ignitionOn) { | ||
| 43 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 2 times.
|
2 | if (!ignitionOn) { |
| 44 | // don't prime on ignition-off | |||
| 45 | ✗ | return; | ||
| 46 | } | |||
| 47 | ||||
| 48 | // First, we need a protection against 'fake' ignition switch on and off (i.e. no engine started), to avoid repeated prime pulses. | |||
| 49 | // So we check and update the ignition switch counter in non-volatile backup-RAM | |||
| 50 | 2 | uint32_t ignSwitchCounter = getKeyCycleCounter(); | ||
| 51 | ||||
| 52 | // if we're just toying with the ignition switch, give it another chance eventually... | |||
| 53 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 2 times.
|
2 | if (ignSwitchCounter > 10) { |
| 54 | ✗ | ignSwitchCounter = 0; | ||
| 55 | } | |||
| 56 | ||||
| 57 | // If we're going to skip this pulse, then save the counter as 0. | |||
| 58 | // That's because we'll definitely need the prime pulse next time (either due to the cylinder cleanup or the engine spinning) | |||
| 59 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 2 times.
|
2 | if (isPrimeInjectionPulseSkipped()) { |
| 60 | ✗ | ignSwitchCounter = -1; | ||
| 61 | } | |||
| 62 | ||||
| 63 | // start prime injection if this is a 'fresh start' | |||
| 64 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 2 times.
✗ Decision 'false' not taken.
|
2 | if (ignSwitchCounter == 0) { |
| 65 | // Give sensors long enough to wake up before priming | |||
| 66 | 2 | constexpr float minimumPrimeDelayMs = 100; | ||
| 67 | 2 | int32_t primeDelayNt = assertFloatFitsInto32BitsAndCast("primingDelay", MSF2NT(engineConfiguration->primingDelay * 1000 + minimumPrimeDelayMs)); | ||
| 68 | ||||
| 69 | 2 | auto startTime = getTimeNowNt() + primeDelayNt; | ||
| 70 |
1/1✓ Branch 4 taken 2 times.
|
2 | getScheduler()->schedule("primingDelay", nullptr, startTime, action_s::make<onPrimeStartAdapter>( this )); | |
| 71 | } else { | |||
| 72 | ✗ | efiPrintf("Skipped priming pulse since ignSwitchCounter = %lu", ignSwitchCounter); | ||
| 73 | } | |||
| 74 | ||||
| 75 | // we'll reset it later when the engine starts | |||
| 76 | 2 | setKeyCycleCounter(ignSwitchCounter + 1); | ||
| 77 | } | |||
| 78 | ||||
| 79 | 2 | void PrimeController::setKeyCycleCounter(uint32_t count) { | ||
| 80 | #if EFI_BACKUP_SRAM | |||
| 81 | backupRamSave(backup_ram_e::IgnCounter, count); | |||
| 82 | #endif // EFI_BACKUP_SRAM | |||
| 83 | 2 | } | ||
| 84 | ||||
| 85 | 2 | uint32_t PrimeController::getKeyCycleCounter() const { | ||
| 86 | #if EFI_BACKUP_SRAM | |||
| 87 | return backupRamLoad(backup_ram_e::IgnCounter); | |||
| 88 | #else // not EFI_BACKUP_SRAM | |||
| 89 | 2 | return 0; | ||
| 90 | #endif // EFI_BACKUP_SRAM | |||
| 91 | } | |||
| 92 | ||||
| 93 | 1 | void PrimeController::onPrimeStart() { | ||
| 94 | 1 | auto durationMs = getPrimeDuration(); | ||
| 95 | ||||
| 96 | // Don't prime a zero-duration pulse | |||
| 97 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1 time.
|
1 | if (durationMs <= 0) { |
| 98 | ✗ | efiPrintf("Skipped zero-duration priming pulse."); | ||
| 99 | ✗ | return; | ||
| 100 | } | |||
| 101 | #if EFI_PROD_CODE | |||
| 102 | if (durationMs >= TOO_FAR_INTO_FUTURE_MS) { | |||
| 103 | criticalError("Priming duration too long %dms", durationMs); | |||
| 104 | } | |||
| 105 | #endif | |||
| 106 | ||||
| 107 | 1 | efiPrintf("Firing priming pulse of %.2f ms", durationMs); | ||
| 108 | 1 | engine->outputChannels.injectionPrimingCounter++; | ||
| 109 | ||||
| 110 | 1 | auto endTime = sumTickAndFloat(getTimeNowNt(), MSF2NT(durationMs)); | ||
| 111 | ||||
| 112 | // Open all injectors, schedule closing later | |||
| 113 | 1 | m_isPriming = true; | ||
| 114 | 1 | startSimultaneousInjection(); | ||
| 115 |
1/1✓ Branch 4 taken 1 time.
|
1 | getScheduler()->schedule("onPrimeStart", nullptr, endTime, action_s::make<onPrimeEndAdapter>( this )); | |
| 116 | } | |||
| 117 | ||||
| 118 | 1 | void PrimeController::onPrimeEnd() { | ||
| 119 | 1 | endSimultaneousInjectionOnlyTogglePins(); | ||
| 120 | ||||
| 121 | 1 | m_isPriming = false; | ||
| 122 | 1 | } | ||
| 123 | ||||
| 124 | 1092 | void PrimeController::onSlowCallback() { | ||
| 125 | 1092 | if (!getEngineRotationState()->isStopped()) { | ||
| 126 | #if EFI_BACKUP_SRAM | |||
| 127 | backupRamSave(backup_ram_e::IgnCounter, 0); | |||
| 128 | #endif /* EFI_BACKUP_SRAM */ | |||
| 129 | } | |||
| 130 | 1092 | } | ||
| 131 |