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 | 1085 | void PrimeController::onSlowCallback() { | ||
125 | 1085 | if (!getEngineRotationState()->isStopped()) { | ||
126 | #if EFI_BACKUP_SRAM | |||
127 | backupRamSave(backup_ram_e::IgnCounter, 0); | |||
128 | #endif /* EFI_BACKUP_SRAM */ | |||
129 | } | |||
130 | 1085 | } | ||
131 |