| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /** | |||
| 2 | * @file trigger_emulator_algo.cpp | |||
| 3 | * | |||
| 4 | * This file is about producing real electrical signals which emulate trigger signal based on | |||
| 5 | * a known TriggerWaveform. | |||
| 6 | * | |||
| 7 | * Historically this implementation was implemented based on PwmConfig which is maybe not the | |||
| 8 | * best way to implement it. (todo: why is not the best way?) | |||
| 9 | * | |||
| 10 | * A newer implementation of pretty much the same thing is TriggerStimulatorHelper | |||
| 11 | * todo: one emulator should be enough! another one should be eliminated | |||
| 12 | * | |||
| 13 | * @date Mar 3, 2014 | |||
| 14 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
| 15 | */ | |||
| 16 | ||||
| 17 | #include "pch.h" | |||
| 18 | ||||
| 19 | 561377 | int getPreviousIndex(const int currentIndex, const int size) { | ||
| 20 | 561377 | return (currentIndex + size - 1) % size; | ||
| 21 | } | |||
| 22 | ||||
| 23 | 374300 | bool needEvent(const int currentIndex, const MultiChannelStateSequence & mcss, int channelIndex) { | ||
| 24 | 374300 | int prevIndex = getPreviousIndex(currentIndex, mcss.phaseCount); | ||
| 25 | 374300 | pin_state_t previousValue = mcss.getChannelState(channelIndex, /*phaseIndex*/prevIndex); | ||
| 26 | 374300 | pin_state_t currentValue = mcss.getChannelState(channelIndex, /*phaseIndex*/currentIndex); | ||
| 27 | ||||
| 28 | 374300 | return previousValue != currentValue; | ||
| 29 | } | |||
| 30 | ||||
| 31 | #if EFI_EMULATE_POSITION_SENSORS | |||
| 32 | ||||
| 33 | #if !EFI_SHAFT_POSITION_INPUT | |||
| 34 | fail("EFI_SHAFT_POSITION_INPUT required to have EFI_EMULATE_POSITION_SENSORS") | |||
| 35 | #endif | |||
| 36 | ||||
| 37 | #include "trigger_emulator_algo.h" | |||
| 38 | #include "trigger_central.h" | |||
| 39 | #include "trigger_simulator.h" | |||
| 40 | ||||
| 41 | 2 | TriggerEmulatorHelper::TriggerEmulatorHelper() { | ||
| 42 | 2 | } | ||
| 43 | ||||
| 44 | static OutputPin emulatorOutputs[NUM_EMULATOR_CHANNELS][PWM_PHASE_MAX_WAVE_PER_PWM]; | |||
| 45 | ||||
| 46 | 73 | void TriggerEmulatorHelper::handleEmulatorCallback(int channel, const MultiChannelStateSequence& multiChannelStateSequence, int stateIndex) { | ||
| 47 | 73 | efitick_t stamp = getTimeNowNt(); | ||
| 48 | ||||
| 49 | // todo: code duplication with TriggerStimulatorHelper::feedSimulatedEvent? | |||
| 50 | #if EFI_SHAFT_POSITION_INPUT | |||
| 51 |
2/2✓ Branch 0 taken 146 times.
✓ Branch 1 taken 73 times.
|
2/2✓ Decision 'true' taken 146 times.
✓ Decision 'false' taken 73 times.
|
219 | for (size_t i = 0; i < PWM_PHASE_MAX_WAVE_PER_PWM; i++) { |
| 52 |
2/2✓ Branch 1 taken 73 times.
✓ Branch 2 taken 73 times.
|
2/2✓ Decision 'true' taken 73 times.
✓ Decision 'false' taken 73 times.
|
146 | if (needEvent(stateIndex, multiChannelStateSequence, i)) { |
| 53 | 73 | bool isRise = TriggerValue::RISE == multiChannelStateSequence.getChannelState(/*phaseIndex*/i, stateIndex); | ||
| 54 | ||||
| 55 |
3/4✓ Branch 0 taken 24 times.
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
|
73 | isRise ^= (i == 0 && engineConfiguration->invertPrimaryTriggerSignal); | |
| 56 |
3/4✓ Branch 0 taken 49 times.
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
|
73 | isRise ^= (i == 1 && engineConfiguration->invertSecondaryTriggerSignal); | |
| 57 | ||||
| 58 |
1/2✓ Branch 0 taken 73 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 73 times.
✗ Decision 'false' not taken.
|
73 | if (channel == 0) { |
| 59 | 73 | handleShaftSignal(i, isRise, stamp); | ||
| 60 | } else { | |||
| 61 | ✗ | handleVvtCamSignal(isRise ? TriggerValue::RISE : TriggerValue::FALL, stamp, INDEX_BY_BANK_CAM(channel - 1, i)); | ||
| 62 | } | |||
| 63 | } | |||
| 64 | } | |||
| 65 | #endif // EFI_SHAFT_POSITION_INPUT | |||
| 66 | 73 | } | ||
| 67 | ||||
| 68 | // same is used for either self or external trigger simulation | |||
| 69 | PwmConfig triggerEmulatorSignals[NUM_EMULATOR_CHANNELS]; | |||
| 70 | TriggerWaveform *triggerEmulatorWaveforms[NUM_EMULATOR_CHANNELS]; | |||
| 71 | ||||
| 72 | static int atTriggerVersions[NUM_EMULATOR_CHANNELS] = { 0 }; | |||
| 73 | ||||
| 74 | /** | |||
| 75 | * todo: why is this method NOT reciprocal to getCrankDivider?! | |||
| 76 | * todo: oh this method has only one usage? there must me another very similar method! | |||
| 77 | */ | |||
| 78 | ✗ | static float getRpmMultiplier(operation_mode_e mode) { | ||
| 79 | ✗ | switch (mode) { | ||
| 80 | ✗ | case FOUR_STROKE_SYMMETRICAL_CRANK_SENSOR: | ||
| 81 | case FOUR_STROKE_THREE_TIMES_CRANK_SENSOR: | |||
| 82 | case FOUR_STROKE_SIX_TIMES_CRANK_SENSOR: | |||
| 83 | case FOUR_STROKE_TWELVE_TIMES_CRANK_SENSOR: | |||
| 84 | case FOUR_STROKE_CRANK_SENSOR: | |||
| 85 | case FOUR_STROKE_CAM_SENSOR: | |||
| 86 | case OM_NONE: | |||
| 87 | ✗ | return getCrankDivider(mode) / 2.0; | ||
| 88 | ✗ | case TWO_STROKE: | ||
| 89 | // unit test coverage still runs if the value below is changed to '2' not a great sign! | |||
| 90 | // but HW CI insists that we have '1' here | |||
| 91 | ✗ | return 1; | ||
| 92 | }; | |||
| 93 | ✗ | criticalError("We should not have reach this line"); | ||
| 94 | ✗ | return 1; | ||
| 95 | } | |||
| 96 | ||||
| 97 | ✗ | void setTriggerEmulatorRPM(int rpm) { | ||
| 98 | ✗ | criticalAssertVoid(rpm >= 0 && rpm <= 30000, "emulator RPM out of range"); | ||
| 99 | ||||
| 100 | ✗ | engineConfiguration->triggerSimulatorRpm = rpm; | ||
| 101 | /** | |||
| 102 | * All we need to do here is to change the periodMs | |||
| 103 | * togglePwmState() would see that the periodMs has changed and act accordingly | |||
| 104 | */ | |||
| 105 | ✗ | for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) { | ||
| 106 | ✗ | float rPerSecond = NAN; | ||
| 107 | ✗ | if (rpm != 0) { | ||
| 108 | // use 0.5 multiplier for cam | |||
| 109 | ✗ | float rpmM = (channel == 0) ? getRpmMultiplier(getEngineRotationState()->getOperationMode()) : 0.5f; | ||
| 110 | ✗ | rPerSecond = rpm * rpmM / 60.0; // per minute converted to per second | ||
| 111 | } | |||
| 112 | ✗ | triggerEmulatorSignals[channel].setFrequency(rPerSecond); | ||
| 113 | } | |||
| 114 | ||||
| 115 | ✗ | engine->resetEngineSnifferIfInTestMode(); | ||
| 116 | ||||
| 117 | ✗ | efiPrintf("Emulating position sensor(s). RPM=%d", rpm); | ||
| 118 | } | |||
| 119 | ||||
| 120 | ✗ | static void updateTriggerWaveformIfNeeded(PwmConfig *state) { | ||
| 121 | ✗ | for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) { | ||
| 122 | ✗ | if (state != &triggerEmulatorSignals[channel]) | ||
| 123 | ✗ | continue; | ||
| 124 | ||||
| 125 | ✗ | if (atTriggerVersions[channel] < triggerEmulatorWaveforms[channel]->version) { | ||
| 126 | ✗ | atTriggerVersions[channel] = triggerEmulatorWaveforms[channel]->version; | ||
| 127 | ✗ | efiPrintf("Stimulator: updating trigger shape for ch%d: %d/%d %ld", channel, atTriggerVersions[channel], | ||
| 128 | engine->getGlobalConfigurationVersion(), getTimeNowMs()); | |||
| 129 | ||||
| 130 | ✗ | copyPwmParameters(state, &triggerEmulatorWaveforms[channel]->wave); | ||
| 131 | ✗ | state->safe.periodNt = -1; // this would cause loop re-initialization | ||
| 132 | } | |||
| 133 | } | |||
| 134 | ✗ | } | ||
| 135 | ||||
| 136 | static TriggerEmulatorHelper helper; | |||
| 137 | static bool hasStimPins = false; | |||
| 138 | ||||
| 139 | static bool hasInitTriggerEmulator = false; | |||
| 140 | ||||
| 141 | #if EFI_PROD_CODE | |||
| 142 | PUBLIC_API_WEAK void onTriggerEmulatorPinState(int, int) { } | |||
| 143 | #endif /* EFI_PROD_CODE */ | |||
| 144 | ||||
| 145 | # if !EFI_UNIT_TEST | |||
| 146 | ||||
| 147 | static void emulatorApplyPinState(int stateIndex, PwmConfig *state) /* pwm_gen_callback */ { | |||
| 148 | assertStackVoid("emulator", ObdCode::STACK_USAGE_MISC, EXPECTED_REMAINING_STACK); | |||
| 149 | if (engine->triggerCentral.directSelfStimulation) { | |||
| 150 | /** | |||
| 151 | * this callback would invoke the input signal handlers directly | |||
| 152 | */ | |||
| 153 | for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) { | |||
| 154 | if (state != &triggerEmulatorSignals[channel]) | |||
| 155 | continue; | |||
| 156 | helper.handleEmulatorCallback(channel, | |||
| 157 | *state->multiChannelStateSequence, | |||
| 158 | stateIndex); | |||
| 159 | } | |||
| 160 | } | |||
| 161 | ||||
| 162 | #if EFI_PROD_CODE | |||
| 163 | // Only set pins if they're configured - no need to waste the cycles otherwise | |||
| 164 | else if (hasStimPins) { | |||
| 165 | applyPinState(stateIndex, state); | |||
| 166 | ||||
| 167 | // this allows any arbitrary code to synchronize with the trigger emulator | |||
| 168 | for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) { | |||
| 169 | if (state != &triggerEmulatorSignals[channel]) | |||
| 170 | continue; | |||
| 171 | onTriggerEmulatorPinState(stateIndex, channel); | |||
| 172 | } | |||
| 173 | } | |||
| 174 | #endif /* EFI_PROD_CODE */ | |||
| 175 | } | |||
| 176 | ||||
| 177 | static void startSimulatedTriggerSignal() { | |||
| 178 | // No need to start more than once | |||
| 179 | if (hasInitTriggerEmulator) { | |||
| 180 | return; | |||
| 181 | } | |||
| 182 | ||||
| 183 | // store the crank+cam waveforms | |||
| 184 | triggerEmulatorWaveforms[0] = &engine->triggerCentral.triggerShape; | |||
| 185 | for (int cami = 0; cami < CAMS_PER_BANK; cami++) { | |||
| 186 | triggerEmulatorWaveforms[1 + cami] = &engine->triggerCentral.vvtShape[cami]; | |||
| 187 | } | |||
| 188 | ||||
| 189 | setTriggerEmulatorRPM(engineConfiguration->triggerSimulatorRpm); | |||
| 190 | ||||
| 191 | for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) { | |||
| 192 | TriggerWaveform *s = triggerEmulatorWaveforms[channel]; | |||
| 193 | if (s->getSize() == 0) | |||
| 194 | continue; | |||
| 195 | triggerEmulatorSignals[channel].weComplexInit( | |||
| 196 | &engine->scheduler, | |||
| 197 | &s->wave, | |||
| 198 | updateTriggerWaveformIfNeeded, emulatorApplyPinState); | |||
| 199 | } | |||
| 200 | hasInitTriggerEmulator = true; | |||
| 201 | } | |||
| 202 | ||||
| 203 | // self-stimulation | |||
| 204 | // see below for trigger output generator | |||
| 205 | void enableTriggerStimulator(bool incGlobalConfiguration) { | |||
| 206 | startSimulatedTriggerSignal(); | |||
| 207 | engine->triggerCentral.directSelfStimulation = true; | |||
| 208 | engine->rpmCalculator.Register(); | |||
| 209 | if (incGlobalConfiguration) { | |||
| 210 | incrementGlobalConfigurationVersion("trgSim"); | |||
| 211 | } | |||
| 212 | } | |||
| 213 | ||||
| 214 | // start generating trigger signal on physical outputs | |||
| 215 | // similar but different from self-stimulation | |||
| 216 | void enableExternalTriggerStimulator() { | |||
| 217 | startSimulatedTriggerSignal(); | |||
| 218 | engine->triggerCentral.directSelfStimulation = false; | |||
| 219 | incrementGlobalConfigurationVersion("extTrg"); | |||
| 220 | } | |||
| 221 | ||||
| 222 | void disableTriggerStimulator() { | |||
| 223 | engine->triggerCentral.directSelfStimulation = false; | |||
| 224 | for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) { | |||
| 225 | triggerEmulatorSignals[channel].stop(); | |||
| 226 | } | |||
| 227 | hasInitTriggerEmulator = false; | |||
| 228 | incrementGlobalConfigurationVersion("disTrg"); | |||
| 229 | } | |||
| 230 | ||||
| 231 | void onConfigurationChangeRpmEmulatorCallback(engine_configuration_s *previousConfiguration) { | |||
| 232 | if (engineConfiguration->triggerSimulatorRpm == | |||
| 233 | previousConfiguration->triggerSimulatorRpm) { | |||
| 234 | return; | |||
| 235 | } | |||
| 236 | setTriggerEmulatorRPM(engineConfiguration->triggerSimulatorRpm); | |||
| 237 | } | |||
| 238 | ||||
| 239 | void initTriggerEmulator() { | |||
| 240 | startTriggerEmulatorPins(); | |||
| 241 | ||||
| 242 | addConsoleActionI(CMD_RPM, setTriggerEmulatorRPM); | |||
| 243 | } | |||
| 244 | ||||
| 245 | #endif /* EFI_UNIT_TEST */ | |||
| 246 | ||||
| 247 | 221 | void startTriggerEmulatorPins() { | ||
| 248 | 221 | hasStimPins = false; | ||
| 249 |
2/2✓ Branch 0 taken 663 times.
✓ Branch 1 taken 221 times.
|
2/2✓ Decision 'true' taken 663 times.
✓ Decision 'false' taken 221 times.
|
884 | for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) { |
| 250 |
2/2✓ Branch 2 taken 1326 times.
✓ Branch 3 taken 663 times.
|
2/2✓ Decision 'true' taken 1326 times.
✓ Decision 'false' taken 663 times.
|
1989 | for (size_t i = 0; i < efi::size(emulatorOutputs[channel]); i++) { |
| 251 | 1326 | triggerEmulatorSignals[channel].outputPins[i] = &emulatorOutputs[channel][i]; | ||
| 252 | ||||
| 253 | #if EFI_PROD_CODE | |||
| 254 | brain_pin_e pin; | |||
| 255 | ||||
| 256 | pin_output_mode_e outputMode; | |||
| 257 | if (channel == 0) { | |||
| 258 | pin = engineConfiguration->triggerSimulatorPins[i]; | |||
| 259 | outputMode = engineConfiguration->triggerSimulatorPinModes[i]; | |||
| 260 | } else if (channel == 1 && i == 0) { | |||
| 261 | pin = engineConfiguration->camSimulatorPin; | |||
| 262 | outputMode = engineConfiguration->camSimulatorPinMode; | |||
| 263 | } else { | |||
| 264 | // todo: add pin configs for cam simulator channels | |||
| 265 | continue; | |||
| 266 | } | |||
| 267 | ||||
| 268 | // Only bother trying to set output pins if they're configured | |||
| 269 | if (isBrainPinValid(pin)) { | |||
| 270 | hasStimPins = true; | |||
| 271 | } | |||
| 272 | ||||
| 273 | if (isConfigurationChanged(triggerSimulatorPins[i])) { | |||
| 274 | triggerEmulatorSignals[channel].outputPins[i]->initPin("Trigger emulator", pin, | |||
| 275 | outputMode); | |||
| 276 | } | |||
| 277 | #endif // EFI_PROD_CODE | |||
| 278 | } | |||
| 279 | } | |||
| 280 | 221 | } | ||
| 281 | ||||
| 282 | 221 | void stopTriggerEmulatorPins() { | ||
| 283 | #if EFI_PROD_CODE | |||
| 284 | for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) { | |||
| 285 | // todo: add pin configs for cam simulator channels | |||
| 286 | if (channel != 0) | |||
| 287 | continue; | |||
| 288 | for (size_t i = 0; i < efi::size(emulatorOutputs[channel]); i++) { | |||
| 289 | if (isConfigurationChanged(triggerSimulatorPins[i])) { | |||
| 290 | triggerEmulatorSignals[channel].outputPins[i]->deInit(); | |||
| 291 | } | |||
| 292 | } | |||
| 293 | } | |||
| 294 | #endif // EFI_PROD_CODE | |||
| 295 | 221 | } | ||
| 296 | ||||
| 297 | #endif /* EFI_EMULATE_POSITION_SENSORS */ | |||
| 298 |