| 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 | 3988 | trigger_type_e getVvtTriggerType(vvt_mode_e vvtMode) { | ||
| 73 |
11/25✓ Branch 0 taken 3878 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 4 times.
✗ Branch 24 not taken.
|
3988 | switch (vvtMode) { | |
| 74 |
1/1✓ Decision 'true' taken 3878 times.
|
3878 | 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 3878 times.
|
3878 | 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 4 times.
|
4 | case VVT_SUBARU_7TOOTH: | |
| 127 | 4 | return trigger_type_e::TT_VVT_SUBARU_7_WITHOUT_6; | ||
| 128 | ✗ | default: | ||
| 129 | ✗ | return getCustomVvtTriggerType(vvtMode); | ||
| 130 | } | |||
| 131 | } | |||
| 132 | ||||
| 133 | 908 | 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 | 908 | engine->triggerCentral.applyShapesConfiguration(); | ||
| 139 | ||||
| 140 |
1/2✓ Branch 0 taken 908 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 908 times.
✗ Decision 'false' not taken.
|
908 | if (!engine->triggerCentral.triggerShape.shapeDefinitionError) { |
| 141 | 908 | prepareOutputSignals(); | ||
| 142 | } | |||
| 143 | #endif /* EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT */ | |||
| 144 | 908 | } | ||
| 145 | ||||
| 146 | #include "board_overrides.h" | |||
| 147 | ||||
| 148 | std::optional<setup_custom_board_overrides_type> custom_board_periodicSlowCallback; | |||
| 149 | std::optional<setup_custom_board_overrides_type> custom_board_periodicFastCallback; | |||
| 150 | ||||
| 151 | 1086 | void boardPeriodicSlowCallback() { | ||
| 152 | // placeholder to force upgrade | |||
| 153 | 1086 | } | ||
| 154 | ||||
| 155 | 1101 | void boardPeriodicFastCallback() { | ||
| 156 | // placeholder to force upgrade | |||
| 157 | 1101 | } | ||
| 158 | ||||
| 159 | 1086 | void Engine::periodicSlowCallback() { | ||
| 160 | 1086 | ScopePerf perf(PE::EnginePeriodicSlowCallback); | ||
| 161 | ||||
| 162 | #if EFI_SHAFT_POSITION_INPUT | |||
| 163 | // Re-read config in case it's changed | |||
| 164 |
1/1✓ Branch 1 taken 1086 times.
|
1086 | triggerCentral.primaryTriggerConfiguration.update(); | |
| 165 |
2/2✓ Branch 0 taken 2172 times.
✓ Branch 1 taken 1086 times.
|
2/2✓ Decision 'true' taken 2172 times.
✓ Decision 'false' taken 1086 times.
|
3258 | for (int camIndex = 0;camIndex < CAMS_PER_BANK;camIndex++) { |
| 166 |
1/1✓ Branch 2 taken 2172 times.
|
2172 | triggerCentral.vvtTriggerConfiguration[camIndex].update(); | |
| 167 | } | |||
| 168 | ||||
| 169 |
5/6✓ Branch 0 taken 1086 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1086 times.
✓ Branch 5 taken 24 times.
✓ Branch 6 taken 1062 times.
✓ Branch 8 taken 1086 times.
|
1086 | getEngineState()->heaterControlEnabled = engineConfiguration->forceO2Heating || engine->rpmCalculator.isRunning(); | |
| 170 |
2/2✓ Branch 1 taken 1086 times.
✓ Branch 4 taken 1086 times.
|
1086 | enginePins.o2heater.setValue(getEngineState()->heaterControlEnabled); | |
| 171 |
2/2✓ Branch 1 taken 1086 times.
✓ Branch 4 taken 1086 times.
|
1086 | enginePins.starterRelayDisable.setValue(Sensor::getOrZero(SensorType::Rpm) < engineConfiguration->cranking.rpm); | |
| 172 | #endif // EFI_SHAFT_POSITION_INPUT | |||
| 173 | ||||
| 174 |
1/1✓ Branch 1 taken 1086 times.
|
1086 | efiWatchdog(); | |
| 175 |
1/1✓ Branch 1 taken 1086 times.
|
1086 | updateSlowSensors(); | |
| 176 |
1/1✓ Branch 1 taken 1086 times.
|
1086 | checkShutdown(); | |
| 177 | ||||
| 178 |
3/3✓ Branch 1 taken 1086 times.
✓ Branch 5 taken 1086 times.
✓ Branch 8 taken 1086 times.
|
1086 | module<TpsAccelEnrichment>()->onNewValue(Sensor::getOrZero(SensorType::Tps1)); | |
| 179 | ||||
| 180 |
1/1✓ Branch 1 taken 1086 times.
|
1086 | updateVrThresholdPwm(); | |
| 181 | ||||
| 182 |
1/1✓ Branch 1 taken 1086 times.
|
1086 | updateGppwm(); | |
| 183 | ||||
| 184 |
6/8✓ Branch 1 taken 5430 times.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 2123 times.
✓ Branch 6 taken 49 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 49 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1086 times.
|
33666 | engine->engineModules.apply_all([](auto & m) { m.onSlowCallback(); }); | |
| 185 | ||||
| 186 | #if (BOARD_TLE8888_COUNT > 0) | |||
| 187 | tle8888startup(); | |||
| 188 | #endif | |||
| 189 | ||||
| 190 | #if EFI_DYNO_VIEW | |||
| 191 | updateDynoView(); | |||
| 192 | #endif | |||
| 193 | ||||
| 194 | // TODO: move to sensor_checker.cpp? | |||
| 195 | if ((engine->rpmCalculator.isCranking()) && (Sensor::getOrZero(SensorType::BatteryVoltage) < 7)) { | |||
| 196 | // undervoltage crancking! | |||
| 197 | if (getEngineState()->undervoltageCrankingTimer.getElapsedSeconds() > 1) { | |||
| 198 | warningTsReport(ObdCode::OBD_System_Voltage_Low, "Cranking on low battery!"); | |||
| 199 | } | |||
| 200 | } else { | |||
| 201 | getEngineState()->undervoltageCrankingTimer.reset(); | |||
| 202 | } | |||
| 203 | ||||
| 204 | slowCallBackWasInvoked = true; | |||
| 205 | ||||
| 206 | #if EFI_PROD_CODE | |||
| 207 | void baroLps25Update(); | |||
| 208 | baroLps25Update(); | |||
| 209 | #endif // EFI_PROD_CODE | |||
| 210 | boardPeriodicSlowCallback(); | |||
| 211 | call_board_override(custom_board_periodicSlowCallback); | |||
| 212 | } | |||
| 213 | ||||
| 214 | /** | |||
| 215 | * We are executing these heavy (logarithm) methods from outside the trigger callbacks for performance reasons. | |||
| 216 | * See also periodicFastCallback | |||
| 217 | */ | |||
| 218 | 1114 | void Engine::updateSlowSensors() { | ||
| 219 | 1114 | updateSwitchInputs(); | ||
| 220 | ||||
| 221 | #if EFI_PROD_CODE | |||
| 222 | // todo: extract method? do better? see https://github.com/rusefi/rusefi/issues/7511 for details | |||
| 223 | engine->module<InjectorModelSecondary>()->updateState(); | |||
| 224 | engine->module<InjectorModelPrimary>()->updateState(); | |||
| 225 | #endif // EFI_PROD_CODE | |||
| 226 | ||||
| 227 | #if EFI_SHAFT_POSITION_INPUT | |||
| 228 | 1114 | float rpm = Sensor::getOrZero(SensorType::Rpm); | ||
| 229 | 1114 | triggerCentral.isEngineSnifferEnabled = rpm < engineConfiguration->engineSnifferRpmThreshold; | ||
| 230 | #endif // EFI_SHAFT_POSITION_INPUT | |||
| 231 | 1114 | } | ||
| 232 | ||||
| 233 | 1122 | bool getClutchDownState() { | ||
| 234 | #if EFI_GPIO_HARDWARE | |||
| 235 |
2/2✓ Branch 1 taken 100 times.
✓ Branch 2 taken 1022 times.
|
2/2✓ Decision 'true' taken 100 times.
✓ Decision 'false' taken 1022 times.
|
1122 | if (isBrainPinValid(engineConfiguration->clutchDownPin)) { |
| 236 | 100 | return efiReadPin(engineConfiguration->clutchDownPin, engineConfiguration->clutchDownPinMode); | ||
| 237 | } | |||
| 238 | #endif // EFI_GPIO_HARDWARE | |||
| 239 | // todo: boolean sensors should leverage sensor framework #6342 | |||
| 240 | 1022 | return engine->engineState.lua.clutchDownState; | ||
| 241 | } | |||
| 242 | ||||
| 243 | 1118 | static bool getClutchUpState() { | ||
| 244 | #if EFI_GPIO_HARDWARE | |||
| 245 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1118 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1118 times.
|
1118 | if (isBrainPinValid(engineConfiguration->clutchUpPin)) { |
| 246 | ✗ | return efiReadPin(engineConfiguration->clutchUpPin, engineConfiguration->clutchUpPinMode); | ||
| 247 | } | |||
| 248 | #endif // EFI_GPIO_HARDWARE | |||
| 249 | // todo: boolean sensors should leverage sensor framework #6342 | |||
| 250 | 1118 | return engine->engineState.lua.clutchUpState; | ||
| 251 | } | |||
| 252 | ||||
| 253 | 1118 | bool getBrakePedalState() { | ||
| 254 | #if EFI_GPIO_HARDWARE | |||
| 255 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1118 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1118 times.
|
1118 | if (isBrainPinValid(engineConfiguration->brakePedalPin)) { |
| 256 | ✗ | return efiReadPin(engineConfiguration->brakePedalPin, engineConfiguration->brakePedalPinMode); | ||
| 257 | } | |||
| 258 | #endif // EFI_GPIO_HARDWARE | |||
| 259 | // todo: boolean sensors should leverage sensor framework #6342 | |||
| 260 | 1118 | return engine->engineState.lua.brakePedalState; | ||
| 261 | } | |||
| 262 | ||||
| 263 | ||||
| 264 | 1118 | void Engine::updateSwitchInputs() { | ||
| 265 | // this value is not used yet | |||
| 266 | 1118 | engine->engineState.clutchDownState = getClutchDownState(); | ||
| 267 | 1118 | engine->clutchUpSwitchedState.update(getClutchUpState()); | ||
| 268 | 1118 | engine->brakePedalSwitchedState.update(getBrakePedalState()); | ||
| 269 | #if EFI_GPIO_HARDWARE | |||
| 270 | { | |||
| 271 | bool currentState; | |||
| 272 |
2/2✓ Branch 1 taken 90 times.
✓ Branch 2 taken 1028 times.
|
2/2✓ Decision 'true' taken 90 times.
✓ Decision 'false' taken 1028 times.
|
1118 | if (hasAcToggle()) { |
| 273 | 90 | currentState = getAcToggle(); | ||
| 274 | #ifdef EFI_KLINE | |||
| 275 | } else if (engineConfiguration->hondaK) { | |||
| 276 | extern bool kAcRequestState; | |||
| 277 | currentState = kAcRequestState; | |||
| 278 | #endif // EFI_KLINE | |||
| 279 | } else { | |||
| 280 | 1028 | currentState = engine->engineState.lua.acRequestState; | ||
| 281 | } | |||
| 282 | 1118 | AcController & acController = engine->module<AcController>().unmock(); | ||
| 283 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1113 times.
|
2/2✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 1113 times.
|
1118 | if (engine->acButtonSwitchedState.update(currentState)) { |
| 284 | 5 | acController.timeSinceStateChange.reset(); | ||
| 285 | } | |||
| 286 | } | |||
| 287 | ||||
| 288 | 1118 | pokeAuxDigital(); | ||
| 289 | ||||
| 290 | #endif // EFI_GPIO_HARDWARE | |||
| 291 | 1118 | } | ||
| 292 | ||||
| 293 |
22/22✓ Branch 122 taken 678 times.
✓ Branch 127 taken 5424 times.
✓ Branch 128 taken 678 times.
✓ Branch 130 taken 678 times.
✓ Branch 134 taken 678 times.
✓ Branch 137 taken 678 times.
✓ Branch 140 taken 678 times.
✓ Branch 143 taken 678 times.
✓ Branch 155 taken 2712 times.
✓ Branch 156 taken 1356 times.
✓ Branch 157 taken 1356 times.
✓ Branch 158 taken 678 times.
✓ Branch 160 taken 678 times.
✓ Branch 166 taken 678 times.
✓ Branch 169 taken 8136 times.
✓ Branch 171 taken 8136 times.
✓ Branch 172 taken 678 times.
✓ Branch 174 taken 8136 times.
✓ Branch 175 taken 678 times.
✓ Branch 177 taken 678 times.
✓ Branch 182 taken 678 times.
✓ Branch 185 taken 678 times.
|
26442 | Engine::Engine() { | |
| 294 | // Everything else has default initializers setup in generated file | |||
| 295 | 678 | engineState.lua.fuelMult = 1; | ||
| 296 | 678 | ignitionState.luaTimingMult = 1; | ||
| 297 | 678 | } | ||
| 298 | ||||
| 299 | 2444 | int Engine::getGlobalConfigurationVersion() const { | ||
| 300 | 2444 | return globalConfigurationVersion; | ||
| 301 | } | |||
| 302 | ||||
| 303 | ✗ | void Engine::reset() { | ||
| 304 | /** | |||
| 305 | * it's important for wrapAngle() that engineCycle field never has zero | |||
| 306 | */ | |||
| 307 | ✗ | engineState.engineCycle = getEngineCycle(FOUR_STROKE_CRANK_SENSOR); | ||
| 308 | ✗ | resetLua(); | ||
| 309 | ✗ | } | ||
| 310 | ||||
| 311 | ✗ | void Engine::resetLua() { | ||
| 312 | // todo: https://github.com/rusefi/rusefi/issues/4308 | |||
| 313 | ✗ | engineState.lua = {}; | ||
| 314 | ✗ | engineState.lua.fuelAdd = 0; | ||
| 315 | ✗ | engineState.lua.fuelMult = 1; | ||
| 316 | ✗ | engineState.lua.luaDisableEtb = false; | ||
| 317 | ✗ | engineState.lua.luaIgnCut = false; | ||
| 318 | ✗ | engineState.lua.luaFuelCut = false; | ||
| 319 | ✗ | engineState.lua.disableDecelerationFuelCutOff = false; | ||
| 320 | #if EFI_BOOST_CONTROL | |||
| 321 | ✗ | module<BoostController>().unmock().resetLua(); | ||
| 322 | #endif // EFI_BOOST_CONTROL | |||
| 323 | ✗ | ignitionState.luaTimingAdd = 0; | ||
| 324 | ✗ | ignitionState.luaTimingMult = 1; | ||
| 325 | #if EFI_IDLE_CONTROL | |||
| 326 | ✗ | module<IdleController>().unmock().luaAdd = 0; | ||
| 327 | #endif // EFI_IDLE_CONTROL | |||
| 328 | ✗ | } | ||
| 329 | ||||
| 330 | /** | |||
| 331 | * Here we have a bunch of stuff which should invoked after configuration change | |||
| 332 | * so that we can prepare some helper structures | |||
| 333 | */ | |||
| 334 | 221 | void Engine::preCalculate() { | ||
| 335 | #if EFI_TUNER_STUDIO | |||
| 336 | // we take 2 bytes of crc32, no idea if it's right to call it crc16 or not | |||
| 337 | // we have a hack here - we rely on the fact that engineMake is the first of three relevant fields | |||
| 338 | 221 | engine->outputChannels.engineMakeCodeNameCrc16 = crc32(engineConfiguration->engineMake, 3 * VEHICLE_INFO_SIZE); | ||
| 339 | ||||
| 340 | 221 | engine->outputChannels.tuneCrc16 = crc32(config, sizeof(persistent_config_s)); | ||
| 341 | #endif /* EFI_TUNER_STUDIO */ | |||
| 342 | 221 | } | ||
| 343 | ||||
| 344 | #if EFI_SHAFT_POSITION_INPUT | |||
| 345 | 38630 | void Engine::OnTriggerStateProperState(efitick_t nowNt, size_t triggerStateIndex) { | ||
| 346 | UNUSED(triggerStateIndex); | |||
| 347 | ||||
| 348 | 38630 | rpmCalculator.setSpinningUp(nowNt); | ||
| 349 | 38630 | } | ||
| 350 | ||||
| 351 | 38630 | TriggerStateListener* Engine::nextListener() { | ||
| 352 | 38630 | return secondListener; | ||
| 353 | } | |||
| 354 | ||||
| 355 | 129 | void Engine::OnTriggerSynchronizationLost() { | ||
| 356 | // Needed for early instant-RPM detection | |||
| 357 | 129 | rpmCalculator.setStopSpinning(); | ||
| 358 | ||||
| 359 | 129 | triggerCentral.triggerState.resetState(); | ||
| 360 | 129 | triggerCentral.instantRpm.resetInstantRpm(); | ||
| 361 | ||||
| 362 |
2/2✓ Branch 1 taken 258 times.
✓ Branch 2 taken 129 times.
|
2/2✓ Decision 'true' taken 258 times.
✓ Decision 'false' taken 129 times.
|
387 | for (size_t i = 0; i < efi::size(triggerCentral.vvtState); i++) { |
| 363 |
2/2✓ Branch 1 taken 516 times.
✓ Branch 2 taken 258 times.
|
2/2✓ Decision 'true' taken 516 times.
✓ Decision 'false' taken 258 times.
|
774 | for (size_t j = 0; j < efi::size(triggerCentral.vvtState[0]); j++) { |
| 364 | 516 | triggerCentral.vvtState[i][j].resetState(); | ||
| 365 | } | |||
| 366 | } | |||
| 367 | 129 | } | ||
| 368 | ||||
| 369 | 5249 | void Engine::OnTriggerSynchronization(bool wasSynchronized, bool isDecodingError) { | ||
| 370 | // TODO: this logic probably shouldn't be part of engine.cpp | |||
| 371 | ||||
| 372 | // We only care about trigger shape once we have synchronized trigger. Anything could happen | |||
| 373 | // during first revolution and it's fine | |||
| 374 |
2/2✓ Branch 0 taken 5078 times.
✓ Branch 1 taken 171 times.
|
2/2✓ Decision 'true' taken 5078 times.
✓ Decision 'false' taken 171 times.
|
5249 | if (wasSynchronized) { |
| 375 | 5078 | enginePins.triggerDecoderErrorPin.setValue(isDecodingError); | ||
| 376 | ||||
| 377 | // 'triggerStateListener is not null' means we are running a real engine and now just preparing trigger shape | |||
| 378 | // that's a bit of a hack, a sweet OOP solution would be a real callback or at least 'needDecodingErrorLogic' method? | |||
| 379 | if (isDecodingError) { | |||
| 380 | #if EFI_PROD_CODE | |||
| 381 | if (engineConfiguration->verboseTriggerSynchDetails || (triggerCentral.triggerState.someSortOfTriggerError() && !engineConfiguration->silentTriggerError)) { | |||
| 382 | efiPrintf("error: synchronizationPoint @ index %lu expected %d/%d got %d/%d", | |||
| 383 | triggerCentral.triggerState.currentCycle.current_index, | |||
| 384 | triggerCentral.triggerShape.getExpectedEventCount(TriggerWheel::T_PRIMARY), | |||
| 385 | triggerCentral.triggerShape.getExpectedEventCount(TriggerWheel::T_SECONDARY), | |||
| 386 | triggerCentral.triggerState.currentCycle.eventCount[0], | |||
| 387 | triggerCentral.triggerState.currentCycle.eventCount[1]); | |||
| 388 | } | |||
| 389 | #endif /* EFI_PROD_CODE */ | |||
| 390 | } | |||
| 391 | ||||
| 392 | 5078 | engine->triggerCentral.triggerErrorDetection.add(isDecodingError); | ||
| 393 | } | |||
| 394 | ||||
| 395 | 5249 | } | ||
| 396 | #endif | |||
| 397 | ||||
| 398 | ✗ | void Engine::injectEngineReferences() { | ||
| 399 | #if EFI_SHAFT_POSITION_INPUT | |||
| 400 | ✗ | triggerCentral.primaryTriggerConfiguration.update(); | ||
| 401 | ✗ | for (int camIndex = 0;camIndex < CAMS_PER_BANK;camIndex++) { | ||
| 402 | ✗ | triggerCentral.vvtTriggerConfiguration[camIndex].update(); | ||
| 403 | } | |||
| 404 | #endif // EFI_SHAFT_POSITION_INPUT | |||
| 405 | ✗ | } | ||
| 406 | ||||
| 407 | ✗ | void Engine::setConfig() { | ||
| 408 | #if !EFI_UNIT_TEST | |||
| 409 | // huh should this be happy? static_assert(config != nullptr); | |||
| 410 | #endif | |||
| 411 | ✗ | efi::clear(config); | ||
| 412 | ||||
| 413 | ✗ | injectEngineReferences(); | ||
| 414 | ✗ | } | ||
| 415 | ||||
| 416 | /** | |||
| 417 | * This code asserts that we do not have unexpected gaps in time flow with the exception of internal flash burn. | |||
| 418 | */ | |||
| 419 | 1086 | static void assertTimeIsLinear() { | ||
| 420 | #if ! EFI_UNIT_TEST | |||
| 421 | static efitimems_t mostRecentMs = 0; | |||
| 422 | efitimems_t msNow = getTimeNowMs(); | |||
| 423 | if (engineConfiguration->watchOutForLinearTime && engine->configBurnTimer.hasElapsedSec(5)) { | |||
| 424 | if (mostRecentMs != 0) { | |||
| 425 | efitimems_t gapInMs = msNow - mostRecentMs; | |||
| 426 | // todo: lower gapInMs threshold? | |||
| 427 | if (gapInMs > 200) { | |||
| 428 | firmwareError(ObdCode::RUNTIME_CRITICAL_WATCH_DOG_SECONDS, "gap in time: mostRecentMs %lumS, now=%lumS, gap=%lumS", | |||
| 429 | mostRecentMs, msNow, gapInMs); | |||
| 430 | } | |||
| 431 | } | |||
| 432 | } | |||
| 433 | mostRecentMs = msNow; | |||
| 434 | #endif | |||
| 435 | 1086 | } | ||
| 436 | ||||
| 437 | 1086 | void Engine::efiWatchdog() { | ||
| 438 | 1086 | assertTimeIsLinear(); | ||
| 439 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1086 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1086 times.
|
1086 | if (isRunningPwmTest) { |
| 440 | ✗ | return; | ||
| 441 | } | |||
| 442 | ||||
| 443 | #if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT | |||
| 444 |
5/6✓ Branch 3 taken 1086 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 83 times.
✓ Branch 7 taken 1003 times.
✓ Branch 8 taken 83 times.
✓ Branch 9 taken 1003 times.
|
2/2✓ Decision 'true' taken 83 times.
✓ Decision 'false' taken 1003 times.
|
1086 | if (module<PrimeController>()->isPriming() || triggerCentral.engineMovedRecently()) { |
| 445 | // do not invoke check in priming or if engine moved recently, no need to assert safe pin state. | |||
| 446 | 83 | return; | ||
| 447 | } | |||
| 448 | ||||
| 449 |
1/2✓ Branch 0 taken 1003 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 1003 times.
✗ Decision 'false' not taken.
|
1003 | if (!triggerCentral.isSpinningJustForWatchdog) { |
| 450 |
5/6✓ Branch 1 taken 1003 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 999 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 999 times.
|
2/2✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 999 times.
|
1003 | if (!isRunningBenchTest() && enginePins.stopPins()) { |
| 451 | // todo: make this a firmwareError assuming functional tests would run | |||
| 452 | 4 | warning(ObdCode::CUSTOM_ERR_2ND_WATCHDOG, "Some pins were turned off by 2nd pass watchdog"); | ||
| 453 | } | |||
| 454 | 1003 | return; | ||
| 455 | } | |||
| 456 | ||||
| 457 | /** | |||
| 458 | * todo: better watch dog implementation should be implemented - see | |||
| 459 | * http://sourceforge.net/p/rusefi/tickets/96/ | |||
| 460 | */ | |||
| 461 | ✗ | triggerCentral.isSpinningJustForWatchdog = false; | ||
| 462 | ✗ | onEngineHasStopped(); | ||
| 463 | #endif // EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT | |||
| 464 | } | |||
| 465 | ||||
| 466 | ✗ | void Engine::onEngineHasStopped() { | ||
| 467 | #if EFI_ENGINE_CONTROL | |||
| 468 | ✗ | ignitionEvents.isReady = false; | ||
| 469 | #endif // EFI_ENGINE_CONTROL | |||
| 470 | ||||
| 471 | #if EFI_PROD_CODE || EFI_SIMULATOR | |||
| 472 | efiPrintf("Engine has stopped spinning."); | |||
| 473 | #endif | |||
| 474 | ||||
| 475 | // this invocation should be the last layer of defence in terms of making sure injectors/coils are not active | |||
| 476 | ✗ | enginePins.stopPins(); | ||
| 477 | ✗ | } | ||
| 478 | ||||
| 479 | 1086 | void Engine::checkShutdown() { | ||
| 480 | #if EFI_MAIN_RELAY_CONTROL | |||
| 481 | // if we are already in the "ignition_on" mode, then do nothing | |||
| 482 | /* this logic is not alive | |||
| 483 | if (ignitionOnTimeNt > 0) { | |||
| 484 | return; | |||
| 485 | } | |||
| 486 | todo: move to shutdown_controller.cpp | |||
| 487 | */ | |||
| 488 | ||||
| 489 | // here we are in the shutdown (the ignition is off) or initial mode (after the firmware fresh start) | |||
| 490 | /* this needs work or tests | |||
| 491 | const efitick_t engineStopWaitTimeoutUs = 500000LL; // 0.5 sec | |||
| 492 | // in shutdown mode, we need a small cooldown time between the ignition off and on | |||
| 493 | todo: move to shutdown_controller.cpp | |||
| 494 | if (stopEngineRequestTimeNt == 0 || (getTimeNowNt() - stopEngineRequestTimeNt) > US2NT(engineStopWaitTimeoutUs)) { | |||
| 495 | // if the ignition key is turned on again, | |||
| 496 | // we cancel the shutdown mode, but only if all shutdown procedures are complete | |||
| 497 | const float vBattThresholdOn = 8.0f; | |||
| 498 | // we fallback into zero instead of VBAT_FALLBACK_VALUE because it's not safe to false-trigger the "ignition on" event, | |||
| 499 | // and we want to turn on the main relay only when 100% sure. | |||
| 500 | if ((Sensor::getOrZero(SensorType::BatteryVoltage) > vBattThresholdOn) && !isInShutdownMode()) { | |||
| 501 | ignitionOnTimeNt = getTimeNowNt(); | |||
| 502 | efiPrintf("Ignition voltage detected!"); | |||
| 503 | if (stopEngineRequestTimeNt != 0) { | |||
| 504 | efiPrintf("Cancel the engine shutdown!"); | |||
| 505 | stopEngineRequestTimeNt = 0; | |||
| 506 | } | |||
| 507 | } | |||
| 508 | } | |||
| 509 | */ | |||
| 510 | #endif /* EFI_MAIN_RELAY_CONTROL */ | |||
| 511 | 1086 | } | ||
| 512 | ||||
| 513 | ✗ | bool Engine::isInShutdownMode() const { | ||
| 514 | // TODO: this logic is currently broken | |||
| 515 | #if 0 && EFI_MAIN_RELAY_CONTROL && EFI_PROD_CODE | |||
| 516 | // if we are in "ignition_on" mode and not in shutdown mode | |||
| 517 | if (stopEngineRequestTimeNt == 0 && ignitionOnTimeNt > 0) { | |||
| 518 | const float vBattThresholdOff = 5.0f; | |||
| 519 | // start the shutdown process if the ignition voltage dropped low | |||
| 520 | if (Sensor::get(SensorType::BatteryVoltage).value_or(VBAT_FALLBACK_VALUE) <= vBattThresholdOff) { | |||
| 521 | doScheduleStopEngine(); | |||
| 522 | } | |||
| 523 | } | |||
| 524 | ||||
| 525 | // we are not in the shutdown mode? | |||
| 526 | if (stopEngineRequestTimeNt == 0) { | |||
| 527 | return false; | |||
| 528 | } | |||
| 529 | ||||
| 530 | const efitick_t turnOffWaitTimeoutNt = NT_PER_SECOND; | |||
| 531 | // We don't want any transients to step in, so we wait at least 1 second whatever happens. | |||
| 532 | // Also it's good to give the stepper motor some time to start moving to the initial position (or parking) | |||
| 533 | if ((getTimeNowNt() - stopEngineRequestTimeNt) < turnOffWaitTimeoutNt) | |||
| 534 | return true; | |||
| 535 | ||||
| 536 | const efitick_t engineSpinningWaitTimeoutNt = 5 * NT_PER_SECOND; | |||
| 537 | // The engine is still spinning! Give it some time to stop (but wait no more than 5 secs) | |||
| 538 | if (isSpinning && (getTimeNowNt() - stopEngineRequestTimeNt) < engineSpinningWaitTimeoutNt) | |||
| 539 | return true; | |||
| 540 | ||||
| 541 | // The idle motor valve is still moving! Give it some time to park (but wait no more than 10 secs) | |||
| 542 | // Usually it can move to the initial 'cranking' position or zero 'parking' position. | |||
| 543 | const efitick_t idleMotorWaitTimeoutNt = 10 * NT_PER_SECOND; | |||
| 544 | if (isIdleMotorBusy() && (getTimeNowNt() - stopEngineRequestTimeNt) < idleMotorWaitTimeoutNt) | |||
| 545 | return true; | |||
| 546 | #endif /* EFI_MAIN_RELAY_CONTROL */ | |||
| 547 | ✗ | return false; | ||
| 548 | } | |||
| 549 | ||||
| 550 | 1146 | bool Engine::isMainRelayEnabled() const { | ||
| 551 | #if EFI_MAIN_RELAY_CONTROL | |||
| 552 | 1146 | return enginePins.mainRelay.getLogicValue(); | ||
| 553 | #else | |||
| 554 | // if no main relay control, we assume it's always turned on | |||
| 555 | return true; | |||
| 556 | #endif /* EFI_MAIN_RELAY_CONTROL */ | |||
| 557 | } | |||
| 558 | ||||
| 559 | 3643 | injection_mode_e getCurrentInjectionMode() { | ||
| 560 |
2/2✓ Branch 2 taken 1365 times.
✓ Branch 3 taken 2278 times.
|
3643 | return getEngineRotationState()->isCranking() ? engineConfiguration->crankingInjectionMode : engineConfiguration->injectionMode; | |
| 561 | } | |||
| 562 | ||||
| 563 | /** | |||
| 564 | * The idea of this method is to execute all heavy calculations in a lower-priority thread, | |||
| 565 | * so that trigger event handler/IO scheduler tasks are faster. | |||
| 566 | */ | |||
| 567 | 1101 | void Engine::periodicFastCallback() { | ||
| 568 | 1101 | ScopePerf pc(PE::EnginePeriodicFastCallback); | ||
| 569 | ||||
| 570 |
1/1✓ Branch 1 taken 1101 times.
|
1101 | boardPeriodicFastCallback(); | |
| 571 |
1/1✓ Branch 1 taken 1101 times.
|
1101 | call_board_override(custom_board_periodicFastCallback); | |
| 572 | ||||
| 573 | ||||
| 574 |
1/1✓ Branch 1 taken 1101 times.
|
1101 | engineState.periodicFastCallback(); | |
| 575 | ||||
| 576 |
1/1✓ Branch 1 taken 1101 times.
|
1101 | speedoUpdate(); | |
| 577 | ||||
| 578 | 34131 | engineModules.apply_all([](auto & m) { m.onFastCallback(); }); | ||
| 579 | } | |||
| 580 | ||||
| 581 | 131 | void Engine::onEngineStopped() { | ||
| 582 | 4061 | engineModules.apply_all([](auto& m) { m.onEngineStop(); }); | ||
| 583 | } | |||
| 584 | ||||
| 585 | 1618811 | EngineRotationState * getEngineRotationState() { | ||
| 586 | 1618811 | return &engine->rpmCalculator; | ||
| 587 | } | |||
| 588 | ||||
| 589 | 357625 | EngineState * getEngineState() { | ||
| 590 | 357625 | return &engine->engineState; | ||
| 591 | } | |||
| 592 | ||||
| 593 | 9222 | TunerStudioOutputChannels *getTunerStudioOutputChannels() { | ||
| 594 | 9222 | return &engine->outputChannels; | ||
| 595 | } | |||
| 596 | ||||
| 597 | 1550 | Scheduler *getScheduler() { | ||
| 598 | 1550 | return &engine->scheduler; | ||
| 599 | } | |||
| 600 | ||||
| 601 | #if EFI_SHAFT_POSITION_INPUT | |||
| 602 | 804580 | TriggerCentral * getTriggerCentral() { | ||
| 603 | 804580 | return &engine->triggerCentral; | ||
| 604 | } | |||
| 605 | #endif // EFI_SHAFT_POSITION_INPUT | |||
| 606 | ||||
| 607 | #if EFI_ENGINE_CONTROL | |||
| 608 | 144385 | LimpManager * getLimpManager() { | ||
| 609 | 144385 | return &engine->module<LimpManager>().unmock(); | ||
| 610 | } | |||
| 611 | ||||
| 612 | 31264 | FuelSchedule *getFuelSchedule() { | ||
| 613 | 31264 | return &engine->injectionEvents; | ||
| 614 | } | |||
| 615 | ||||
| 616 | 87 | IgnitionEventList *getIgnitionEvents() { | ||
| 617 | 87 | return &engine->ignitionEvents; | ||
| 618 | } | |||
| 619 | #endif // EFI_ENGINE_CONTROL | |||
| 620 |