Line | Branch | Decision | Exec | Source |
---|---|---|---|---|
1 | /** | |||
2 | * @file engine.cpp | |||
3 | * | |||
4 | * | |||
5 | * This might be a http://en.wikipedia.org/wiki/God_object but that's best way I can | |||
6 | * express myself in C/C++. I am open for suggestions :) | |||
7 | * | |||
8 | * @date May 21, 2014 | |||
9 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
10 | */ | |||
11 | ||||
12 | #include "pch.h" | |||
13 | ||||
14 | #include "trigger_central.h" | |||
15 | #include "fuel_math.h" | |||
16 | #include "advance_map.h" | |||
17 | #include "speed_density.h" | |||
18 | #include "advance_map.h" | |||
19 | #include "init.h" | |||
20 | ||||
21 | #include "aux_valves.h" | |||
22 | #include "perf_trace.h" | |||
23 | #include "backup_ram.h" | |||
24 | #include "idle_thread.h" | |||
25 | #include "idle_hardware.h" | |||
26 | #include "gppwm.h" | |||
27 | #include "speedometer.h" | |||
28 | #include "dynoview.h" | |||
29 | #include "boost_control.h" | |||
30 | #include "ac_control.h" | |||
31 | #include "vr_pwm.h" | |||
32 | #include "max3185x.h" | |||
33 | #if EFI_MC33816 | |||
34 | #include "mc33816.h" | |||
35 | #endif // EFI_MC33816 | |||
36 | ||||
37 | #include "bench_test.h" | |||
38 | ||||
39 | #if EFI_PROD_CODE | |||
40 | #include "trigger_emulator_algo.h" | |||
41 | #endif /* EFI_PROD_CODE */ | |||
42 | ||||
43 | #if (BOARD_TLE8888_COUNT > 0) | |||
44 | #include "gpio/tle8888.h" | |||
45 | #endif | |||
46 | ||||
47 | #if EFI_ENGINE_SNIFFER | |||
48 | #include "engine_sniffer.h" | |||
49 | extern int waveChartUsedSize; | |||
50 | extern WaveChart waveChart; | |||
51 | #endif /* EFI_ENGINE_SNIFFER */ | |||
52 | ||||
53 | 1 | void Engine::resetEngineSnifferIfInTestMode() { | ||
54 | #if EFI_ENGINE_SNIFFER | |||
55 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1 time.
|
1 | if (isFunctionalTestMode) { |
56 | // TODO: what is the exact reasoning for the exact engine sniffer pause time I wonder | |||
57 | ✗ | waveChart.pauseEngineSnifferUntilNt = getTimeNowNt() + MS2NT(300); | ||
58 | ✗ | waveChart.reset(); | ||
59 | } | |||
60 | #endif /* EFI_ENGINE_SNIFFER */ | |||
61 | 1 | } | ||
62 | ||||
63 | ✗ | PUBLIC_API_WEAK trigger_type_e getCustomVvtTriggerType(vvt_mode_e vvtMode) { | ||
64 | ✗ | criticalError("Broken VVT mode maybe corrupted calibration %d: %s", vvtMode, getVvt_mode_e(vvtMode)); | ||
65 | ✗ | return trigger_type_e::TT_HALF_MOON; // we have to return something for the sake of -Werror=return-type | ||
66 | } | |||
67 | ||||
68 | // todo: move this method from engine.cpp already? | |||
69 | /** | |||
70 | * VVT decoding delegates to universal trigger decoder. Here we map vvt_mode_e into corresponding trigger_type_e | |||
71 | */ | |||
72 | 3980 | trigger_type_e getVvtTriggerType(vvt_mode_e vvtMode) { | ||
73 |
11/25✓ Branch 0 taken 3872 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 36 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 6 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 4 times.
✓ Branch 16 taken 10 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 21 taken 2 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 2 times.
✗ Branch 24 not taken.
|
3980 | switch (vvtMode) { | |
74 |
1/1✓ Decision 'true' taken 3872 times.
|
3872 | case VVT_CUSTOM_1: | |
75 | case VVT_CUSTOM_2: | |||
76 | case VVT_INACTIVE: | |||
77 | // hold on, what? 'VVT_INACTIVE' means TT_HALF_MOON?! | |||
78 |
1/1✓ Decision 'true' taken 3872 times.
|
3872 | return trigger_type_e::TT_HALF_MOON; | |
79 |
1/1✓ Decision 'true' taken 14 times.
|
14 | case VVT_TOYOTA_3_TOOTH: | |
80 | 14 | return trigger_type_e::TT_VVT_TOYOTA_3_TOOTH; | ||
81 |
1/1✓ Decision 'true' taken 18 times.
|
18 | case VVT_MIATA_NB: | |
82 | 18 | return trigger_type_e::TT_VVT_MIATA_NB; | ||
83 |
1/1✓ Decision 'true' taken 14 times.
|
14 | case VVT_BOSCH_QUICK_START: | |
84 | 14 | return trigger_type_e::TT_VVT_BOSCH_QUICK_START; | ||
85 |
1/1✓ Decision 'true' taken 2 times.
|
2 | case VVT_HONDA_K_EXHAUST: | |
86 | 2 | return trigger_type_e::TT_HONDA_K_CAM_4_1; | ||
87 |
1/1✓ Decision 'true' taken 36 times.
|
36 | case VVT_HONDA_K_INTAKE: | |
88 | case VVT_SINGLE_TOOTH: | |||
89 | case VVT_MAP_V_TWIN: | |||
90 |
1/1✓ Decision 'true' taken 36 times.
|
36 | return trigger_type_e::TT_HALF_MOON; | |
91 | ✗ | case VVT_FORD_ST170: | ||
92 | ✗ | return trigger_type_e::TT_FORD_ST170; | ||
93 | ✗ | case VVT_BARRA_3_PLUS_1: | ||
94 | ✗ | return trigger_type_e::TT_VVT_BARRA_3_PLUS_1; | ||
95 | ✗ | case VVT_FORD_COYOTE: | ||
96 | ✗ | return trigger_type_e::TT_VVT_FORD_COYOTE; | ||
97 | ✗ | case VVT_DEV: | ||
98 | ✗ | return trigger_type_e::TT_DEV; | ||
99 | ✗ | case VVT_MAZDA_SKYACTIV: | ||
100 | ✗ | return trigger_type_e::TT_VVT_MAZDA_SKYACTIV; | ||
101 | ✗ | case VVT_MAZDA_L: | ||
102 | ✗ | return trigger_type_e::TT_VVT_MAZDA_L; | ||
103 |
1/1✓ Decision 'true' taken 6 times.
|
6 | case VVT_NISSAN_VQ: | |
104 | 6 | return trigger_type_e::TT_VVT_NISSAN_VQ35; | ||
105 | ✗ | case VVT_TOYOTA_4_1: | ||
106 | ✗ | return trigger_type_e::TT_VVT_TOYOTA_4_1; | ||
107 | ✗ | case VVT_MITSUBISHI_4G69: | ||
108 | ✗ | return trigger_type_e::TT_VVT_MITSUBISHI_4G69; | ||
109 |
1/1✓ Decision 'true' taken 4 times.
|
4 | case VVT_MITSUBISHI_3A92: | |
110 | 4 | return trigger_type_e::TT_VVT_MITSUBISHI_3A92; | ||
111 |
1/1✓ Decision 'true' taken 10 times.
|
10 | case VVT_MITSUBISHI_6G72: | |
112 | 10 | return trigger_type_e::TT_VVT_MITSU_6G72; | ||
113 | ✗ | case VVT_HONDA_CBR_600: | ||
114 | ✗ | return trigger_type_e::TT_HONDA_CBR_600; | ||
115 | ✗ | case VVT_CHRYSLER_PHASER: | ||
116 | ✗ | return trigger_type_e::TT_CHRYSLER_PHASER; | ||
117 | ✗ | case VVT_TOYOTA_3TOOTH_UZ: | ||
118 | ✗ | return trigger_type_e::TT_TOYOTA_3_TOOTH_UZ; | ||
119 | ✗ | case VVT_NISSAN_MR: | ||
120 | ✗ | return trigger_type_e::TT_NISSAN_MR18_CAM_VVT; | ||
121 |
1/1✓ Decision 'true' taken 2 times.
|
2 | case VVT_UNUSED_17: | |
122 | case VVT_MITSUBISHI_4G63: | |||
123 |
1/1✓ Decision 'true' taken 2 times.
|
2 | return trigger_type_e::TT_MITSU_4G63_CAM; | |
124 | ✗ | case VVT_HR12DDR_IN: | ||
125 | ✗ | return trigger_type_e::TT_NISSAN_HR_CAM_IN; | ||
126 |
1/1✓ Decision 'true' taken 2 times.
|
2 | case VVT_SUBARU_7TOOTH: | |
127 | 2 | return trigger_type_e::TT_VVT_SUBARU_7_WITHOUT_6; | ||
128 | ✗ | default: | ||
129 | ✗ | return getCustomVvtTriggerType(vvtMode); | ||
130 | } | |||
131 | } | |||
132 | ||||
133 | 905 | void Engine::updateTriggerConfiguration() { | ||
134 | #if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT | |||
135 | // we have a confusing threading model so some synchronization would not hurt | |||
136 | chibios_rt::CriticalSectionLocker csl; | |||
137 | ||||
138 | 905 | engine->triggerCentral.applyShapesConfiguration(); | ||
139 | ||||
140 |
1/2✓ Branch 0 taken 905 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 905 times.
✗ Decision 'false' not taken.
|
905 | if (!engine->triggerCentral.triggerShape.shapeDefinitionError) { |
141 | 905 | prepareOutputSignals(); | ||
142 | } | |||
143 | #endif /* EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT */ | |||
144 | 905 | } | ||
145 | ||||
146 | 1085 | PUBLIC_API_WEAK void boardPeriodicSlowCallback() { } | ||
147 | ||||
148 | 1120 | PUBLIC_API_WEAK void boardPeriodicFastCallback() { } | ||
149 | ||||
150 | 1085 | void Engine::periodicSlowCallback() { | ||
151 | 1085 | ScopePerf perf(PE::EnginePeriodicSlowCallback); | ||
152 | ||||
153 | #if EFI_SHAFT_POSITION_INPUT | |||
154 | // Re-read config in case it's changed | |||
155 |
1/1✓ Branch 1 taken 1085 times.
|
1085 | triggerCentral.primaryTriggerConfiguration.update(); | |
156 |
2/2✓ Branch 0 taken 2170 times.
✓ Branch 1 taken 1085 times.
|
2/2✓ Decision 'true' taken 2170 times.
✓ Decision 'false' taken 1085 times.
|
3255 | for (int camIndex = 0;camIndex < CAMS_PER_BANK;camIndex++) { |
157 |
1/1✓ Branch 2 taken 2170 times.
|
2170 | triggerCentral.vvtTriggerConfiguration[camIndex].update(); | |
158 | } | |||
159 | ||||
160 |
5/6✓ Branch 0 taken 1085 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1085 times.
✓ Branch 5 taken 24 times.
✓ Branch 6 taken 1061 times.
✓ Branch 8 taken 1085 times.
|
1085 | getEngineState()->heaterControlEnabled = engineConfiguration->forceO2Heating || engine->rpmCalculator.isRunning(); | |
161 |
2/2✓ Branch 1 taken 1085 times.
✓ Branch 4 taken 1085 times.
|
1085 | enginePins.o2heater.setValue(getEngineState()->heaterControlEnabled); | |
162 |
2/2✓ Branch 1 taken 1085 times.
✓ Branch 4 taken 1085 times.
|
1085 | enginePins.starterRelayDisable.setValue(Sensor::getOrZero(SensorType::Rpm) < engineConfiguration->cranking.rpm); | |
163 | #endif // EFI_SHAFT_POSITION_INPUT | |||
164 | ||||
165 |
1/1✓ Branch 1 taken 1085 times.
|
1085 | efiWatchdog(); | |
166 |
1/1✓ Branch 1 taken 1085 times.
|
1085 | updateSlowSensors(); | |
167 |
1/1✓ Branch 1 taken 1085 times.
|
1085 | checkShutdown(); | |
168 | ||||
169 |
3/3✓ Branch 1 taken 1085 times.
✓ Branch 5 taken 1085 times.
✓ Branch 8 taken 1085 times.
|
1085 | module<TpsAccelEnrichment>()->onNewValue(Sensor::getOrZero(SensorType::Tps1)); | |
170 | ||||
171 |
1/1✓ Branch 1 taken 1085 times.
|
1085 | updateVrThresholdPwm(); | |
172 | ||||
173 |
1/1✓ Branch 1 taken 1085 times.
|
1085 | updateGppwm(); | |
174 | ||||
175 |
6/8✓ Branch 1 taken 4340 times.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 2121 times.
✓ Branch 6 taken 49 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 49 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1085 times.
|
33635 | engine->engineModules.apply_all([](auto & m) { m.onSlowCallback(); }); | |
176 | ||||
177 | #if (BOARD_TLE8888_COUNT > 0) | |||
178 | tle8888startup(); | |||
179 | #endif | |||
180 | ||||
181 | #if EFI_DYNO_VIEW | |||
182 | updateDynoView(); | |||
183 | #endif | |||
184 | ||||
185 | // TODO: move to sensor_checker.cpp? | |||
186 | if ((engine->rpmCalculator.isCranking()) && (Sensor::getOrZero(SensorType::BatteryVoltage) < 7)) { | |||
187 | // undervoltage crancking! | |||
188 | if (getEngineState()->undervoltageCrankingTimer.getElapsedSeconds() > 1) { | |||
189 | warningTsReport(ObdCode::OBD_System_Voltage_Low, "Cranking on low battery!"); | |||
190 | } | |||
191 | } else { | |||
192 | getEngineState()->undervoltageCrankingTimer.reset(); | |||
193 | } | |||
194 | ||||
195 | slowCallBackWasInvoked = true; | |||
196 | ||||
197 | #if EFI_PROD_CODE | |||
198 | void baroLps25Update(); | |||
199 | baroLps25Update(); | |||
200 | #endif // EFI_PROD_CODE | |||
201 | boardPeriodicSlowCallback(); | |||
202 | } | |||
203 | ||||
204 | /** | |||
205 | * We are executing these heavy (logarithm) methods from outside the trigger callbacks for performance reasons. | |||
206 | * See also periodicFastCallback | |||
207 | */ | |||
208 | 1113 | void Engine::updateSlowSensors() { | ||
209 | 1113 | updateSwitchInputs(); | ||
210 | ||||
211 | #if EFI_PROD_CODE | |||
212 | // todo: extract method? do better? see https://github.com/rusefi/rusefi/issues/7511 for details | |||
213 | engine->module<InjectorModelSecondary>()->updateState(); | |||
214 | engine->module<InjectorModelPrimary>()->updateState(); | |||
215 | #endif // EFI_PROD_CODE | |||
216 | ||||
217 | #if EFI_SHAFT_POSITION_INPUT | |||
218 | 1113 | float rpm = Sensor::getOrZero(SensorType::Rpm); | ||
219 | 1113 | triggerCentral.isEngineSnifferEnabled = rpm < engineConfiguration->engineSnifferRpmThreshold; | ||
220 | #endif // EFI_SHAFT_POSITION_INPUT | |||
221 | 1113 | } | ||
222 | ||||
223 | 1121 | bool getClutchDownState() { | ||
224 | #if EFI_GPIO_HARDWARE | |||
225 |
2/2✓ Branch 1 taken 100 times.
✓ Branch 2 taken 1021 times.
|
2/2✓ Decision 'true' taken 100 times.
✓ Decision 'false' taken 1021 times.
|
1121 | if (isBrainPinValid(engineConfiguration->clutchDownPin)) { |
226 | 100 | return efiReadPin(engineConfiguration->clutchDownPin, engineConfiguration->clutchDownPinMode); | ||
227 | } | |||
228 | #endif // EFI_GPIO_HARDWARE | |||
229 | // todo: boolean sensors should leverage sensor framework #6342 | |||
230 | 1021 | return engine->engineState.lua.clutchDownState; | ||
231 | } | |||
232 | ||||
233 | 1117 | static bool getClutchUpState() { | ||
234 | #if EFI_GPIO_HARDWARE | |||
235 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1117 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1117 times.
|
1117 | if (isBrainPinValid(engineConfiguration->clutchUpPin)) { |
236 | ✗ | return efiReadPin(engineConfiguration->clutchUpPin, engineConfiguration->clutchUpPinMode); | ||
237 | } | |||
238 | #endif // EFI_GPIO_HARDWARE | |||
239 | // todo: boolean sensors should leverage sensor framework #6342 | |||
240 | 1117 | return engine->engineState.lua.clutchUpState; | ||
241 | } | |||
242 | ||||
243 | 1117 | bool getBrakePedalState() { | ||
244 | #if EFI_GPIO_HARDWARE | |||
245 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1117 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1117 times.
|
1117 | if (isBrainPinValid(engineConfiguration->brakePedalPin)) { |
246 | ✗ | return efiReadPin(engineConfiguration->brakePedalPin, engineConfiguration->brakePedalPinMode); | ||
247 | } | |||
248 | #endif // EFI_GPIO_HARDWARE | |||
249 | // todo: boolean sensors should leverage sensor framework #6342 | |||
250 | 1117 | return engine->engineState.lua.brakePedalState; | ||
251 | } | |||
252 | ||||
253 | ||||
254 | 1117 | void Engine::updateSwitchInputs() { | ||
255 | // this value is not used yet | |||
256 | 1117 | engine->engineState.clutchDownState = getClutchDownState(); | ||
257 | 1117 | engine->clutchUpSwitchedState.update(getClutchUpState()); | ||
258 | 1117 | engine->brakePedalSwitchedState.update(getBrakePedalState()); | ||
259 | #if EFI_GPIO_HARDWARE | |||
260 | { | |||
261 | bool currentState; | |||
262 |
2/2✓ Branch 1 taken 90 times.
✓ Branch 2 taken 1027 times.
|
2/2✓ Decision 'true' taken 90 times.
✓ Decision 'false' taken 1027 times.
|
1117 | if (hasAcToggle()) { |
263 | 90 | currentState = getAcToggle(); | ||
264 | #ifdef EFI_KLINE | |||
265 | } else if (engineConfiguration->hondaK) { | |||
266 | extern bool kAcRequestState; | |||
267 | currentState = kAcRequestState; | |||
268 | #endif // EFI_KLINE | |||
269 | } else { | |||
270 | 1027 | currentState = engine->engineState.lua.acRequestState; | ||
271 | } | |||
272 | 1117 | AcController & acController = engine->module<AcController>().unmock(); | ||
273 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1112 times.
|
2/2✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 1112 times.
|
1117 | if (engine->acButtonSwitchedState.update(currentState)) { |
274 | 5 | acController.timeSinceStateChange.reset(); | ||
275 | } | |||
276 | } | |||
277 | ||||
278 | 1117 | pokeAuxDigital(); | ||
279 | ||||
280 | #endif // EFI_GPIO_HARDWARE | |||
281 | 1117 | } | ||
282 | ||||
283 |
22/22✓ Branch 122 taken 677 times.
✓ Branch 127 taken 5416 times.
✓ Branch 128 taken 677 times.
✓ Branch 130 taken 677 times.
✓ Branch 134 taken 677 times.
✓ Branch 137 taken 677 times.
✓ Branch 140 taken 677 times.
✓ Branch 143 taken 677 times.
✓ Branch 155 taken 2708 times.
✓ Branch 156 taken 1354 times.
✓ Branch 157 taken 1354 times.
✓ Branch 158 taken 677 times.
✓ Branch 160 taken 677 times.
✓ Branch 166 taken 677 times.
✓ Branch 169 taken 8124 times.
✓ Branch 171 taken 8124 times.
✓ Branch 172 taken 677 times.
✓ Branch 174 taken 8124 times.
✓ Branch 175 taken 677 times.
✓ Branch 177 taken 677 times.
✓ Branch 182 taken 677 times.
✓ Branch 185 taken 677 times.
|
26403 | Engine::Engine() { | |
284 | // Everything else has default initializers setup in generated file | |||
285 | 677 | engineState.lua.fuelMult = 1; | ||
286 | 677 | ignitionState.luaTimingMult = 1; | ||
287 | 677 | } | ||
288 | ||||
289 | 2250 | int Engine::getGlobalConfigurationVersion() const { | ||
290 | 2250 | return globalConfigurationVersion; | ||
291 | } | |||
292 | ||||
293 | ✗ | void Engine::reset() { | ||
294 | /** | |||
295 | * it's important for wrapAngle() that engineCycle field never has zero | |||
296 | */ | |||
297 | ✗ | engineState.engineCycle = getEngineCycle(FOUR_STROKE_CRANK_SENSOR); | ||
298 | ✗ | resetLua(); | ||
299 | ✗ | } | ||
300 | ||||
301 | ✗ | void Engine::resetLua() { | ||
302 | // todo: https://github.com/rusefi/rusefi/issues/4308 | |||
303 | ✗ | engineState.lua = {}; | ||
304 | ✗ | engineState.lua.fuelAdd = 0; | ||
305 | ✗ | engineState.lua.fuelMult = 1; | ||
306 | ✗ | engineState.lua.luaDisableEtb = false; | ||
307 | ✗ | engineState.lua.luaIgnCut = false; | ||
308 | ✗ | engineState.lua.luaFuelCut = false; | ||
309 | ✗ | engineState.lua.disableDecelerationFuelCutOff = false; | ||
310 | #if EFI_BOOST_CONTROL | |||
311 | ✗ | module<BoostController>().unmock().resetLua(); | ||
312 | #endif // EFI_BOOST_CONTROL | |||
313 | ✗ | ignitionState.luaTimingAdd = 0; | ||
314 | ✗ | ignitionState.luaTimingMult = 1; | ||
315 | #if EFI_IDLE_CONTROL | |||
316 | ✗ | module<IdleController>().unmock().luaAdd = 0; | ||
317 | #endif // EFI_IDLE_CONTROL | |||
318 | ✗ | } | ||
319 | ||||
320 | /** | |||
321 | * Here we have a bunch of stuff which should invoked after configuration change | |||
322 | * so that we can prepare some helper structures | |||
323 | */ | |||
324 | 219 | void Engine::preCalculate() { | ||
325 | #if EFI_TUNER_STUDIO | |||
326 | // we take 2 bytes of crc32, no idea if it's right to call it crc16 or not | |||
327 | // we have a hack here - we rely on the fact that engineMake is the first of three relevant fields | |||
328 | 219 | engine->outputChannels.engineMakeCodeNameCrc16 = crc32(engineConfiguration->engineMake, 3 * VEHICLE_INFO_SIZE); | ||
329 | ||||
330 | 219 | engine->outputChannels.tuneCrc16 = crc32(config, sizeof(persistent_config_s)); | ||
331 | #endif /* EFI_TUNER_STUDIO */ | |||
332 | 219 | } | ||
333 | ||||
334 | #if EFI_SHAFT_POSITION_INPUT | |||
335 | 36279 | void Engine::OnTriggerStateProperState(efitick_t nowNt, size_t triggerStateIndex) { | ||
336 | UNUSED(triggerStateIndex); | |||
337 | ||||
338 | 36279 | rpmCalculator.setSpinningUp(nowNt); | ||
339 | 36279 | } | ||
340 | ||||
341 | 36279 | TriggerStateListener* Engine::nextListener() { | ||
342 | 36279 | return secondListener; | ||
343 | } | |||
344 | ||||
345 | 128 | void Engine::OnTriggerSynchronizationLost() { | ||
346 | // Needed for early instant-RPM detection | |||
347 | 128 | rpmCalculator.setStopSpinning(); | ||
348 | ||||
349 | 128 | triggerCentral.triggerState.resetState(); | ||
350 | 128 | triggerCentral.instantRpm.resetInstantRpm(); | ||
351 | ||||
352 |
2/2✓ Branch 1 taken 256 times.
✓ Branch 2 taken 128 times.
|
2/2✓ Decision 'true' taken 256 times.
✓ Decision 'false' taken 128 times.
|
384 | for (size_t i = 0; i < efi::size(triggerCentral.vvtState); i++) { |
353 |
2/2✓ Branch 1 taken 512 times.
✓ Branch 2 taken 256 times.
|
2/2✓ Decision 'true' taken 512 times.
✓ Decision 'false' taken 256 times.
|
768 | for (size_t j = 0; j < efi::size(triggerCentral.vvtState[0]); j++) { |
354 | 512 | triggerCentral.vvtState[i][j].resetState(); | ||
355 | } | |||
356 | } | |||
357 | 128 | } | ||
358 | ||||
359 | 4466 | void Engine::OnTriggerSynchronization(bool wasSynchronized, bool isDecodingError) { | ||
360 | // TODO: this logic probably shouldn't be part of engine.cpp | |||
361 | ||||
362 | // We only care about trigger shape once we have synchronized trigger. Anything could happen | |||
363 | // during first revolution and it's fine | |||
364 |
2/2✓ Branch 0 taken 4298 times.
✓ Branch 1 taken 168 times.
|
2/2✓ Decision 'true' taken 4298 times.
✓ Decision 'false' taken 168 times.
|
4466 | if (wasSynchronized) { |
365 | 4298 | enginePins.triggerDecoderErrorPin.setValue(isDecodingError); | ||
366 | ||||
367 | // 'triggerStateListener is not null' means we are running a real engine and now just preparing trigger shape | |||
368 | // that's a bit of a hack, a sweet OOP solution would be a real callback or at least 'needDecodingErrorLogic' method? | |||
369 | if (isDecodingError) { | |||
370 | #if EFI_PROD_CODE | |||
371 | if (engineConfiguration->verboseTriggerSynchDetails || (triggerCentral.triggerState.someSortOfTriggerError() && !engineConfiguration->silentTriggerError)) { | |||
372 | efiPrintf("error: synchronizationPoint @ index %lu expected %d/%d got %d/%d", | |||
373 | triggerCentral.triggerState.currentCycle.current_index, | |||
374 | triggerCentral.triggerShape.getExpectedEventCount(TriggerWheel::T_PRIMARY), | |||
375 | triggerCentral.triggerShape.getExpectedEventCount(TriggerWheel::T_SECONDARY), | |||
376 | triggerCentral.triggerState.currentCycle.eventCount[0], | |||
377 | triggerCentral.triggerState.currentCycle.eventCount[1]); | |||
378 | } | |||
379 | #endif /* EFI_PROD_CODE */ | |||
380 | } | |||
381 | ||||
382 | 4298 | engine->triggerCentral.triggerErrorDetection.add(isDecodingError); | ||
383 | } | |||
384 | ||||
385 | 4466 | } | ||
386 | #endif | |||
387 | ||||
388 | ✗ | void Engine::injectEngineReferences() { | ||
389 | #if EFI_SHAFT_POSITION_INPUT | |||
390 | ✗ | triggerCentral.primaryTriggerConfiguration.update(); | ||
391 | ✗ | for (int camIndex = 0;camIndex < CAMS_PER_BANK;camIndex++) { | ||
392 | ✗ | triggerCentral.vvtTriggerConfiguration[camIndex].update(); | ||
393 | } | |||
394 | #endif // EFI_SHAFT_POSITION_INPUT | |||
395 | ✗ | } | ||
396 | ||||
397 | ✗ | void Engine::setConfig() { | ||
398 | #if !EFI_UNIT_TEST | |||
399 | // huh should this be happy? static_assert(config != nullptr); | |||
400 | #endif | |||
401 | ✗ | efi::clear(config); | ||
402 | ||||
403 | ✗ | injectEngineReferences(); | ||
404 | ✗ | } | ||
405 | ||||
406 | /** | |||
407 | * This code asserts that we do not have unexpected gaps in time flow with the exception of internal flash burn. | |||
408 | */ | |||
409 | 1085 | static void assertTimeIsLinear() { | ||
410 | #if ! EFI_UNIT_TEST | |||
411 | static efitimems_t mostRecentMs = 0; | |||
412 | efitimems_t msNow = getTimeNowMs(); | |||
413 | if (engineConfiguration->watchOutForLinearTime && engine->configBurnTimer.hasElapsedSec(5)) { | |||
414 | if (mostRecentMs != 0) { | |||
415 | efitimems_t gapInMs = msNow - mostRecentMs; | |||
416 | // todo: lower gapInMs threshold? | |||
417 | if (gapInMs > 200) { | |||
418 | firmwareError(ObdCode::RUNTIME_CRITICAL_WATCH_DOG_SECONDS, "gap in time: mostRecentMs %lumS, now=%lumS, gap=%lumS", | |||
419 | mostRecentMs, msNow, gapInMs); | |||
420 | } | |||
421 | } | |||
422 | } | |||
423 | mostRecentMs = msNow; | |||
424 | #endif | |||
425 | 1085 | } | ||
426 | ||||
427 | 1085 | void Engine::efiWatchdog() { | ||
428 | 1085 | assertTimeIsLinear(); | ||
429 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1085 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1085 times.
|
1085 | if (isRunningPwmTest) |
430 | ✗ | return; | ||
431 | ||||
432 | #if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT | |||
433 |
5/6✓ Branch 3 taken 1085 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 83 times.
✓ Branch 7 taken 1002 times.
✓ Branch 8 taken 83 times.
✓ Branch 9 taken 1002 times.
|
2/2✓ Decision 'true' taken 83 times.
✓ Decision 'false' taken 1002 times.
|
1085 | if (module<PrimeController>()->isPriming() || triggerCentral.engineMovedRecently()) { |
434 | // do not invoke check in priming or if engine moved recently, no need to assert safe pin state. | |||
435 | 83 | return; | ||
436 | } | |||
437 | ||||
438 |
1/2✓ Branch 0 taken 1002 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 1002 times.
✗ Decision 'false' not taken.
|
1002 | if (!triggerCentral.isSpinningJustForWatchdog) { |
439 |
5/6✓ Branch 1 taken 1002 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 998 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 998 times.
|
2/2✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 998 times.
|
1002 | if (!isRunningBenchTest() && enginePins.stopPins()) { |
440 | // todo: make this a firmwareError assuming functional tests would run | |||
441 | 4 | warning(ObdCode::CUSTOM_ERR_2ND_WATCHDOG, "Some pins were turned off by 2nd pass watchdog"); | ||
442 | } | |||
443 | 1002 | return; | ||
444 | } | |||
445 | ||||
446 | /** | |||
447 | * todo: better watch dog implementation should be implemented - see | |||
448 | * http://sourceforge.net/p/rusefi/tickets/96/ | |||
449 | */ | |||
450 | ✗ | triggerCentral.isSpinningJustForWatchdog = false; | ||
451 | ✗ | onEngineHasStopped(); | ||
452 | #endif // EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT | |||
453 | } | |||
454 | ||||
455 | ✗ | void Engine::onEngineHasStopped() { | ||
456 | #if EFI_ENGINE_CONTROL | |||
457 | ✗ | ignitionEvents.isReady = false; | ||
458 | #endif // EFI_ENGINE_CONTROL | |||
459 | ||||
460 | #if EFI_PROD_CODE || EFI_SIMULATOR | |||
461 | efiPrintf("Engine has stopped spinning."); | |||
462 | #endif | |||
463 | ||||
464 | // this invocation should be the last layer of defence in terms of making sure injectors/coils are not active | |||
465 | ✗ | enginePins.stopPins(); | ||
466 | ✗ | } | ||
467 | ||||
468 | 1085 | void Engine::checkShutdown() { | ||
469 | #if EFI_MAIN_RELAY_CONTROL | |||
470 | // if we are already in the "ignition_on" mode, then do nothing | |||
471 | /* this logic is not alive | |||
472 | if (ignitionOnTimeNt > 0) { | |||
473 | return; | |||
474 | } | |||
475 | todo: move to shutdown_controller.cpp | |||
476 | */ | |||
477 | ||||
478 | // here we are in the shutdown (the ignition is off) or initial mode (after the firmware fresh start) | |||
479 | /* this needs work or tests | |||
480 | const efitick_t engineStopWaitTimeoutUs = 500000LL; // 0.5 sec | |||
481 | // in shutdown mode, we need a small cooldown time between the ignition off and on | |||
482 | todo: move to shutdown_controller.cpp | |||
483 | if (stopEngineRequestTimeNt == 0 || (getTimeNowNt() - stopEngineRequestTimeNt) > US2NT(engineStopWaitTimeoutUs)) { | |||
484 | // if the ignition key is turned on again, | |||
485 | // we cancel the shutdown mode, but only if all shutdown procedures are complete | |||
486 | const float vBattThresholdOn = 8.0f; | |||
487 | // we fallback into zero instead of VBAT_FALLBACK_VALUE because it's not safe to false-trigger the "ignition on" event, | |||
488 | // and we want to turn on the main relay only when 100% sure. | |||
489 | if ((Sensor::getOrZero(SensorType::BatteryVoltage) > vBattThresholdOn) && !isInShutdownMode()) { | |||
490 | ignitionOnTimeNt = getTimeNowNt(); | |||
491 | efiPrintf("Ignition voltage detected!"); | |||
492 | if (stopEngineRequestTimeNt != 0) { | |||
493 | efiPrintf("Cancel the engine shutdown!"); | |||
494 | stopEngineRequestTimeNt = 0; | |||
495 | } | |||
496 | } | |||
497 | } | |||
498 | */ | |||
499 | #endif /* EFI_MAIN_RELAY_CONTROL */ | |||
500 | 1085 | } | ||
501 | ||||
502 | ✗ | bool Engine::isInShutdownMode() const { | ||
503 | // TODO: this logic is currently broken | |||
504 | #if 0 && EFI_MAIN_RELAY_CONTROL && EFI_PROD_CODE | |||
505 | // if we are in "ignition_on" mode and not in shutdown mode | |||
506 | if (stopEngineRequestTimeNt == 0 && ignitionOnTimeNt > 0) { | |||
507 | const float vBattThresholdOff = 5.0f; | |||
508 | // start the shutdown process if the ignition voltage dropped low | |||
509 | if (Sensor::get(SensorType::BatteryVoltage).value_or(VBAT_FALLBACK_VALUE) <= vBattThresholdOff) { | |||
510 | doScheduleStopEngine(); | |||
511 | } | |||
512 | } | |||
513 | ||||
514 | // we are not in the shutdown mode? | |||
515 | if (stopEngineRequestTimeNt == 0) { | |||
516 | return false; | |||
517 | } | |||
518 | ||||
519 | const efitick_t turnOffWaitTimeoutNt = NT_PER_SECOND; | |||
520 | // We don't want any transients to step in, so we wait at least 1 second whatever happens. | |||
521 | // Also it's good to give the stepper motor some time to start moving to the initial position (or parking) | |||
522 | if ((getTimeNowNt() - stopEngineRequestTimeNt) < turnOffWaitTimeoutNt) | |||
523 | return true; | |||
524 | ||||
525 | const efitick_t engineSpinningWaitTimeoutNt = 5 * NT_PER_SECOND; | |||
526 | // The engine is still spinning! Give it some time to stop (but wait no more than 5 secs) | |||
527 | if (isSpinning && (getTimeNowNt() - stopEngineRequestTimeNt) < engineSpinningWaitTimeoutNt) | |||
528 | return true; | |||
529 | ||||
530 | // The idle motor valve is still moving! Give it some time to park (but wait no more than 10 secs) | |||
531 | // Usually it can move to the initial 'cranking' position or zero 'parking' position. | |||
532 | const efitick_t idleMotorWaitTimeoutNt = 10 * NT_PER_SECOND; | |||
533 | if (isIdleMotorBusy() && (getTimeNowNt() - stopEngineRequestTimeNt) < idleMotorWaitTimeoutNt) | |||
534 | return true; | |||
535 | #endif /* EFI_MAIN_RELAY_CONTROL */ | |||
536 | ✗ | return false; | ||
537 | } | |||
538 | ||||
539 | 1165 | bool Engine::isMainRelayEnabled() const { | ||
540 | #if EFI_MAIN_RELAY_CONTROL | |||
541 | 1165 | return enginePins.mainRelay.getLogicValue(); | ||
542 | #else | |||
543 | // if no main relay control, we assume it's always turned on | |||
544 | return true; | |||
545 | #endif /* EFI_MAIN_RELAY_CONTROL */ | |||
546 | } | |||
547 | ||||
548 | 3644 | injection_mode_e getCurrentInjectionMode() { | ||
549 |
2/2✓ Branch 2 taken 1366 times.
✓ Branch 3 taken 2278 times.
|
3644 | return getEngineRotationState()->isCranking() ? engineConfiguration->crankingInjectionMode : engineConfiguration->injectionMode; | |
550 | } | |||
551 | ||||
552 | /** | |||
553 | * The idea of this method is to execute all heavy calculations in a lower-priority thread, | |||
554 | * so that trigger event handler/IO scheduler tasks are faster. | |||
555 | */ | |||
556 | 1120 | void Engine::periodicFastCallback() { | ||
557 | 1120 | ScopePerf pc(PE::EnginePeriodicFastCallback); | ||
558 | ||||
559 |
1/1✓ Branch 1 taken 1120 times.
|
1120 | boardPeriodicFastCallback(); | |
560 | ||||
561 | ||||
562 |
1/1✓ Branch 1 taken 1120 times.
|
1120 | engineState.periodicFastCallback(); | |
563 | ||||
564 |
1/1✓ Branch 1 taken 1120 times.
|
1120 | speedoUpdate(); | |
565 | ||||
566 | 34720 | engineModules.apply_all([](auto & m) { m.onFastCallback(); }); | ||
567 | } | |||
568 | ||||
569 | 130 | void Engine::onEngineStopped() { | ||
570 | 4030 | engineModules.apply_all([](auto& m) { m.onEngineStop(); }); | ||
571 | } | |||
572 | ||||
573 | 1593522 | EngineRotationState * getEngineRotationState() { | ||
574 | 1593522 | return &engine->rpmCalculator; | ||
575 | } | |||
576 | ||||
577 | 337621 | EngineState * getEngineState() { | ||
578 | 337621 | return &engine->engineState; | ||
579 | } | |||
580 | ||||
581 | 9220 | TunerStudioOutputChannels *getTunerStudioOutputChannels() { | ||
582 | 9220 | return &engine->outputChannels; | ||
583 | } | |||
584 | ||||
585 | 1552 | Scheduler *getScheduler() { | ||
586 | 1552 | return &engine->scheduler; | ||
587 | } | |||
588 | ||||
589 | #if EFI_SHAFT_POSITION_INPUT | |||
590 | 734874 | TriggerCentral * getTriggerCentral() { | ||
591 | 734874 | return &engine->triggerCentral; | ||
592 | } | |||
593 | #endif // EFI_SHAFT_POSITION_INPUT | |||
594 | ||||
595 | #if EFI_ENGINE_CONTROL | |||
596 | 134866 | LimpManager * getLimpManager() { | ||
597 | 134866 | return &engine->module<LimpManager>().unmock(); | ||
598 | } | |||
599 | ||||
600 | 28925 | FuelSchedule *getFuelSchedule() { | ||
601 | 28925 | return &engine->injectionEvents; | ||
602 | } | |||
603 | ||||
604 | 86 | IgnitionEventList *getIgnitionEvents() { | ||
605 | 86 | return &engine->ignitionEvents; | ||
606 | } | |||
607 | #endif // EFI_ENGINE_CONTROL | |||
608 |