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

371 lines
11 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
2019-01-04 21:32:56 -08:00
* @author Andrey Belomutskiy, (c) 2012-2019
2015-07-10 06:01:56 -07:00
*/
2018-09-16 19:26:57 -07:00
#include "global.h"
2015-07-10 06:01:56 -07:00
#include "engine.h"
#include "engine_state.h"
#include "efiGpio.h"
#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"
2016-01-25 00:02:33 -08:00
#include "efilib2.h"
#include "settings.h"
2017-11-26 19:30:37 -08:00
#include "aux_valves.h"
#include "map_averaging.h"
#include "fsio_impl.h"
2015-07-10 06:01:56 -07:00
2016-01-23 15:01:40 -08:00
#if EFI_PROD_CODE || defined(__DOXYGEN__)
2015-07-10 06:01:56 -07:00
#include "injector_central.h"
#else
#define isRunningBenchTest() true
2016-01-23 15:01:40 -08:00
#endif /* EFI_PROD_CODE */
2015-07-10 06:01:56 -07:00
2018-12-25 18:18:14 -08:00
static TriggerState initState CCM_OPTIONAL;
2019-01-04 21:32:56 -08:00
LoggingWithStorage engineLogger("engine");
2016-01-01 16:02:59 -08:00
2015-07-10 06:01:56 -07:00
EXTERN_ENGINE
;
#if EFI_TUNER_STUDIO || defined(__DOXYGEN__)
2016-02-10 16:01:47 -08:00
extern TunerStudioOutputChannels tsOutputChannels;
#endif /* EFI_TUNER_STUDIO */
2016-02-10 16:01:47 -08:00
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
2018-12-25 18:18:14 -08:00
void Engine::initializeTriggerShape(Logging *logger DECLARE_ENGINE_PARAMETER_SUFFIX) {
#if !EFI_UNIT_TEST
// we have a confusing threading model so some synchronization would not hurt
bool alreadyLocked = lockAnyContext();
#endif /* EFI_UNIT_TEST */
TRIGGER_SHAPE(initializeTriggerShape(logger,
engineConfiguration->operationMode,
engineConfiguration->useOnlyRisingEdgeForTrigger, &engineConfiguration->trigger));
2018-12-25 18:18:14 -08:00
if (!TRIGGER_SHAPE(shapeDefinitionError)) {
/**
* this instance is used only to initialize 'this' TriggerShape instance
* #192 BUG real hardware trigger events could be coming even while we are initializing trigger
*/
initState.reset();
calculateTriggerSynchPoint(&ENGINE(triggerCentral.triggerShape),
&initState PASS_ENGINE_PARAMETER_SUFFIX);
2018-12-25 18:18:14 -08:00
if (engine->triggerCentral.triggerShape.getSize() == 0) {
firmwareError(CUSTOM_ERR_TRIGGER_ZERO, "triggerShape size is zero");
}
engine->engineCycleEventCount = TRIGGER_SHAPE(getLength());
}
#if !EFI_UNIT_TEST
if (!alreadyLocked) {
unlockAnyContext();
}
#endif
if (!TRIGGER_SHAPE(shapeDefinitionError)) {
prepareOutputSignals(PASS_ENGINE_PARAMETER_SIGNATURE);
}
}
static void cylinderCleanupControl(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
#if EFI_ENGINE_CONTROL || defined(__DOXYGEN__)
bool newValue;
if (engineConfiguration->isCylinderCleanupEnabled) {
newValue = !engine->rpmCalculator.isRunning(PASS_ENGINE_PARAMETER_SIGNATURE) && getTPS(PASS_ENGINE_PARAMETER_SIGNATURE) > CLEANUP_MODE_TPS;
} else {
newValue = false;
}
if (newValue != engine->isCylinderCleanupMode) {
engine->isCylinderCleanupMode = newValue;
scheduleMsg(&engineLogger, "isCylinderCleanupMode %s", boolToString(newValue));
}
#endif
}
void Engine::periodicSlowCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
watchdog();
updateSlowSensors(PASS_ENGINE_PARAMETER_SIGNATURE);
checkShutdown();
#if EFI_FSIO || defined(__DOXYGEN__)
// todo: enable this for unit tests
#if ! EFI_UNIT_TEST
runFsio(PASS_ENGINE_PARAMETER_SIGNATURE);
#endif
#endif /* EFI_PROD_CODE && EFI_FSIO */
2018-12-25 18:18:14 -08:00
cylinderCleanupControl(PASS_ENGINE_PARAMETER_SIGNATURE);
slowCallBackWasInvoked = TRUE;
2018-12-25 18:18:14 -08:00
}
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) {
int rpm = rpmCalculator.getRpm(PASS_ENGINE_PARAMETER_SIGNATURE);
2016-01-30 19:03:36 -08:00
isEngineChartEnabled = CONFIG(isEngineChartEnabled) && rpm < CONFIG(engineSnifferRpmThreshold);
sensorChartMode = rpm < CONFIG(sensorSnifferRpmThreshold) ? CONFIGB(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 (CONFIG(fuelLevelSensor) != EFI_ADC_NONE) {
2015-07-10 06:01:56 -07:00
float fuelLevelVoltage = getVoltageDivided("fuel", engineConfiguration->fuelLevelSensor);
sensors.fuelTankGauge = interpolateMsg("fgauge", CONFIGB(fuelLevelEmptyTankVoltage), 0,
CONFIGB(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
2017-05-15 20:28:49 -07:00
engineState.injectorLag = getInjectorLag(sensors.vBatt PASS_ENGINE_PARAMETER_SUFFIX);
2015-07-10 06:01:56 -07:00
}
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
}
2016-12-18 07:02:38 -08:00
Engine::Engine() {
reset();
}
2015-07-10 06:01:56 -07:00
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) {
return stopEngineRequestTimeNt != 0 &&
nowNt - stopEngineRequestTimeNt < 3 * US2NT(US_PER_SECOND_LL);
}
2016-12-18 07:02:38 -08:00
void Engine::reset() {
2017-02-24 16:20:33 -08:00
withError = isEngineChartEnabled = false;
2017-09-17 19:05:03 -07:00
etbAutoTune = false;
2016-01-30 19:03:36 -08:00
sensorChartMode = SC_OFF;
2016-08-30 18:02:38 -07:00
actualLastInjection = 0;
2017-07-25 17:37:46 -07:00
fsioTimingAdjustment = 0;
2018-07-29 15:02:37 -07:00
fsioIdleTargetRPMAdjustment = 0;
2016-08-30 19:02:21 -07:00
isAlternatorControlEnabled = false;
2017-05-04 14:03:23 -07:00
callFromPitStopEndTime = 0;
2017-07-26 17:27:08 -07:00
rpmHardLimitTimestamp = 0;
2016-08-30 19:02:21 -07:00
wallFuelCorrection = 0;
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);
2017-11-20 12:01:48 -08:00
lastTriggerToothEventTimeNt = 0;
2015-07-10 06:01:56 -07:00
isCylinderCleanupMode = false;
engineCycleEventCount = 0;
stopEngineRequestTimeNt = 0;
isRunningPwmTest = false;
isTestMode = false;
isSpinning = false;
2017-05-11 05:32:08 -07:00
isCltBroken = false;
2015-07-10 06:01:56 -07:00
adcToVoltageInputDividerCoefficient = NAN;
sensors.reset();
2015-07-10 06:01:56 -07:00
memset(&ignitionPin, 0, sizeof(ignitionPin));
knockNow = false;
knockEver = false;
knockCount = 0;
knockDebug = false;
2015-07-11 13:01:31 -07:00
knockVolts = 0;
2016-01-26 20:01:44 -08:00
iHead = NULL;
2015-07-11 13:01:31 -07:00
2015-07-10 06:01:56 -07:00
timeOfLastKnockEvent = 0;
2017-11-06 19:29:39 -08:00
injectionDuration = 0;
2017-05-15 02:08:17 -07:00
clutchDownState = clutchUpState = brakePedalState = false;
2015-07-10 06:01:56 -07:00
memset(&m, 0, sizeof(m));
config = NULL;
engineConfigurationPtr = NULL;
2015-07-10 06:01:56 -07:00
}
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) {
sparkTable.preCalc(engineConfiguration->sparkDwellRpmBins,
engineConfiguration->sparkDwellValues);
2015-07-10 06:01:56 -07:00
2018-11-03 06:44:34 -07:00
#if ! EFI_UNIT_TEST
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
2015-07-10 06:01:56 -07:00
/**
* Here we prepare a fast, index-based MAF lookup from a slower curve description
*/
for (int i = 0; i < MAF_DECODING_CACHE_SIZE; i++) {
float volts = i / MAF_DECODING_CACHE_MULT;
2017-05-29 08:31:07 -07:00
float maf = interpolate2d("maf", volts, config->mafDecodingBins,
2015-07-10 06:01:56 -07:00
config->mafDecoding, MAF_DECODING_COUNT);
mafDecodingLookup[i] = maf;
}
}
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));
2018-07-29 13:30:23 -07:00
engineState.warmupAfrPid.initPidClass(&config->engineConfiguration.warmupAfrPid);
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
#define REVOLUTION_TIME_HIGH_THRESHOLD (60 * 1000000LL / 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;
2017-11-26 19:30:37 -08:00
#if EFI_PROD_CODE || EFI_SIMULATOR || defined(__DOXYGEN__)
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() {
#if EFI_MAIN_RELAY_CONTROL || defined(__DOXYGEN__)
int rpm = rpmCalculator.getRpm();
2019-01-05 20:33:04 -08:00
/**
* Something is weird here: "below 5.0 volts on battery" what is it about? Is this about
* Frankenso powering everything while driver has already turned ignition off? or what is this condition about?
*/
const float vBattThreshold = 5.0f;
if (isValidRpm(rpm) && sensors.vBatt < vBattThreshold && stopEngineRequestTimeNt == 0) {
2019-01-05 20:33:04 -08:00
scheduleStopEngine();
// todo: add stepper motor parking
}
#endif /* EFI_MAIN_RELAY_CONTROL */
}
bool Engine::isInShutdownMode() {
#if EFI_MAIN_RELAY_CONTROL || defined(__DOXYGEN__)
if (stopEngineRequestTimeNt == 0) // the shutdown procedure is not started
return false;
const efitime_t engineStopWaitTimeoutNt = 5LL * 1000000LL;
// The engine is still spinning! Give it some time to stop (but wait no more than 5 secs)
if (isSpinning && (getTimeNowNt() - stopEngineRequestTimeNt) < US2NT(engineStopWaitTimeoutNt))
return true;
// todo: add checks for stepper motor parking
#endif /* EFI_MAIN_RELAY_CONTROL */
return false;
}
2017-05-15 20:28:49 -07:00
injection_mode_e Engine::getCurrentInjectionMode(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
2017-07-06 05:49:55 -07:00
return rpmCalculator.isCranking(PASS_ENGINE_PARAMETER_SIGNATURE) ? CONFIG(crankingInjectionMode) : CONFIG(injectionMode);
2016-11-30 19:06:43 -08:00
}
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) {
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);
2016-01-25 00:02:33 -08:00
2016-01-30 19:03:36 -08:00
engine->m.beforeFuelCalc = GET_TIMESTAMP();
int rpm = rpmCalculator.getRpm(PASS_ENGINE_PARAMETER_SIGNATURE);
2017-11-06 19:29:39 -08:00
ENGINE(injectionDuration) = getInjectionDuration(rpm PASS_ENGINE_PARAMETER_SUFFIX);
2016-01-30 19:03:36 -08:00
engine->m.fuelCalcTime = GET_TIMESTAMP() - engine->m.beforeFuelCalc;
2015-07-10 06:01:56 -07:00
}