fome-fw/firmware/controllers/algo/engine.cpp

405 lines
13 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
2015-12-31 13:02:30 -08:00
* @author Andrey Belomutskiy, (c) 2012-2016
2015-07-10 06:01:56 -07:00
*/
#include "main.h"
#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"
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
static LoggingWithStorage logger("engine");
2016-01-01 16:02:59 -08:00
extern fuel_Map3D_t veMap;
2016-07-01 18:01:48 -07:00
extern afr_Map3D_t afrMap;
2016-01-01 16:02:59 -08:00
2015-07-10 06:01:56 -07:00
EXTERN_ENGINE
;
2016-02-10 16:01:47 -08:00
#if ! EFI_UNIT_TEST || defined(__DOXYGEN__)
extern TunerStudioOutputChannels tsOutputChannels;
#endif
2016-01-23 15:01:40 -08:00
MockAdcState::MockAdcState() {
memset(hasMockAdc, 0, sizeof(hasMockAdc));
}
#if EFI_ENABLE_MOCK_ADC || EFI_SIMULATOR
void MockAdcState::setMockVoltage(int hwChannel, float voltage) {
scheduleMsg(&logger, "fake voltage: channel %d value %f", hwChannel, voltage);
fakeAdcValues[hwChannel] = voltsToAdc(voltage);
hasMockAdc[hwChannel] = true;
}
#endif /* EFI_ENABLE_MOCK_ADC */
int MockAdcState::getMockAdcValue(int hwChannel) {
return fakeAdcValues[hwChannel];
}
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
*/
2016-01-01 14:02:49 -08:00
void Engine::updateSlowSensors(DECLARE_ENGINE_PARAMETER_F) {
2016-01-30 19:03:36 -08:00
int rpm = rpmCalculator.rpmValue;
isEngineChartEnabled = CONFIG(isEngineChartEnabled) && rpm < CONFIG(engineSnifferRpmThreshold);
sensorChartMode = rpm < CONFIG(sensorSnifferRpmThreshold) ? boardConfiguration->sensorChartMode : SC_OFF;
2016-02-10 14:01:44 -08:00
engineState.updateSlowSensors(PASS_ENGINE_PARAMETER_F);
2015-07-10 06:01:56 -07:00
if (engineConfiguration->fuelLevelSensor != EFI_ADC_NONE) {
float fuelLevelVoltage = getVoltageDivided("fuel", engineConfiguration->fuelLevelSensor);
2015-12-26 09:03:13 -08:00
engineState.fuelTankGauge = interpolate(boardConfiguration->fuelLevelEmptyTankVoltage, 0,
2015-07-10 06:01:56 -07:00
boardConfiguration->fuelLevelFullTankVoltage, 100,
fuelLevelVoltage);
}
float vBatt = hasVBatt(PASS_ENGINE_PARAMETER_F) ? getVBatt(PASS_ENGINE_PARAMETER_F) : 12;
2016-02-06 09:02:24 -08:00
engineState.injectorLag = getInjectorLag(vBatt PASS_ENGINE_PARAMETER);
2015-07-10 06:01:56 -07:00
}
void Engine::onTriggerEvent(efitick_t nowNt) {
isSpinning = true;
lastTriggerEventTimeNt = nowNt;
}
Engine::Engine(persistent_config_s *config) {
2016-01-25 00:02:33 -08:00
init(config);
2016-02-08 21:01:43 -08:00
engineState.warmupAfrPid.init(&config->engineConfiguration.warmupAfrPid, 0.5, 1.5);
2016-01-30 19:03:36 -08:00
isEngineChartEnabled = false;
sensorChartMode = SC_OFF;
2016-08-30 18:02:38 -07:00
actualLastInjection = 0;
2016-08-30 19:02:21 -07:00
fuelScheduleForThisEngineCycle = NULL;
isAlternatorControlEnabled = false;
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);
2015-07-10 06:01:56 -07:00
lastTriggerEventTimeNt = 0;
isCylinderCleanupMode = false;
engineCycleEventCount = 0;
stopEngineRequestTimeNt = 0;
isRunningPwmTest = false;
isTestMode = false;
isSpinning = false;
adcToVoltageInputDividerCoefficient = NAN;
engineConfiguration2 = NULL;
engineState.iat = engineState.clt = NAN;
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;
2016-02-06 09:02:24 -08:00
fuelMs = 0;
2015-07-10 06:01:56 -07:00
clutchDownState = clutchUpState = false;
memset(&m, 0, sizeof(m));
}
EngineState::EngineState() {
2016-01-18 11:01:39 -08:00
dwellAngle = 0;
2016-01-01 14:02:49 -08:00
engineNoiseHipLevel = 0;
2016-02-06 09:02:24 -08:00
injectorLag = 0;
2016-07-13 16:03:06 -07:00
warningCounter = 0;
2016-07-13 19:02:35 -07:00
lastErrorCode = 0;
targetAFR = 0;
tpsAccelEnrich = 0;
tChargeK = 0;
2016-08-31 21:02:04 -07:00
runningFuel = baseFuel = currentVE = 0;
2016-07-14 20:02:55 -07:00
timeOfPreviousWarning = -10;
2016-08-31 21:02:04 -07:00
baseTableFuel = iat = iatFuelCorrection = 0;
clt = cltFuelCorrection = 0;
warmupTargetAfr = airMass = 0;
baroCorrection = timingAdvance = fuelTankGauge = 0;
sparkDwell = mapAveragingDuration = 0;
injectionOffset = 0;
2015-07-10 06:01:56 -07:00
}
2016-02-10 14:01:44 -08:00
void EngineState::updateSlowSensors(DECLARE_ENGINE_PARAMETER_F) {
iat = getIntakeAirTemperature(PASS_ENGINE_PARAMETER_F);
clt = getCoolantTemperature(PASS_ENGINE_PARAMETER_F);
warmupTargetAfr = interpolate2d(clt, engineConfiguration->warmupTargetAfrBins,
engineConfiguration->warmupTargetAfr, WARMUP_TARGET_AFR_SIZE);
}
2016-01-01 14:02:49 -08:00
void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_F) {
int rpm = ENGINE(rpmCalculator.rpmValue);
sparkDwell = getSparkDwell(rpm PASS_ENGINE_PARAMETER);
dwellAngle = sparkDwell / getOneDegreeTimeMs(rpm);
2016-08-26 15:02:39 -07:00
// todo: move this into slow callback, no reason for IAT corr to be here
2016-01-01 14:02:49 -08:00
iatFuelCorrection = getIatCorrection(iat PASS_ENGINE_PARAMETER);
2016-08-26 15:02:39 -07:00
// todo: move this into slow callback, no reason for CLT corr to be here
2016-02-15 15:02:03 -08:00
if (boardConfiguration->useWarmupPidAfr && clt < engineConfiguration->warmupAfrThreshold) {
2016-02-07 13:01:55 -08:00
if (rpm < 200) {
cltFuelCorrection = 1;
2016-02-06 14:01:38 -08:00
warmupAfrPid.reset();
2016-02-07 13:01:55 -08:00
} else {
2016-02-15 15:02:03 -08:00
cltFuelCorrection = warmupAfrPid.getValue(warmupTargetAfr, getAfr(PASS_ENGINE_PARAMETER_F), 1);
2016-02-07 13:01:55 -08:00
}
2016-02-10 16:01:47 -08:00
#if ! EFI_UNIT_TEST || defined(__DOXYGEN__)
if (engineConfiguration->debugMode == WARMUP_ENRICH) {
2016-02-15 15:02:03 -08:00
tsOutputChannels.debugFloatField1 = warmupTargetAfr;
2016-02-10 16:01:47 -08:00
warmupAfrPid.postState(&tsOutputChannels);
}
#endif
2016-02-06 09:02:24 -08:00
} else {
cltFuelCorrection = getCltCorrection(clt PASS_ENGINE_PARAMETER);
}
2016-01-01 14:02:49 -08:00
2016-01-01 16:02:59 -08:00
engineNoiseHipLevel = interpolate2d(rpm, engineConfiguration->knockNoiseRpmBins,
engineConfiguration->knockNoise, ENGINE_NOISE_CURVE_SIZE);
baroCorrection = getBaroCorrection(PASS_ENGINE_PARAMETER_F);
injectionOffset = getinjectionOffset(rpm PASS_ENGINE_PARAMETER);
float engineLoad = getEngineLoadT(PASS_ENGINE_PARAMETER_F);
timingAdvance = getAdvance(rpm, engineLoad PASS_ENGINE_PARAMETER);
2016-08-28 13:02:34 -07:00
if (engineConfiguration->fuelAlgorithm == LM_SPEED_DENSITY) {
2016-01-01 16:02:59 -08:00
float coolantC = ENGINE(engineState.clt);
float intakeC = ENGINE(engineState.iat);
float tps = getTPS(PASS_ENGINE_PARAMETER_F);
2016-10-11 18:03:00 -07:00
tChargeK = convertCelsiusToKelvin(getTCharge(rpm, tps, coolantC, intakeC PASS_ENGINE_PARAMETER));
2016-01-01 16:02:59 -08:00
float map = getMap();
/**
* *0.01 because of https://sourceforge.net/p/rusefi/tickets/153/
*/
2016-03-11 12:01:58 -08:00
currentVE = baroCorrection * veMap.getValue(rpm, map) * 0.01;
2016-06-09 08:02:31 -07:00
targetAFR = afrMap.getValue(rpm, map);
2016-01-01 16:02:59 -08:00
} else {
baseTableFuel = getBaseTableFuel(engineConfiguration, rpm, engineLoad);
}
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() {
sparkTable.preCalc(engineConfiguration->sparkDwellBins,
engineConfiguration->sparkDwell);
/**
* 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;
float maf = interpolate2d(volts, config->mafDecodingBins,
config->mafDecoding, MAF_DECODING_COUNT);
mafDecodingLookup[i] = maf;
}
}
2016-01-24 23:03:01 -08:00
void Engine::init(persistent_config_s *config) {
2016-01-25 00:02:33 -08:00
this->config = config;
engineConfiguration = &config->engineConfiguration;
memset(config, 0, sizeof(persistent_config_s));
2015-07-10 06:01:56 -07:00
}
void Engine::printKnockState(void) {
scheduleMsg(&logger, "knock now=%s/ever=%s", boolToString(knockNow), boolToString(knockEver));
}
void Engine::knockLogic(float knockVolts) {
2015-07-11 13:01:31 -07:00
this->knockVolts = knockVolts;
2015-07-10 06:01:56 -07:00
knockNow = knockVolts > engineConfiguration->knockVThreshold;
/**
* 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)
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();
/**
* Lowest possible cranking is about 240 RPM, that's 4 revolutions per second.
* 0.25 second is 250000 uS
*
* 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
*/
efitick_t timeSinceLastTriggerEvent = nowNt - lastTriggerEventTimeNt;
if (timeSinceLastTriggerEvent < US2NT(250000LL)) {
return;
}
isSpinning = false;
#if EFI_PROD_CODE || EFI_SIMULATOR
scheduleMsg(&logger, "engine has STOPPED");
scheduleMsg(&logger, "templog engine has STOPPED [%x][%x] [%x][%x] %d",
(int)(nowNt >> 32), (int)nowNt,
(int)(lastTriggerEventTimeNt >> 32), (int)lastTriggerEventTimeNt,
(int)timeSinceLastTriggerEvent
);
triggerInfo();
#endif
2016-11-03 20:02:58 -07:00
enginePins.stopPins();
2015-07-10 06:01:56 -07:00
#endif
}
2016-01-25 09:01:30 -08:00
void Engine::prepareFuelSchedule(DECLARE_ENGINE_PARAMETER_F) {
int rpm = rpmCalculator.rpmValue;
2016-06-26 17:03:27 -07:00
efiAssertVoid(ENGINE(engineConfiguration2)->injectionEvents != ENGINE(engineConfiguration2)->processing, "fuel pointers");
2016-01-25 09:01:30 -08:00
ENGINE(m.beforeInjectonSch) = GET_TIMESTAMP();
injection_mode_e mode = isCrankingR(rpm) ? CONFIG(crankingInjectionMode) : CONFIG(injectionMode);
2016-08-05 22:04:28 -07:00
if (ENGINE(engineConfiguration2)->processing->usedAtEngineCycle != 0 &&
ENGINE(engineConfiguration2)->processing->usedAtEngineCycle == ENGINE(rpmCalculator).getRevolutionCounter()) {
// we are here if engine is still using this older fuel schedule, not yet time to override it
// scheduleMsg(&logger, "still need %d", ENGINE(rpmCalculator).getRevolutionCounter());
return;
}
2016-01-25 09:01:30 -08:00
ENGINE(engineConfiguration2)->processing->addFuelEvents(
mode PASS_ENGINE_PARAMETER);
ENGINE(m.injectonSchTime) = GET_TIMESTAMP() - ENGINE(m.beforeInjectonSch);
/**
* Swap pointers. This way we are always reading from one instance while adjusting scheduling of another instance.
*/
FuelSchedule * t = ENGINE(engineConfiguration2)->injectionEvents;
ENGINE(engineConfiguration2)->injectionEvents = ENGINE(engineConfiguration2)->processing;
ENGINE(engineConfiguration2)->processing = t;
}
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
*/
void Engine::periodicFastCallback(DECLARE_ENGINE_PARAMETER_F) {
int rpm = rpmCalculator.rpmValue;
2016-01-01 14:02:49 -08:00
if (isValidRpm(rpm)) {
MAP_sensor_config_s * c = &engineConfiguration->map;
angle_t start = interpolate2d(rpm, c->samplingAngleBins, c->samplingAngle, MAP_ANGLE_SIZE);
angle_t offsetAngle = TRIGGER_SHAPE(eventAngles[CONFIG(mapAveragingSchedulingAtIndex)]);
for (int i = 0; i < engineConfiguration->specs.cylindersCount; i++) {
angle_t cylinderOffset = getEngineCycle(engineConfiguration->operationMode) * i / engineConfiguration->specs.cylindersCount;
float cylinderStart = start + cylinderOffset - offsetAngle + tdcPosition();
fixAngle(cylinderStart);
engine->engineState.mapAveragingStart[i] = cylinderStart;
}
engine->engineState.mapAveragingDuration = interpolate2d(rpm, c->samplingWindowBins, c->samplingWindow, MAP_WINDOW_SIZE);
} else {
for (int i = 0; i < engineConfiguration->specs.cylindersCount; i++) {
engine->engineState.mapAveragingStart[i] = NAN;
}
engine->engineState.mapAveragingDuration = NAN;
}
engineState.periodicFastCallback(PASS_ENGINE_PARAMETER_F);
2016-01-25 00:02:33 -08:00
2016-01-30 19:03:36 -08:00
engine->m.beforeFuelCalc = GET_TIMESTAMP();
2016-08-26 15:02:39 -07:00
ENGINE(fuelMs) = getInjectionDuration(rpm PASS_ENGINE_PARAMETER) * engineConfiguration->globalFuelCorrection;
2016-01-30 19:03:36 -08:00
engine->m.fuelCalcTime = GET_TIMESTAMP() - engine->m.beforeFuelCalc;
prepareFuelSchedule(PASS_ENGINE_PARAMETER_F);
2015-07-10 06:01:56 -07:00
}
StartupFuelPumping::StartupFuelPumping() {
isTpsAbove50 = false;
pumpsCounter = 0;
}
void StartupFuelPumping::setPumpsCounter(
engine_configuration_s *engineConfiguration, int newValue) {
if (pumpsCounter != newValue) {
pumpsCounter = newValue;
if (pumpsCounter == PUMPS_TO_PRIME) {
scheduleMsg(&logger, "let's squirt prime pulse %f", pumpsCounter);
pumpsCounter = 0;
} else {
scheduleMsg(&logger, "setPumpsCounter %d", pumpsCounter);
}
}
}
void StartupFuelPumping::update(DECLARE_ENGINE_PARAMETER_F) {
2016-01-18 09:03:32 -08:00
if (engine->rpmCalculator.getRpm(PASS_ENGINE_PARAMETER_F) == 0) {
2015-07-10 06:01:56 -07:00
bool isTpsAbove50 = getTPS(PASS_ENGINE_PARAMETER_F) >= 50;
if (this->isTpsAbove50 != isTpsAbove50) {
setPumpsCounter(engineConfiguration, pumpsCounter + 1);
}
} else {
/**
* Engine is not stopped - not priming pumping mode
*/
setPumpsCounter(engineConfiguration, 0);
isTpsAbove50 = false;
}
}