rusefi-1/firmware/controllers/algo/engine.cpp

693 lines
22 KiB
C++
Raw Normal View History

2015-07-10 06:01:56 -07:00
/**
* @file engine.cpp
*
*
* This might be a http://en.wikipedia.org/wiki/God_object but that's best way I can
* express myself in C/C++. I am open for suggestions :)
*
* @date May 21, 2014
2020-01-07 21:02:40 -08:00
* @author Andrey Belomutskiy, (c) 2012-2020
2015-07-10 06:01:56 -07:00
*/
#include "engine.h"
2019-05-27 15:58:43 -07:00
#include "allsensors.h"
2019-03-29 06:11:13 -07:00
#include "efi_gpio.h"
#include "pin_repository.h"
2015-07-10 06:01:56 -07:00
#include "trigger_central.h"
#include "fuel_math.h"
#include "engine_math.h"
#include "advance_map.h"
#include "speed_density.h"
2016-01-01 16:02:59 -08:00
#include "advance_map.h"
2019-07-06 17:15:49 -07:00
#include "os_util.h"
#include "os_access.h"
#include "settings.h"
2017-11-26 19:30:37 -08:00
#include "aux_valves.h"
#include "map_averaging.h"
#include "fsio_impl.h"
2019-10-11 17:43:21 -07:00
#include "perf_trace.h"
2020-09-07 12:08:54 -07:00
#include "backup_ram.h"
#include "idle_thread.h"
#include "idle_hardware.h"
#include "sensor.h"
#include "gppwm.h"
#include "tachometer.h"
#include "dynoview.h"
2020-10-19 19:04:06 -07:00
#if EFI_MC33816
#include "mc33816.h"
#endif // EFI_MC33816
2015-07-10 06:01:56 -07:00
2020-05-09 19:43:39 -07:00
#if EFI_TUNER_STUDIO
2020-05-25 10:02:05 -07:00
#include "tunerstudio_outputs.h"
2020-05-09 19:43:39 -07:00
#endif /* EFI_TUNER_STUDIO */
2019-04-12 19:07:03 -07:00
#if EFI_PROD_CODE
#include "trigger_emulator_algo.h"
#include "bench_test.h"
2015-07-10 06:01:56 -07:00
#else
#define isRunningBenchTest() true
2016-01-23 15:01:40 -08:00
#endif /* EFI_PROD_CODE */
2015-07-10 06:01:56 -07:00
#if (BOARD_TLE8888_COUNT > 0)
#include "gpio/tle8888.h"
#endif
2019-01-04 21:32:56 -08:00
LoggingWithStorage engineLogger("engine");
2016-01-01 16:02:59 -08:00
EXTERN_ENGINE;
2015-07-10 06:01:56 -07:00
2019-12-21 17:43:11 -08:00
#if EFI_ENGINE_SNIFFER
#include "engine_sniffer.h"
extern int waveChartUsedSize;
extern WaveChart waveChart;
#endif /* EFI_ENGINE_SNIFFER */
FsioState::FsioState() {
#if EFI_ENABLE_ENGINE_WARNING
isEngineWarning = FALSE;
#endif
#if EFI_ENABLE_CRITICAL_ENGINE_STOP
isCriticalEngineCondition = FALSE;
#endif
}
2016-01-23 15:01:40 -08:00
2019-12-21 17:43:11 -08:00
void Engine::resetEngineSnifferIfInTestMode() {
#if EFI_ENGINE_SNIFFER
if (isFunctionalTestMode) {
// TODO: what is the exact reasoning for the exact engine sniffer pause time I wonder
waveChart.pauseEngineSnifferUntilNt = getTimeNowNt() + MS2NT(300);
2019-12-21 17:43:11 -08:00
waveChart.reset();
}
#endif /* EFI_ENGINE_SNIFFER */
}
2020-08-26 10:30:55 -07:00
trigger_type_e getVvtTriggerType(vvt_mode_e vvtMode) {
switch (vvtMode) {
case VVT_2JZ:
return TT_VVT_JZ;
case MIATA_NB2:
return TT_VVT_MIATA_NB2;
2020-08-26 21:43:23 -07:00
case VVT_BOSCH_QUICK_START:
return TT_VVT_BOSCH_QUICK_START;
2020-08-26 10:30:55 -07:00
case VVT_FIRST_HALF:
return TT_ONE;
case VVT_SECOND_HALF:
return TT_ONE;
2020-10-03 07:39:43 -07:00
case VVT_4_1:
return TT_ONE;
case VVT_FORD_ST170:
return TT_FORD_ST170;
2020-08-26 10:30:55 -07:00
default:
return TT_ONE;
}
}
void Engine::initializeTriggerWaveform(Logging *logger DECLARE_ENGINE_PARAMETER_SUFFIX) {
2020-08-26 20:35:11 -07:00
static TriggerState initState;
INJECT_ENGINE_REFERENCE(&initState);
2020-08-26 20:35:11 -07:00
// Re-read config in case it's changed
primaryTriggerConfiguration.update();
vvtTriggerConfiguration.update();
2019-04-12 19:07:03 -07:00
#if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT
2018-12-25 18:18:14 -08:00
// we have a confusing threading model so some synchronization would not hurt
chibios_rt::CriticalSectionLocker csl;
2018-12-25 18:18:14 -08:00
TRIGGER_WAVEFORM(initializeTriggerWaveform(logger,
engineConfiguration->ambiguousOperationMode,
engineConfiguration->useOnlyRisingEdgeForTrigger, &engineConfiguration->trigger));
2018-12-25 18:18:14 -08:00
if (!TRIGGER_WAVEFORM(shapeDefinitionError)) {
2018-12-25 18:18:14 -08:00
/**
2020-08-25 09:45:25 -07:00
* 'initState' instance of TriggerState is used only to initialize 'this' TriggerWaveform instance
2018-12-25 18:18:14 -08:00
* #192 BUG real hardware trigger events could be coming even while we are initializing trigger
*/
calculateTriggerSynchPoint(ENGINE(triggerCentral.triggerShape),
initState PASS_ENGINE_PARAMETER_SUFFIX);
2018-12-25 18:18:14 -08:00
engine->engineCycleEventCount = TRIGGER_WAVEFORM(getLength());
2018-12-25 18:18:14 -08:00
}
2020-08-26 10:30:55 -07:00
if (engineConfiguration->vvtMode != VVT_INACTIVE) {
trigger_config_s config;
2020-08-26 21:06:10 -07:00
ENGINE(triggerCentral).vvtTriggerType = config.type = getVvtTriggerType(engineConfiguration->vvtMode);
2020-08-26 10:30:55 -07:00
ENGINE(triggerCentral).vvtShape.initializeTriggerWaveform(logger,
engineConfiguration->ambiguousOperationMode,
2020-08-26 21:06:10 -07:00
engine->engineConfigurationPtr->vvtCamSensorUseRise, &config);
2020-08-26 10:30:55 -07:00
ENGINE(triggerCentral).vvtShape.initializeSyncPoint(initState,
engine->vvtTriggerConfiguration,
config);
2020-08-26 10:30:55 -07:00
}
if (!TRIGGER_WAVEFORM(shapeDefinitionError)) {
2018-12-25 18:18:14 -08:00
prepareOutputSignals(PASS_ENGINE_PARAMETER_SIGNATURE);
}
2019-01-31 14:55:23 -08:00
#endif /* EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT */
}
static void cylinderCleanupControl(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
2019-04-12 19:07:03 -07:00
#if EFI_ENGINE_CONTROL
bool newValue;
if (engineConfiguration->isCylinderCleanupEnabled) {
newValue = !engine->rpmCalculator.isRunning() && Sensor::get(SensorType::DriverThrottleIntent).value_or(0) > CLEANUP_MODE_TPS;
} else {
newValue = false;
}
if (newValue != engine->isCylinderCleanupMode) {
engine->isCylinderCleanupMode = newValue;
scheduleMsg(&engineLogger, "isCylinderCleanupMode %s", boolToString(newValue));
}
#endif
}
2020-12-27 14:41:28 -08:00
#if ANALOG_HW_CHECK_MODE
static void assertCloseTo(const char * msg, float actual, float expected) {
2020-09-19 13:07:09 -07:00
if (actual < 0.75 * expected || actual > 1.25 * expected) {
firmwareError(OBD_PCM_Processor_Fault, "%s analog input validation failed %f vs %f", msg, actual, expected);
}
}
2020-12-27 14:41:28 -08:00
#endif // ANALOG_HW_CHECK_MODE
void Engine::periodicSlowCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
2019-10-11 17:43:21 -07:00
ScopePerf perf(PE::EnginePeriodicSlowCallback);
// Re-read config in case it's changed
primaryTriggerConfiguration.update();
vvtTriggerConfiguration.update();
watchdog();
updateSlowSensors(PASS_ENGINE_PARAMETER_SIGNATURE);
checkShutdown(PASS_ENGINE_PARAMETER_SIGNATURE);
2019-04-12 19:07:03 -07:00
#if EFI_FSIO
runFsio(PASS_ENGINE_PARAMETER_SIGNATURE);
#else
runHardcodedFsio(PASS_ENGINE_PARAMETER_SIGNATURE);
#endif /* EFI_FSIO */
2018-12-25 18:18:14 -08:00
updateGppwm();
updateIdleControl();
cylinderCleanupControl(PASS_ENGINE_PARAMETER_SIGNATURE);
2020-05-05 05:01:40 -07:00
standardAirCharge = getStandardAirCharge(PASS_ENGINE_PARAMETER_SIGNATURE);
#if (BOARD_TLE8888_COUNT > 0)
static efitick_t tle8888CrankingResetTime = 0;
if (CONFIG(useTLE8888_cranking_hack) && ENGINE(rpmCalculator).isCranking()) {
efitick_t nowNt = getTimeNowNt();
if (nowNt - tle8888CrankingResetTime > MS2NT(300)) {
Tle8888 big update 1 (#1892) * smart gpio: fix tle8888 direct pin mapping for MRE * MRE: use TLE8888 pins instead of MCU gpios that drives TLE8888 * TLE8888: cleanup * TLE8888: do not reset driver private data on WD/undervoltage reset * TLE8888: diagnostic updates * TLE8888 driver: BIG driver rework * TLE8888: check SPI answers for abnormal states Reply with other than requested register can be a sign of: -Power-On-Reset, then OpStat0 will be replyed -WatchDog reset, then FWDStat1 will be replyed -Invalid communication frame, then Diag0 will be replyed Keep tracking last accessed register and check with the next reply. * TLE8888: debug clean-up * TLE8888: implement spi array write This reduce CS inactive state time between two consequent accesses from 8.8 uS to 1.4 uS * TLE8888: fix PP outputs in OD mode * TLE8888: cleanup register definitions * TLE8888: run separate driver thread for each chip instance Calculating poll interval for few chips become more complex, avoid this running thread for each device. * TLE8888: fix cypress and kinetic compilation Both platforms define its own MAX and cause redifination error if common.h is included in driver. * MRE: update mapping.yaml and fix direct pin mapping for TLE8888 * TLE8888: diagnnostic: disable switch off in case of overcurrent For all output, use current limiting instead * TLE8888: check for overvoltage on OUT8..OUT13 * TLE8888: add TODO note about how to recover from failure condition Currently TLE8888 automaticly recovers only from overcurrent and (may be) overtemperature conditions. Short to bat cause output disable (bit in OECONFIG is reset) and needs driver/host intervention. * TLE8888: save few bytes of RAM * TLE8888: Lada Kalina is test mule for IDLE stepper on TLE8888 Don't forget to enable PP mode for TLE8888 outputs 21..24: uncomment line 1087 in tle8888.c * TLE8888: reorder code, cleanup * TLE8888: mode all debug/statisctic to per-chip struct * TLE8888: rework poll interval calculation * MRE: use TLE8888 pins instead of MCU gpios that drives TLE8888 #2
2020-10-23 09:25:30 -07:00
tle8888_req_init();
// let's reset TLE8888 every 300ms while cranking since that's the best we can do to deal with undervoltage reset
// PS: oh yes, it's a horrible design! Please suggest something better!
tle8888CrankingResetTime = nowNt;
}
}
#endif
#if EFI_DYNO_VIEW
updateDynoView(PASS_ENGINE_PARAMETER_SIGNATURE);
#endif
slowCallBackWasInvoked = true;
2020-12-27 14:41:28 -08:00
#if ANALOG_HW_CHECK_MODE
efiAssertVoid(OBD_PCM_Processor_Fault, isAdcChannelValid(CONFIG(clt).adcChannel), "No CLT setting");
efitimesec_t secondsNow = getTimeNowSeconds();
if (secondsNow > 2 && secondsNow < 180) {
assertCloseTo("RPM", Sensor::get(SensorType::Rpm).Value, HW_CHECK_RPM);
} else if (!hasFirmwareError() && secondsNow > 180) {
static bool isHappyTest = false;
if (!isHappyTest) {
setTriggerEmulatorRPM(5 * HW_CHECK_RPM);
scheduleMsg(&engineLogger, "TEST PASSED");
isHappyTest = true;
}
}
assertCloseTo("clt", Sensor::get(SensorType::Clt).Value, 49.3);
assertCloseTo("iat", Sensor::get(SensorType::Iat).Value, 73.2);
assertCloseTo("aut1", Sensor::get(SensorType::AuxTemp1).Value, 13.8);
assertCloseTo("aut2", Sensor::get(SensorType::AuxTemp2).Value, 6.2);
2020-12-27 14:41:28 -08:00
#endif // ANALOG_HW_CHECK_MODE
2018-12-25 18:18:14 -08:00
}
#if (BOARD_TLE8888_COUNT > 0)
extern float vBattForTle8888;
#endif /* BOARD_TLE8888_COUNT */
2015-07-10 06:01:56 -07:00
/**
* We are executing these heavy (logarithm) methods from outside the trigger callbacks for performance reasons.
2016-01-01 14:02:49 -08:00
* See also periodicFastCallback
2015-07-10 06:01:56 -07:00
*/
2017-05-15 20:28:49 -07:00
void Engine::updateSlowSensors(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
2020-12-26 18:31:41 -08:00
updateSwitchInputs(PASS_ENGINE_PARAMETER_SIGNATURE);
2019-04-12 19:07:03 -07:00
#if EFI_ENGINE_CONTROL
2019-01-21 18:48:58 -08:00
int rpm = GET_RPM();
2016-01-30 19:03:36 -08:00
isEngineChartEnabled = CONFIG(isEngineChartEnabled) && rpm < CONFIG(engineSnifferRpmThreshold);
sensorChartMode = rpm < CONFIG(sensorSnifferRpmThreshold) ? CONFIG(sensorChartMode) : SC_OFF;
2016-01-30 19:03:36 -08:00
2017-05-15 20:28:49 -07:00
engineState.updateSlowSensors(PASS_ENGINE_PARAMETER_SIGNATURE);
2015-07-10 06:01:56 -07:00
2017-11-24 16:16:00 -08:00
// todo: move this logic somewhere to sensors folder?
if (isAdcChannelValid(CONFIG(fuelLevelSensor))) {
float fuelLevelVoltage = getVoltageDivided("fuel", engineConfiguration->fuelLevelSensor PASS_ENGINE_PARAMETER_SUFFIX);
sensors.fuelTankLevel = interpolateMsg("fgauge", CONFIG(fuelLevelEmptyTankVoltage), 0,
CONFIG(fuelLevelFullTankVoltage), 100,
2015-07-10 06:01:56 -07:00
fuelLevelVoltage);
}
2017-05-15 20:28:49 -07:00
sensors.vBatt = hasVBatt(PASS_ENGINE_PARAMETER_SIGNATURE) ? getVBatt(PASS_ENGINE_PARAMETER_SIGNATURE) : 12;
2015-07-10 06:01:56 -07:00
#if (BOARD_TLE8888_COUNT > 0)
// nasty value injection into C driver which would not be able to access Engine class
vBattForTle8888 = sensors.vBatt;
#endif /* BOARD_TLE8888_COUNT */
2020-10-19 19:04:06 -07:00
#if EFI_MC33816
initMc33816IfNeeded();
#endif // EFI_MC33816
2019-01-31 08:57:15 -08:00
#endif
2015-07-10 06:01:56 -07:00
}
2020-12-26 18:31:41 -08:00
void Engine::updateSwitchInputs(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
#if EFI_GPIO_HARDWARE
// this value is not used yet
if (isBrainPinValid(CONFIG(clutchDownPin))) {
2020-12-26 18:31:41 -08:00
engine->clutchDownState = efiReadPin(CONFIG(clutchDownPin));
}
if (hasAcToggle(PASS_ENGINE_PARAMETER_SIGNATURE)) {
bool result = getAcToggle(PASS_ENGINE_PARAMETER_SIGNATURE);
if (engine->acSwitchState != result) {
engine->acSwitchState = result;
engine->acSwitchLastChangeTime = getTimeNowUs();
}
engine->acSwitchState = result;
}
if (isBrainPinValid(CONFIG(clutchUpPin))) {
2020-12-26 18:31:41 -08:00
engine->clutchUpState = efiReadPin(CONFIG(clutchUpPin));
}
if (isBrainPinValid(CONFIG(throttlePedalUpPin))) {
2020-12-26 18:31:41 -08:00
engine->engineState.idle.throttlePedalUpState = efiReadPin(CONFIG(throttlePedalUpPin));
}
if (isBrainPinValid(engineConfiguration->brakePedalPin)) {
2020-12-26 18:31:41 -08:00
engine->brakePedalState = efiReadPin(engineConfiguration->brakePedalPin);
}
#endif // EFI_GPIO_HARDWARE
}
2017-11-20 12:01:48 -08:00
void Engine::onTriggerSignalEvent(efitick_t nowNt) {
2015-07-10 06:01:56 -07:00
isSpinning = true;
2017-11-20 12:01:48 -08:00
lastTriggerToothEventTimeNt = nowNt;
2015-07-10 06:01:56 -07:00
}
Engine::Engine() {
2016-12-18 07:02:38 -08:00
reset();
}
Engine::Engine(persistent_config_s *config) {
2016-12-18 07:02:38 -08:00
setConfig(config);
reset();
}
2019-01-05 20:33:04 -08:00
/**
* @see scheduleStopEngine()
* @return true if there is a reason to stop engine
*/
bool Engine::needToStopEngine(efitick_t nowNt) const {
2019-01-05 20:33:04 -08:00
return stopEngineRequestTimeNt != 0 &&
nowNt - stopEngineRequestTimeNt < 3 * NT_PER_SECOND;
2019-01-05 20:33:04 -08:00
}
int Engine::getGlobalConfigurationVersion(void) const {
return globalConfigurationVersion;
}
2016-12-18 07:02:38 -08:00
void Engine::reset() {
2016-01-14 21:01:42 -08:00
/**
* it's important for fixAngle() that engineCycle field never has zero
*/
engineCycle = getEngineCycle(FOUR_STROKE_CRANK_SENSOR);
2015-07-10 06:01:56 -07:00
memset(&ignitionPin, 0, sizeof(ignitionPin));
}
2016-01-01 14:02:49 -08:00
2015-07-10 06:01:56 -07:00
/**
* Here we have a bunch of stuff which should invoked after configuration change
* so that we can prepare some helper structures
*/
void Engine::preCalculate(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
2020-05-09 19:43:39 -07:00
// todo: start using this 'adcToVoltageInputDividerCoefficient' micro-optimization or... throw it away?
2019-01-31 08:57:15 -08:00
#if HAL_USE_ADC
adcToVoltageInputDividerCoefficient = adcToVolts(1) * engineConfiguration->analogInputDividerCoefficient;
2018-11-03 06:44:34 -07:00
#else
adcToVoltageInputDividerCoefficient = engineConfigurationPtr->analogInputDividerCoefficient;
2018-11-03 06:44:34 -07:00
#endif
2020-05-09 19:43:39 -07:00
#if EFI_TUNER_STUDIO
// we take 2 bytes of crc32, no idea if it's right to call it crc16 or not
2020-05-09 21:59:32 -07:00
// we have a hack here - we rely on the fact that engineMake is the first of three relevant fields
2020-05-09 19:43:39 -07:00
tsOutputChannels.engineMakeCodeNameCrc16 = crc32(engineConfiguration->engineMake, 3 * VEHICLE_INFO_SIZE);
2020-06-04 21:43:05 -07:00
// we need and can empty warning message for CRC purposes
memset(engine->config->warning_message, 0, sizeof(error_message_t));
tsOutputChannels.tuneCrc16 = crc32(engine->config, sizeof(persistent_config_s));
2020-05-09 19:43:39 -07:00
#endif /* EFI_TUNER_STUDIO */
2015-07-10 06:01:56 -07:00
}
#if EFI_SHAFT_POSITION_INPUT
void Engine::OnTriggerStateDecodingError() {
Engine *engine = this;
EXPAND_Engine;
2020-01-26 03:12:01 -08:00
warning(CUSTOM_SYNC_COUNT_MISMATCH, "trigger not happy current %d/%d/%d expected %d/%d/%d",
triggerCentral.triggerState.currentCycle.eventCount[0],
triggerCentral.triggerState.currentCycle.eventCount[1],
triggerCentral.triggerState.currentCycle.eventCount[2],
TRIGGER_WAVEFORM(expectedEventCount[0]),
TRIGGER_WAVEFORM(expectedEventCount[1]),
TRIGGER_WAVEFORM(expectedEventCount[2]));
2020-01-26 03:28:33 -08:00
triggerCentral.triggerState.setTriggerErrorState();
2020-01-26 03:12:01 -08:00
triggerCentral.triggerState.totalTriggerErrorCounter++;
if (CONFIG(verboseTriggerSynchDetails) || (triggerCentral.triggerState.someSortOfTriggerError && !CONFIG(silentTriggerError))) {
#if EFI_PROD_CODE
scheduleMsg(&engineLogger, "error: synchronizationPoint @ index %d expected %d/%d/%d got %d/%d/%d",
triggerCentral.triggerState.currentCycle.current_index,
TRIGGER_WAVEFORM(expectedEventCount[0]),
TRIGGER_WAVEFORM(expectedEventCount[1]),
TRIGGER_WAVEFORM(expectedEventCount[2]),
triggerCentral.triggerState.currentCycle.eventCount[0],
triggerCentral.triggerState.currentCycle.eventCount[1],
triggerCentral.triggerState.currentCycle.eventCount[2]);
#endif /* EFI_PROD_CODE */
}
}
void Engine::OnTriggerStateProperState(efitick_t nowNt) {
Engine *engine = this;
EXPAND_Engine;
2020-01-24 23:00:33 -08:00
rpmCalculator.setSpinningUp(nowNt);
}
2020-01-26 09:02:54 -08:00
void Engine::OnTriggerSynchronizationLost() {
Engine *engine = this;
EXPAND_Engine;
// Needed for early instant-RPM detection
engine->rpmCalculator.setStopSpinning();
2020-01-26 09:02:54 -08:00
}
2020-01-26 00:33:45 -08:00
void Engine::OnTriggerInvalidIndex(int currentIndex) {
Engine *engine = this;
EXPAND_Engine;
// let's not show a warning if we are just starting to spin
2020-09-03 16:29:15 -07:00
if (GET_RPM() != 0) {
2020-01-26 00:33:45 -08:00
warning(CUSTOM_SYNC_ERROR, "sync error: index #%d above total size %d", currentIndex, triggerCentral.triggerShape.getSize());
2020-01-26 03:28:33 -08:00
triggerCentral.triggerState.setTriggerErrorState();
2020-01-26 00:33:45 -08:00
}
}
2020-01-22 10:25:35 -08:00
void Engine::OnTriggerSyncronization(bool wasSynchronized) {
2020-01-23 10:39:50 -08:00
// We only care about trigger shape once we have synchronized trigger. Anything could happen
// during first revolution and it's fine
if (wasSynchronized) {
Engine *engine = this;
EXPAND_Engine;
/**
* We can check if things are fine by comparing the number of events in a cycle with the expected number of event.
*/
bool isDecodingError = triggerCentral.triggerState.validateEventCounters(triggerCentral.triggerShape);
2020-01-23 10:39:50 -08:00
enginePins.triggerDecoderErrorPin.setValue(isDecodingError);
// 'triggerStateListener is not null' means we are running a real engine and now just preparing trigger shape
// that's a bit of a hack, a sweet OOP solution would be a real callback or at least 'needDecodingErrorLogic' method?
if (isDecodingError) {
OnTriggerStateDecodingError();
}
engine->triggerErrorDetection.add(isDecodingError);
if (isTriggerDecoderError(PASS_ENGINE_PARAMETER_SIGNATURE)) {
warning(CUSTOM_OBD_TRG_DECODING, "trigger decoding issue. expected %d/%d/%d got %d/%d/%d",
TRIGGER_WAVEFORM(expectedEventCount[0]), TRIGGER_WAVEFORM(expectedEventCount[1]),
TRIGGER_WAVEFORM(expectedEventCount[2]),
triggerCentral.triggerState.currentCycle.eventCount[0],
triggerCentral.triggerState.currentCycle.eventCount[1],
triggerCentral.triggerState.currentCycle.eventCount[2]);
}
}
2020-01-22 10:25:35 -08:00
}
#endif
void Engine::injectEngineReferences() {
Engine *engine = this;
EXPAND_Engine;
INJECT_ENGINE_REFERENCE(&primaryTriggerConfiguration);
INJECT_ENGINE_REFERENCE(&vvtTriggerConfiguration);
INJECT_ENGINE_REFERENCE(&limpManager);
primaryTriggerConfiguration.update();
vvtTriggerConfiguration.update();
triggerCentral.init(PASS_ENGINE_PARAMETER_SIGNATURE);
}
2016-12-18 07:02:38 -08:00
void Engine::setConfig(persistent_config_s *config) {
2016-01-25 00:02:33 -08:00
this->config = config;
engineConfigurationPtr = &config->engineConfiguration;
2016-01-25 00:02:33 -08:00
memset(config, 0, sizeof(persistent_config_s));
injectEngineReferences();
2015-07-10 06:01:56 -07:00
}
void Engine::printKnockState(void) {
2019-01-04 21:32:56 -08:00
scheduleMsg(&engineLogger, "knock now=%s/ever=%s", boolToString(knockNow), boolToString(knockEver));
2015-07-10 06:01:56 -07:00
}
void Engine::knockLogic(float knockVolts DECLARE_ENGINE_PARAMETER_SUFFIX) {
2015-07-11 13:01:31 -07:00
this->knockVolts = knockVolts;
knockNow = knockVolts > engineConfiguration->knockVThreshold;
2015-07-10 06:01:56 -07:00
/**
* KnockCount is directly proportional to the degrees of ignition
* advance removed
* ex: degrees to subtract = knockCount;
*/
/**
* TODO use knockLevel as a factor for amount of ignition advance
* to remove
* Perhaps allow the user to set a multiplier
* ex: degrees to subtract = knockCount + (knockLevel * X)
* X = user configurable multiplier
*/
if (knockNow) {
knockEver = true;
timeOfLastKnockEvent = getTimeNowUs();
if (knockCount < engineConfiguration->maxKnockSubDeg)
2015-07-10 06:01:56 -07:00
knockCount++;
} else if (knockCount >= 1) {
knockCount--;
} else {
knockCount = 0;
}
}
void Engine::watchdog() {
#if EFI_ENGINE_CONTROL
if (isRunningPwmTest)
return;
if (!isSpinning) {
2016-11-03 20:02:58 -07:00
if (!isRunningBenchTest() && enginePins.stopPins()) {
2016-11-04 19:02:42 -07:00
// todo: make this a firmwareError assuming functional tests would run
warning(CUSTOM_ERR_2ND_WATCHDOG, "Some pins were turned off by 2nd pass watchdog");
2015-07-10 06:01:56 -07:00
}
return;
}
efitick_t nowNt = getTimeNowNt();
2017-11-20 12:01:48 -08:00
// note that we are ignoring the number of tooth here - we
// check for duration between tooth as if we only have one tooth per revolution which is not the case
2021-01-10 20:46:50 -08:00
#define REVOLUTION_TIME_HIGH_THRESHOLD (60 * US_PER_SECOND_LL / RPM_LOW_THRESHOLD)
2015-07-10 06:01:56 -07:00
/**
* todo: better watch dog implementation should be implemented - see
* http://sourceforge.net/p/rusefi/tickets/96/
*
* note that the result of this subtraction could be negative, that would happen if
* we have a trigger event between the time we've invoked 'getTimeNow' and here
*/
2017-11-20 12:01:48 -08:00
efitick_t timeSinceLastTriggerEvent = nowNt - lastTriggerToothEventTimeNt;
if (timeSinceLastTriggerEvent < US2NT(REVOLUTION_TIME_HIGH_THRESHOLD)) {
2015-07-10 06:01:56 -07:00
return;
}
isSpinning = false;
2016-12-18 07:02:38 -08:00
ignitionEvents.isReady = false;
2019-04-12 19:07:03 -07:00
#if EFI_PROD_CODE || EFI_SIMULATOR
2019-01-04 21:32:56 -08:00
scheduleMsg(&engineLogger, "engine has STOPPED");
scheduleMsg(&engineLogger, "templog engine has STOPPED [%x][%x] [%x][%x] %d",
2015-07-10 06:01:56 -07:00
(int)(nowNt >> 32), (int)nowNt,
2017-11-20 12:01:48 -08:00
(int)(lastTriggerToothEventTimeNt >> 32), (int)lastTriggerToothEventTimeNt,
2015-07-10 06:01:56 -07:00
(int)timeSinceLastTriggerEvent
);
triggerInfo();
#endif
2016-11-03 20:02:58 -07:00
enginePins.stopPins();
2015-07-10 06:01:56 -07:00
#endif
}
void Engine::checkShutdown(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
2019-04-12 19:07:03 -07:00
#if EFI_MAIN_RELAY_CONTROL
// if we are already in the "ignition_on" mode, then do nothing
if (ignitionOnTimeNt > 0) {
return;
}
// here we are in the shutdown (the ignition is off) or initial mode (after the firmware fresh start)
const efitick_t engineStopWaitTimeoutUs = 500000LL; // 0.5 sec
// in shutdown mode, we need a small cooldown time between the ignition off and on
if (stopEngineRequestTimeNt == 0 || (getTimeNowNt() - stopEngineRequestTimeNt) > US2NT(engineStopWaitTimeoutUs)) {
// if the ignition key is turned on again,
// we cancel the shutdown mode, but only if all shutdown procedures are complete
const float vBattThresholdOn = 8.0f;
if ((sensors.vBatt > vBattThresholdOn) && !isInShutdownMode(PASS_ENGINE_PARAMETER_SIGNATURE)) {
ignitionOnTimeNt = getTimeNowNt();
stopEngineRequestTimeNt = 0;
scheduleMsg(&engineLogger, "Ingition voltage detected! Cancel the engine shutdown!");
}
}
#endif /* EFI_MAIN_RELAY_CONTROL */
}
2021-01-10 20:46:50 -08:00
bool Engine::isInMainRelayBench(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
if (mainRelayBenchStartNt == 0) {
return false;
}
return (getTimeNowNt() - mainRelayBenchStartNt) < NT_PER_SECOND;
}
bool Engine::isInShutdownMode(DECLARE_ENGINE_PARAMETER_SIGNATURE) const {
2019-04-12 19:07:03 -07:00
#if EFI_MAIN_RELAY_CONTROL
// if we are in "ignition_on" mode and not in shutdown mode
if (stopEngineRequestTimeNt == 0 && ignitionOnTimeNt > 0) {
const float vBattThresholdOff = 5.0f;
// start the shutdown process if the ignition voltage dropped low
if (sensors.vBatt <= vBattThresholdOff) {
scheduleStopEngine();
}
}
// we are not in the shutdown mode?
if (stopEngineRequestTimeNt == 0) {
return false;
}
2021-01-10 20:46:50 -08:00
const efitick_t turnOffWaitTimeoutNt = NT_PER_SECOND;
// We don't want any transients to step in, so we wait at least 1 second whatever happens.
// Also it's good to give the stepper motor some time to start moving to the initial position (or parking)
2021-01-10 20:46:50 -08:00
if ((getTimeNowNt() - stopEngineRequestTimeNt) < turnOffWaitTimeoutNt)
return true;
2021-01-10 20:46:50 -08:00
const efitick_t engineSpinningWaitTimeoutNt = 5 * NT_PER_SECOND;
// The engine is still spinning! Give it some time to stop (but wait no more than 5 secs)
2021-01-10 20:46:50 -08:00
if (isSpinning && (getTimeNowNt() - stopEngineRequestTimeNt) < engineSpinningWaitTimeoutNt)
return true;
// The idle motor valve is still moving! Give it some time to park (but wait no more than 10 secs)
// Usually it can move to the initial 'cranking' position or zero 'parking' position.
2021-01-10 20:46:50 -08:00
const efitick_t idleMotorWaitTimeoutNt = 10 * NT_PER_SECOND;
if (isIdleMotorBusy(PASS_ENGINE_PARAMETER_SIGNATURE) && (getTimeNowNt() - stopEngineRequestTimeNt) < idleMotorWaitTimeoutNt)
return true;
#endif /* EFI_MAIN_RELAY_CONTROL */
return false;
}
bool Engine::isMainRelayEnabled(DECLARE_ENGINE_PARAMETER_SIGNATURE) const {
#if EFI_MAIN_RELAY_CONTROL
return enginePins.mainRelay.getLogicValue();
#else
// if no main relay control, we assume it's always turned on
return true;
#endif /* EFI_MAIN_RELAY_CONTROL */
}
float Engine::getTimeIgnitionSeconds(void) const {
// return negative if the ignition is turned off
if (ignitionOnTimeNt == 0)
return -1;
float numSeconds = (float)NT2US(getTimeNowNt() - ignitionOnTimeNt) / 1000000.0f;
return numSeconds;
}
2017-05-15 20:28:49 -07:00
injection_mode_e Engine::getCurrentInjectionMode(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
return rpmCalculator.isCranking() ? CONFIG(crankingInjectionMode) : CONFIG(injectionMode);
2016-11-30 19:06:43 -08:00
}
// see also in TunerStudio project '[doesTriggerImplyOperationMode] tag
static bool doesTriggerImplyOperationMode(trigger_type_e type) {
return type != TT_TOOTHED_WHEEL
&& type != TT_ONE
&& type != TT_ONE_PLUS_ONE
&& type != TT_3_1_CAM
&& type != TT_TOOTHED_WHEEL_60_2
&& type != TT_TOOTHED_WHEEL_36_1;
}
operation_mode_e Engine::getOperationMode(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
/**
* here we ignore user-provided setting for well known triggers.
* For instance for Miata NA, there is no reason to allow user to set FOUR_STROKE_CRANK_SENSOR
*/
return doesTriggerImplyOperationMode(engineConfiguration->trigger.type) ? triggerCentral.triggerShape.getOperationMode() : engineConfiguration->ambiguousOperationMode;
}
2020-06-01 06:09:55 -07:00
int Engine::getRpmHardLimit(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
if (engineConfiguration->useFSIO6ForRevLimiter) {
return fsioState.fsioRpmHardLimit;
}
return CONFIG(rpmHardLimit);
}
2015-07-10 06:01:56 -07:00
/**
* The idea of this method is to execute all heavy calculations in a lower-priority thread,
2016-01-01 14:02:49 -08:00
* so that trigger event handler/IO scheduler tasks are faster.
2015-07-10 06:01:56 -07:00
*/
2017-05-15 20:28:49 -07:00
void Engine::periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
2019-10-11 17:43:21 -07:00
ScopePerf pc(PE::EnginePeriodicFastCallback);
2016-01-01 14:02:49 -08:00
#if EFI_MAP_AVERAGING
refreshMapAveragingPreCalc(PASS_ENGINE_PARAMETER_SIGNATURE);
#endif
2016-01-01 14:02:49 -08:00
2017-05-15 20:28:49 -07:00
engineState.periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
tachSignalCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
2015-07-10 06:01:56 -07:00
}
void doScheduleStopEngine(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
scheduleMsg(&engineLogger, "Starting doScheduleStopEngine");
engine->stopEngineRequestTimeNt = getTimeNowNt();
engine->ignitionOnTimeNt = 0;
// let's close injectors or else if these happen to be open right now
enginePins.stopPins();
// todo: initiate stepper motor parking
// make sure we have stored all the info
2020-09-10 18:26:21 -07:00
#if EFI_PROD_CODE
//todo: FIX kinetis build with this line
//backupRamFlush();
2020-09-10 18:26:21 -07:00
#endif // EFI_PROD_CODE
}