rusefi/firmware/controllers/trigger/trigger_emulator_algo.cpp

284 lines
9.1 KiB
C++

/**
* @file trigger_emulator_algo.cpp
*
* This file is about producing real electrical signals which emulate trigger signal based on
* a known TriggerWaveform.
*
* Historically this implementation was implemented based on PwmConfig which is maybe not the
* best way to implement it. (todo: why is not the best way?)
*
* A newer implementation of pretty much the same thing is TriggerStimulatorHelper
* todo: one emulator should be enough! another one should be eliminated
*
* @date Mar 3, 2014
* @author Andrey Belomutskiy, (c) 2012-2020
*/
#include "pch.h"
int getPreviousIndex(const int currentIndex, const int size) {
return (currentIndex + size - 1) % size;
}
bool needEvent(const int currentIndex, const MultiChannelStateSequence & mcss, int channelIndex) {
int prevIndex = getPreviousIndex(currentIndex, mcss.phaseCount);
pin_state_t previousValue = mcss.getChannelState(channelIndex, /*phaseIndex*/prevIndex);
pin_state_t currentValue = mcss.getChannelState(channelIndex, /*phaseIndex*/currentIndex);
return previousValue != currentValue;
}
#if EFI_EMULATE_POSITION_SENSORS
#if !EFI_SHAFT_POSITION_INPUT
fail("EFI_SHAFT_POSITION_INPUT required to have EFI_EMULATE_POSITION_SENSORS")
#endif
#include "trigger_emulator_algo.h"
#include "trigger_central.h"
#include "trigger_simulator.h"
TriggerEmulatorHelper::TriggerEmulatorHelper() {
}
static OutputPin emulatorOutputs[NUM_EMULATOR_CHANNELS][PWM_PHASE_MAX_WAVE_PER_PWM];
void TriggerEmulatorHelper::handleEmulatorCallback(int channel, const MultiChannelStateSequence& multiChannelStateSequence, int stateIndex) {
efitick_t stamp = getTimeNowNt();
// todo: code duplication with TriggerStimulatorHelper::feedSimulatedEvent?
#if EFI_SHAFT_POSITION_INPUT
for (size_t i = 0; i < PWM_PHASE_MAX_WAVE_PER_PWM; i++) {
if (needEvent(stateIndex, multiChannelStateSequence, i)) {
bool isRise = TriggerValue::RISE == multiChannelStateSequence.getChannelState(/*phaseIndex*/i, stateIndex);
isRise ^= (i == 0 && engineConfiguration->invertPrimaryTriggerSignal);
isRise ^= (i == 1 && engineConfiguration->invertSecondaryTriggerSignal);
if (channel == 0) {
handleShaftSignal(i, isRise, stamp);
} else {
handleVvtCamSignal(isRise ? TriggerValue::RISE : TriggerValue::FALL, stamp, INDEX_BY_BANK_CAM(channel - 1, i));
}
}
}
#endif // EFI_SHAFT_POSITION_INPUT
}
// same is used for either self or external trigger simulation
PwmConfig triggerEmulatorSignals[NUM_EMULATOR_CHANNELS];
TriggerWaveform *triggerEmulatorWaveforms[NUM_EMULATOR_CHANNELS];
static int atTriggerVersions[NUM_EMULATOR_CHANNELS] = { 0 };
/**
* todo: why is this method NOT reciprocal to getCrankDivider?!
* todo: oh this method has only one usage? there must me another very similar method!
*/
static float getRpmMultiplier(operation_mode_e mode) {
switch (mode) {
case FOUR_STROKE_SYMMETRICAL_CRANK_SENSOR:
case FOUR_STROKE_THREE_TIMES_CRANK_SENSOR:
case FOUR_STROKE_SIX_TIMES_CRANK_SENSOR:
case FOUR_STROKE_TWELVE_TIMES_CRANK_SENSOR:
case FOUR_STROKE_CRANK_SENSOR:
case FOUR_STROKE_CAM_SENSOR:
case OM_NONE:
return getCrankDivider(mode) / 2.0;
case TWO_STROKE:
// unit test coverage still runs if the value below is changed to '2' not a great sign!
// but HW CI insists that we have '1' here
return 1;
};
criticalError("We should not have reach this line");
return 1;
}
void setTriggerEmulatorRPM(int rpm) {
engineConfiguration->triggerSimulatorRpm = rpm;
/**
* All we need to do here is to change the periodMs
* togglePwmState() would see that the periodMs has changed and act accordingly
*/
for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) {
float rPerSecond = NAN;
if (rpm != 0) {
// use 0.5 multiplier for cam
float rpmM = (channel == 0) ? getRpmMultiplier(getEngineRotationState()->getOperationMode()) : 0.5f;
rPerSecond = rpm * rpmM / 60.0; // per minute converted to per second
}
triggerEmulatorSignals[channel].setFrequency(rPerSecond);
}
engine->resetEngineSnifferIfInTestMode();
efiPrintf("Emulating position sensor(s). RPM=%d", rpm);
}
static void updateTriggerWaveformIfNeeded(PwmConfig *state) {
for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) {
if (state != &triggerEmulatorSignals[channel])
continue;
if (atTriggerVersions[channel] < triggerEmulatorWaveforms[channel]->version) {
atTriggerVersions[channel] = triggerEmulatorWaveforms[channel]->version;
efiPrintf("Stimulator: updating trigger shape for ch%d: %d/%d %d", channel, atTriggerVersions[channel],
engine->getGlobalConfigurationVersion(), getTimeNowMs());
copyPwmParameters(state, &triggerEmulatorWaveforms[channel]->wave);
state->safe.periodNt = -1; // this would cause loop re-initialization
}
}
}
static TriggerEmulatorHelper helper;
static bool hasStimPins = false;
static bool hasInitTriggerEmulator = false;
# if !EFI_UNIT_TEST
static void emulatorApplyPinState(int stateIndex, PwmConfig *state) /* pwm_gen_callback */ {
assertStackVoid("emulator", ObdCode::STACK_USAGE_MISC, EXPECTED_REMAINING_STACK);
if (engine->triggerCentral.directSelfStimulation) {
/**
* this callback would invoke the input signal handlers directly
*/
for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) {
if (state != &triggerEmulatorSignals[channel])
continue;
helper.handleEmulatorCallback(channel,
*state->multiChannelStateSequence,
stateIndex);
}
}
#if EFI_PROD_CODE
// Only set pins if they're configured - no need to waste the cycles otherwise
else if (hasStimPins) {
applyPinState(stateIndex, state);
}
#endif /* EFI_PROD_CODE */
}
static void startSimulatedTriggerSignal() {
// No need to start more than once
if (hasInitTriggerEmulator) {
return;
}
// store the crank+cam waveforms
triggerEmulatorWaveforms[0] = &engine->triggerCentral.triggerShape;
for (int cami = 0; cami < CAMS_PER_BANK; cami++) {
triggerEmulatorWaveforms[1 + cami] = &engine->triggerCentral.vvtShape[cami];
}
setTriggerEmulatorRPM(engineConfiguration->triggerSimulatorRpm);
for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) {
TriggerWaveform *s = triggerEmulatorWaveforms[channel];
if (s->getSize() == 0)
continue;
triggerEmulatorSignals[channel].weComplexInit(
&engine->executor,
&s->wave,
updateTriggerWaveformIfNeeded, emulatorApplyPinState);
}
hasInitTriggerEmulator = true;
}
// self-stimulation
// see below for trigger output generator
void enableTriggerStimulator(bool incGlobalConfiguration) {
startSimulatedTriggerSignal();
engine->triggerCentral.directSelfStimulation = true;
engine->rpmCalculator.Register();
if (incGlobalConfiguration) {
incrementGlobalConfigurationVersion("trgSim");
}
}
// start generating trigger signal on physical outputs
// similar but different from self-stimulation
void enableExternalTriggerStimulator() {
startSimulatedTriggerSignal();
engine->triggerCentral.directSelfStimulation = false;
incrementGlobalConfigurationVersion("extTrg");
}
void disableTriggerStimulator() {
engine->triggerCentral.directSelfStimulation = false;
for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) {
triggerEmulatorSignals[channel].stop();
}
hasInitTriggerEmulator = false;
incrementGlobalConfigurationVersion("disTrg");
}
void onConfigurationChangeRpmEmulatorCallback(engine_configuration_s *previousConfiguration) {
if (engineConfiguration->triggerSimulatorRpm ==
previousConfiguration->triggerSimulatorRpm) {
return;
}
setTriggerEmulatorRPM(engineConfiguration->triggerSimulatorRpm);
}
void initTriggerEmulator() {
efiPrintf("Emulating %s", getEngine_type_e(engineConfiguration->engineType));
startTriggerEmulatorPins();
addConsoleActionI(CMD_RPM, setTriggerEmulatorRPM);
}
#endif /* EFI_UNIT_TEST */
void startTriggerEmulatorPins() {
hasStimPins = false;
for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) {
for (size_t i = 0; i < efi::size(emulatorOutputs[channel]); i++) {
triggerEmulatorSignals[channel].outputPins[i] = &emulatorOutputs[channel][i];
#if EFI_PROD_CODE
brain_pin_e pin;
if (channel == 0) {
pin = engineConfiguration->triggerSimulatorPins[i];
} else if (channel == 1 && i == 0) {
pin = engineConfiguration->camSimulatorPin;
} else {
// todo: add pin configs for cam simulator channels
continue;
}
// Only bother trying to set output pins if they're configured
if (isBrainPinValid(pin)) {
hasStimPins = true;
}
if (isConfigurationChanged(triggerSimulatorPins[i])) {
triggerEmulatorSignals[channel].outputPins[i]->initPin("Trigger emulator", pin,
engineConfiguration->triggerSimulatorPinModes[i]);
}
#endif // EFI_PROD_CODE
}
}
}
void stopTriggerEmulatorPins() {
#if EFI_PROD_CODE
for (int channel = 0; channel < NUM_EMULATOR_CHANNELS; channel++) {
// todo: add pin configs for cam simulator channels
if (channel != 0)
continue;
for (size_t i = 0; i < efi::size(emulatorOutputs[channel]); i++) {
if (isConfigurationChanged(triggerSimulatorPins[i])) {
triggerEmulatorSignals[channel].outputPins[i]->deInit();
}
}
}
#endif // EFI_PROD_CODE
}
#endif /* EFI_EMULATE_POSITION_SENSORS */