rusefi-1/firmware/controllers/actuators/idle_thread.cpp

635 lines
21 KiB
C++
Raw Normal View History

2015-07-10 06:01:56 -07:00
/**
* @file idle_thread.cpp
* @brief Idle Air Control valve thread.
*
* This thread looks at current RPM and decides if it should increase or decrease IAC duty cycle.
* This file has the hardware & scheduling logic, desired idle level lives separately.
2015-07-10 06:01:56 -07:00
*
*
* @date May 23, 2013
2020-01-13 18:57:43 -08:00
* @author Andrey Belomutskiy, (c) 2012-2020
2015-07-10 06:01:56 -07:00
*
2019-09-29 12:14:08 -07:00
* enable verbose_idle
2019-09-13 18:07:34 -07:00
* disable verbose_idle
*
2015-07-10 06:01:56 -07:00
* This file is part of rusEfi - see http://rusefi.com
*
* rusEfi is free software; you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
2015-11-09 16:03:32 -08:00
*
2015-07-10 06:01:56 -07:00
*/
#include "pch.h"
2019-04-12 19:07:03 -07:00
#if EFI_IDLE_CONTROL
2015-07-10 06:01:56 -07:00
#include "idle_thread.h"
#include "idle_hardware.h"
2019-08-25 21:19:13 -07:00
2019-08-28 21:10:47 -07:00
#include "periodic_task.h"
2020-05-08 18:39:35 -07:00
#include "dc_motors.h"
2019-08-25 21:19:13 -07:00
#if EFI_TUNER_STUDIO
#include "stepper.h"
#endif
2015-07-10 06:01:56 -07:00
2020-07-22 08:39:09 -07:00
static efitimeus_t restoreAfterPidResetTimeUs = 0;
static PidIndustrial industrialWithOverrideIdlePid;
2017-05-16 19:13:40 -07:00
#if EFI_IDLE_PID_CIC
// Use PID with CIC integrator
static PidCic idleCicPid;
#endif //EFI_IDLE_PID_CIC
Pid * getIdlePid(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
#if EFI_IDLE_PID_CIC
if (CONFIG(useCicPidForIdle)) {
return &idleCicPid;
}
#endif /* EFI_IDLE_PID_CIC */
return &industrialWithOverrideIdlePid;
}
static iacPidMultiplier_t iacPidMultMap;
2019-08-25 21:19:13 -07:00
#if ! EFI_UNIT_TEST
2015-07-10 06:01:56 -07:00
void idleDebug(const char *msg, percent_t value) {
efiPrintf("idle debug: %s%.2f", msg, value);
2015-07-10 06:01:56 -07:00
}
static void showIdleInfo(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
2017-01-28 22:04:16 -08:00
const char * idleModeStr = getIdle_mode_e(engineConfiguration->idleMode);
efiPrintf("useStepperIdle=%s useHbridges=%s",
2021-09-26 15:07:09 -07:00
boolToString(CONFIG(useStepperIdle)), boolToString(CONFIG(useHbridgesToDriveIdleStepper)));
efiPrintf("idleMode=%s position=%.2f",
idleModeStr, getIdlePosition());
2017-05-16 17:52:52 -07:00
if (CONFIG(useStepperIdle)) {
2021-09-26 15:07:09 -07:00
if (CONFIG(useHbridgesToDriveIdleStepper)) {
efiPrintf("Coil A:");
efiPrintf(" pin1=%s", hwPortname(CONFIG(stepperDcIo[0].directionPin1)));
efiPrintf(" pin2=%s", hwPortname(CONFIG(stepperDcIo[0].directionPin2)));
showDcMotorInfo(2);
efiPrintf("Coil B:");
efiPrintf(" pin1=%s", hwPortname(CONFIG(stepperDcIo[1].directionPin1)));
efiPrintf(" pin2=%s", hwPortname(CONFIG(stepperDcIo[1].directionPin2)));
showDcMotorInfo(3);
} else {
efiPrintf("directionPin=%s reactionTime=%.2f", hwPortname(CONFIG(idle).stepperDirectionPin),
engineConfiguration->idleStepperReactionTime);
efiPrintf("stepPin=%s steps=%d", hwPortname(CONFIG(idle).stepperStepPin),
engineConfiguration->idleStepperTotalSteps);
efiPrintf("enablePin=%s/%d", hwPortname(engineConfiguration->stepperEnablePin),
engineConfiguration->stepperEnablePinMode);
}
2015-07-10 06:01:56 -07:00
} else {
if (!CONFIG(isDoubleSolenoidIdle)) {
efiPrintf("idle valve freq=%d on %s", CONFIG(idle).solenoidFrequency,
hwPortname(CONFIG(idle).solenoidPin));
} else {
efiPrintf("idle valve freq=%d on %s", CONFIG(idle).solenoidFrequency,
hwPortname(CONFIG(idle).solenoidPin));
efiPrintf(" and %s", hwPortname(CONFIG(secondSolenoidPin)));
}
2015-07-10 06:01:56 -07:00
}
2017-05-16 17:52:52 -07:00
if (engineConfiguration->idleMode == IM_AUTO) {
getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->showPidStatus("idle");
2017-01-28 22:04:16 -08:00
}
2015-07-10 06:01:56 -07:00
}
void setIdleMode(idle_mode_e value DECLARE_ENGINE_PARAMETER_SUFFIX) {
2015-07-10 06:01:56 -07:00
engineConfiguration->idleMode = value ? IM_AUTO : IM_MANUAL;
showIdleInfo();
}
percent_t getIdlePosition() {
2021-10-16 20:55:29 -07:00
return engine->idle.currentIdlePosition;
2019-08-25 21:19:13 -07:00
}
void setManualIdleValvePosition(int positionPercent) {
2019-08-28 21:10:47 -07:00
if (positionPercent < 1 || positionPercent > 99)
return;
efiPrintf("setting idle valve position %d", positionPercent);
2019-08-28 21:10:47 -07:00
#if ! EFI_UNIT_TEST
showIdleInfo();
#endif /* EFI_UNIT_TEST */
// todo: this is not great that we have to write into configuration here
CONFIG(manIdlePosition) = positionPercent;
2019-08-28 21:10:47 -07:00
}
2019-08-25 21:19:13 -07:00
#endif /* EFI_UNIT_TEST */
void IdleController::init(pid_s* idlePidConfig) {
engine->idle.shouldResetPid = false;
engine->idle.mightResetPid = false;
engine->idle.wasResetPid = false;
m_timingPid.initPidClass(idlePidConfig);
}
2020-12-17 14:46:51 -08:00
int IdleController::getTargetRpm(float clt) const {
auto target = interpolate2d(clt, CONFIG(cltIdleRpmBins), CONFIG(cltIdleRpm));
2020-12-17 14:46:51 -08:00
// Bump for AC
target += engine->acSwitchState ? CONFIG(acIdleRpmBump) : 0;
2020-12-17 14:46:51 -08:00
return target;
2020-12-17 14:46:51 -08:00
}
IIdleController::Phase IdleController::determinePhase(int rpm, int targetRpm, SensorResult tps, float vss, float crankingTaperFraction) const {
if (!engine->rpmCalculator.isRunning()) {
return Phase::Cranking;
}
if (!tps) {
// If the TPS has failed, assume the engine is running
return Phase::Running;
}
// if throttle pressed, we're out of the idle corner
if (tps.Value > CONFIG(idlePidDeactivationTpsThreshold)) {
return Phase::Running;
}
// If rpm too high (but throttle not pressed), we're coasting
int maximumIdleRpm = targetRpm + CONFIG(idlePidRpmUpperLimit);
if (rpm > maximumIdleRpm) {
return Phase::Coasting;
}
// If the vehicle is moving too quickly, disable CL idle
auto maxVss = CONFIG(maxIdleVss);
if (maxVss != 0 && vss > maxVss) {
return Phase::Running;
}
// If still in the cranking taper, disable closed loop idle
if (crankingTaperFraction < 1) {
return Phase::CrankToIdleTaper;
}
// No other conditions met, we are idling!
return Phase::Idling;
}
float IdleController::getCrankingTaperFraction() const {
return (float)engine->rpmCalculator.getRevolutionCounterSinceStart() / CONFIG(afterCrankingIACtaperDuration);
}
float IdleController::getCrankingOpenLoop(float clt) const {
float mult =
CONFIG(overrideCrankingIacSetting)
// Override to separate table
? interpolate2d(clt, config->cltCrankingCorrBins, config->cltCrankingCorr)
// Otherwise use plain running table
: interpolate2d(clt, config->cltIdleCorrBins, config->cltIdleCorr);
return CONFIG(crankingIACposition) * mult;
}
float IdleController::getRunningOpenLoop(float clt, SensorResult tps) const {
float running =
CONFIG(manIdlePosition) // Base idle position (slider)
* interpolate2d(clt, config->cltIdleCorrBins, config->cltIdleCorr);
// Now we bump it by the AC/fan amount if necessary
running += engine->acSwitchState ? CONFIG(acIdleExtraOffset) : 0;
running += enginePins.fanRelay.getLogicValue() ? CONFIG(fan1ExtraIdle) : 0;
running += enginePins.fanRelay2.getLogicValue() ? CONFIG(fan2ExtraIdle) : 0;
// Now bump it by the specified amount when the throttle is opened (if configured)
// nb: invalid tps will make no change, no explicit check required
running += interpolateClamped(
0, 0,
CONFIG(idlePidDeactivationTpsThreshold), CONFIG(iacByTpsTaper),
tps.value_or(0));
return clampF(0, running, 100);
}
float IdleController::getOpenLoop(Phase phase, float clt, SensorResult tps, float crankingTaperFraction) const {
float cranking = getCrankingOpenLoop(clt);
// if we're cranking, nothing more to do.
if (phase == Phase::Cranking) {
return cranking;
}
2021-05-31 14:43:58 -07:00
// If coasting (and enabled), use the coasting position table instead of normal open loop
// TODO: this should be a table of open loop mult vs. RPM, not vs. clt
if (CONFIG(useIacTableForCoasting) && phase == Phase::Coasting) {
return interpolate2d(clt, CONFIG(iacCoastingBins), CONFIG(iacCoasting));
}
float running = getRunningOpenLoop(clt, tps);
// Interpolate between cranking and running over a short time
// This clamps once you fall off the end, so no explicit check for >1 required
return interpolateClamped(0, cranking, 1, running, crankingTaperFraction);
}
float IdleController::getIdleTimingAdjustment(int rpm) {
return getIdleTimingAdjustment(rpm, m_lastTargetRpm, m_lastPhase);
}
float IdleController::getIdleTimingAdjustment(int rpm, int targetRpm, Phase phase) {
// if not enabled, do nothing
if (!CONFIG(useIdleTimingPidControl)) {
return 0;
}
// If not idling, do nothing
if (phase != Phase::Idling) {
m_timingPid.reset();
return 0;
}
if (CONFIG(useInstantRpmForIdle)) {
rpm = engine->triggerCentral.triggerState.getInstantRpm();
}
// If inside the deadzone, do nothing
if (absI(rpm - targetRpm) < CONFIG(idleTimingPidDeadZone)) {
m_timingPid.reset();
return 0;
}
// We're now in the idle mode, and RPM is inside the Timing-PID regulator work zone!
return m_timingPid.getOutput(targetRpm, rpm, FAST_CALLBACK_PERIOD_MS / 1000.0f);
}
2015-09-01 20:02:49 -07:00
/**
* I use this questionable feature to tune acceleration enrichment
*/
static void blipIdle(int idlePosition, int durationMs) {
2021-10-16 21:27:12 -07:00
#if ! EFI_UNIT_TEST
if (engine->timeToStopBlip != 0) {
2015-09-01 20:02:49 -07:00
return; // already in idle blip
}
2021-10-16 21:27:12 -07:00
engine->blipIdlePosition = idlePosition;
engine->timeToStopBlip = getTimeNowUs() + 1000 * durationMs;
#endif // EFI_UNIT_TEST
2015-09-01 20:02:49 -07:00
}
2021-10-16 21:27:12 -07:00
static void finishIdleTestIfNeeded(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
if (engine->timeToStopIdleTest != 0 && getTimeNowUs() > engine->timeToStopIdleTest)
engine->timeToStopIdleTest = 0;
2015-12-24 19:01:26 -08:00
}
2021-10-16 21:27:12 -07:00
static void undoIdleBlipIfNeeded(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
if (engine->timeToStopBlip != 0 && getTimeNowUs() > engine->timeToStopBlip) {
engine->timeToStopBlip = 0;
2015-09-01 20:02:49 -07:00
}
}
2018-07-29 09:23:55 -07:00
/**
* @return idle valve position percentage for automatic closed loop mode
*/
float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos, int rpm, int targetRpm) {
2021-05-31 03:01:57 -07:00
auto idlePid = getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE);
2021-10-16 21:27:12 -07:00
if (engine->idle.shouldResetPid) {
2020-12-29 04:49:10 -08:00
// we reset only if I-term is negative, because the positive I-term is good - it keeps RPM from dropping too low
2021-10-16 21:27:12 -07:00
if (idlePid->getIntegration() <= 0 || engine->idle.mustResetPid) {
2021-05-31 03:01:57 -07:00
idlePid->reset();
2021-10-16 21:27:12 -07:00
engine->idle.mustResetPid = false;
2020-12-29 04:49:10 -08:00
}
// alternatorPidResetCounter++;
2021-10-16 21:27:12 -07:00
engine->idle.shouldResetPid = false;
engine->idle.wasResetPid = true;
2020-12-29 04:49:10 -08:00
}
// todo: move this to pid_s one day
industrialWithOverrideIdlePid.antiwindupFreq = engineConfiguration->idle_antiwindupFreq;
industrialWithOverrideIdlePid.derivativeFilterLoss = engineConfiguration->idle_derivativeFilterLoss;
2020-07-22 08:39:09 -07:00
efitimeus_t nowUs = getTimeNowUs();
2019-09-10 20:03:55 -07:00
2020-12-29 04:49:10 -08:00
if (phase != IIdleController::Phase::Idling) {
2020-07-22 08:39:09 -07:00
// Don't store old I and D terms if PID doesn't work anymore.
// Otherwise they will affect the idle position much later, when the throttle is closed.
2021-10-16 21:27:12 -07:00
if (engine->idle.mightResetPid) {
engine->idle.mightResetPid = false;
engine->idle.shouldResetPid = true;
2020-07-22 08:39:09 -07:00
}
2021-10-16 20:55:29 -07:00
engine->idle.idleState = TPS_THRESHOLD;
// We aren't idling, so don't apply any correction. A positive correction could inhibit a return to idle.
m_lastAutomaticPosition = 0;
return 0;
2020-07-22 08:39:09 -07:00
}
// #1553 we need to give FSIO variable offset or minValue a chance
2020-07-22 08:39:09 -07:00
bool acToggleJustTouched = (nowUs - engine->acSwitchLastChangeTime) < MS2US(500);
2019-09-11 17:46:50 -07:00
// check if within the dead zone
if (!acToggleJustTouched && absI(rpm - targetRpm) <= CONFIG(idlePidRpmDeadZone)) {
2021-10-16 20:55:29 -07:00
engine->idle.idleState = RPM_DEAD_ZONE;
2018-07-29 09:23:55 -07:00
// current RPM is close enough, no need to change anything
return m_lastAutomaticPosition;
2018-07-29 09:23:55 -07:00
}
// When rpm < targetRpm, there's a risk of dropping RPM too low - and the engine dies out.
// So PID reaction should be increased by adding extra percent to PID-error:
percent_t errorAmpCoef = 1.0f;
if (rpm < targetRpm) {
errorAmpCoef += (float)CONFIG(pidExtraForLowRpm) / PERCENT_MULT;
}
2020-07-22 08:39:09 -07:00
// if PID was previously reset, we store the time when it turned on back (see errorAmpCoef correction below)
2021-10-16 21:27:12 -07:00
if (engine->idle.wasResetPid) {
2020-07-22 08:39:09 -07:00
restoreAfterPidResetTimeUs = nowUs;
2021-10-16 21:27:12 -07:00
engine->idle.wasResetPid = false;
2020-07-22 08:39:09 -07:00
}
// increase the errorAmpCoef slowly to restore the process correctly after the PID reset
2021-10-16 20:55:29 -07:00
// todo: move restoreAfterPidResetTimeUs to idle?
efitimeus_t timeSincePidResetUs = nowUs - /*engine->idle.*/restoreAfterPidResetTimeUs;
2020-07-22 08:39:09 -07:00
// todo: add 'pidAfterResetDampingPeriodMs' setting
2021-05-31 03:01:57 -07:00
errorAmpCoef = interpolateClamped(0, 0, MS2US(/*CONFIG(pidAfterResetDampingPeriodMs)*/1000), errorAmpCoef, timeSincePidResetUs);
// If errorAmpCoef > 1.0, then PID thinks that RPM is lower than it is, and controls IAC more aggressively
2021-05-31 03:01:57 -07:00
idlePid->setErrorAmplification(errorAmpCoef);
2021-05-31 03:01:57 -07:00
percent_t newValue = idlePid->getOutput(targetRpm, rpm, SLOW_CALLBACK_PERIOD_MS / 1000.0f);
2021-10-16 20:55:29 -07:00
engine->idle.idleState = PID_VALUE;
2019-03-21 20:50:50 -07:00
// the state of PID has been changed, so we might reset it now, but only when needed (see idlePidDeactivationTpsThreshold)
2021-10-16 21:27:12 -07:00
engine->idle.mightResetPid = true;
2016-06-26 18:02:40 -07:00
// Apply PID Multiplier if used
if (CONFIG(useIacPidMultTable)) {
float engineLoad = getFuelingLoad(PASS_ENGINE_PARAMETER_SIGNATURE);
float multCoef = iacPidMultMap.getValue(rpm / RPM_1_BYTE_PACKING_MULT, engineLoad);
// PID can be completely disabled of multCoef==0, or it just works as usual if multCoef==1
newValue = interpolateClamped(0, 0, 1, newValue, multCoef);
}
// Apply PID Deactivation Threshold as a smooth taper for TPS transients.
// if tps==0 then PID just works as usual, or we completely disable it if tps>=threshold
// TODO: should we just remove this? It reduces the gain if your zero throttle stop isn't perfect,
// which could give unstable results.
newValue = interpolateClamped(0, newValue, CONFIG(idlePidDeactivationTpsThreshold), 0, tpsPos);
m_lastAutomaticPosition = newValue;
2017-05-16 19:13:40 -07:00
return newValue;
2016-06-26 18:02:40 -07:00
}
float IdleController::getIdlePosition() {
// Simplify hardware CI: we borrow the idle valve controller as a PWM source for various stimulation tasks
// The logic in this function is solidly unit tested, so it's not necessary to re-test the particulars on real hardware.
#ifdef HARDWARE_CI
return CONFIG(manIdlePosition);
#endif
2017-02-11 23:02:24 -08:00
/*
* Here we have idle logic thread - actual stepper movement is implemented in a separate
* working thread,
* @see stepper.cpp
*/
getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->iTermMin = engineConfiguration->idlerpmpid_iTermMin;
getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->iTermMax = engineConfiguration->idlerpmpid_iTermMax;
2020-12-26 16:44:40 -08:00
// On failed sensor, use 0 deg C - should give a safe highish idle
2021-10-05 16:59:07 -07:00
float clt = Sensor::getOrZero(SensorType::Clt);
2020-12-26 16:44:40 -08:00
auto tps = Sensor::get(SensorType::DriverThrottleIntent);
float rpm;
if (CONFIG(useInstantRpmForIdle)) {
rpm = engine->triggerCentral.triggerState.getInstantRpm();
2020-12-26 16:44:40 -08:00
} else {
rpm = GET_RPM();
}
// Compute the target we're shooting for
auto targetRpm = getTargetRpm(clt);
m_lastTargetRpm = targetRpm;
// Determine cranking taper
float crankingTaper = getCrankingTaperFraction();
2020-12-29 04:49:10 -08:00
// Determine what operation phase we're in - idling or not
2021-10-05 16:59:07 -07:00
float vehicleSpeed = Sensor::getOrZero(SensorType::VehicleSpeed);
auto phase = determinePhase(rpm, targetRpm, tps, vehicleSpeed, crankingTaper);
m_lastPhase = phase;
2020-12-29 04:49:10 -08:00
2021-10-16 20:55:29 -07:00
bool isAutomaticIdle = tps.Valid && engineConfiguration->idleMode == IM_AUTO;
2021-10-16 20:55:29 -07:00
if (engineConfiguration->isVerboseIAC && isAutomaticIdle) {
efiPrintf("Idle state %s", getIdle_state_e(engine->idle.idleState));
getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->showPidStatus("idle");
2018-07-29 09:23:55 -07:00
}
2021-10-16 21:27:12 -07:00
finishIdleTestIfNeeded(PASS_ENGINE_PARAMETER_SIGNATURE);
undoIdleBlipIfNeeded(PASS_ENGINE_PARAMETER_SIGNATURE);
2015-09-01 20:02:49 -07:00
2018-07-29 09:23:55 -07:00
percent_t iacPosition;
2017-05-16 19:13:40 -07:00
2021-10-16 21:27:12 -07:00
if (engine->timeToStopBlip != 0) {
iacPosition = engine->blipIdlePosition;
2021-10-16 20:55:29 -07:00
engine->idle.idleState = BLIP;
2016-06-26 18:02:40 -07:00
} else {
// Always apply closed loop correction
iacPosition = getOpenLoop(phase, clt, tps, crankingTaper);
2021-10-16 20:55:29 -07:00
engine->idle.baseIdlePosition = iacPosition;
// If TPS is working and automatic mode enabled, add any automatic correction
if (tps.Valid && engineConfiguration->idleMode == IM_AUTO) {
iacPosition += getClosedLoop(phase, tps.Value, rpm, targetRpm);
}
2017-11-03 15:04:24 -07:00
iacPosition = clampPercentValue(iacPosition);
2017-05-16 19:13:40 -07:00
}
#if EFI_TUNER_STUDIO
tsOutputChannels.isIdleClosedLoop = phase == Phase::Idling;
tsOutputChannels.isIdleCoasting = phase == Phase::Coasting;
2015-07-10 06:01:56 -07:00
2017-05-22 12:30:39 -07:00
if (engineConfiguration->debugMode == DBG_IDLE_CONTROL) {
if (engineConfiguration->idleMode == IM_AUTO) {
2017-07-23 07:29:10 -07:00
// see also tsOutputChannels->idlePosition
getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->postState(&tsOutputChannels, 1000000);
2021-10-16 20:55:29 -07:00
tsOutputChannels.debugIntField4 = engine->idle.idleState;
2017-07-23 07:29:10 -07:00
} else {
tsOutputChannels.debugFloatField1 = iacPosition;
extern StepperMotor iacMotor;
tsOutputChannels.debugIntField1 = iacMotor.getTargetPosition();
2017-05-22 12:30:39 -07:00
}
}
#endif /* EFI_TUNER_STUDIO */
2017-05-22 12:30:39 -07:00
2021-10-16 20:55:29 -07:00
engine->idle.currentIdlePosition = iacPosition;
return iacPosition;
}
void IdleController::update() {
float position = getIdlePosition();
applyIACposition(position PASS_ENGINE_PARAMETER_SUFFIX);
}
2019-09-19 19:56:54 -07:00
IdleController idleControllerInstance;
2019-08-28 21:10:47 -07:00
void updateIdleControl()
{
idleControllerInstance.update();
}
float getIdleTimingAdjustment(int rpm) {
return idleControllerInstance.getIdleTimingAdjustment(rpm);
}
bool isIdlingOrTaper() {
return idleControllerInstance.isIdlingOrTaper();
}
2019-08-28 21:10:47 -07:00
static void applyPidSettings(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->updateFactors(engineConfiguration->idleRpmPid.pFactor, engineConfiguration->idleRpmPid.iFactor, engineConfiguration->idleRpmPid.dFactor);
iacPidMultMap.init(CONFIG(iacPidMultTable), CONFIG(iacPidMultLoadBins), CONFIG(iacPidMultRpmBins));
2019-08-28 21:10:47 -07:00
}
2019-08-29 20:50:20 -07:00
void setDefaultIdleParameters(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
engineConfiguration->idleRpmPid.pFactor = 0.1f;
engineConfiguration->idleRpmPid.iFactor = 0.05f;
engineConfiguration->idleRpmPid.dFactor = 0.0f;
engineConfiguration->idlerpmpid_iTermMin = -20;
engineConfiguration->idlerpmpid_iTermMax = 20;
// Good starting point is 10 degrees per 100 rpm, aka 0.1 deg/rpm
CONFIG(idleTimingPid).pFactor = 0.1f;
CONFIG(idleTimingPid).iFactor = 0;
CONFIG(idleTimingPid).dFactor = 0;
// Allow +- 10 degrees adjustment
CONFIG(idleTimingPid).minValue = -10;
CONFIG(idleTimingPid).minValue = 10;
// Idle region is target + 100 RPM
CONFIG(idlePidRpmUpperLimit) = 100;
2019-08-29 20:50:20 -07:00
}
2019-08-28 21:10:47 -07:00
#if ! EFI_UNIT_TEST
2015-07-10 06:01:56 -07:00
2019-08-25 21:19:13 -07:00
void onConfigurationChangeIdleCallback(engine_configuration_s *previousConfiguration) {
2021-10-16 21:27:12 -07:00
engine->idle.shouldResetPid = !getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->isSame(&previousConfiguration->idleRpmPid);
engine->idle.mustResetPid = engine->idle.shouldResetPid;
2019-08-25 21:19:13 -07:00
}
2017-01-06 14:01:28 -08:00
void setTargetIdleRpm(int value) {
2017-12-24 19:05:16 -08:00
setTargetRpmCurve(value PASS_ENGINE_PARAMETER_SUFFIX);
efiPrintf("target idle RPM %d", value);
2017-05-22 20:25:34 -07:00
showIdleInfo();
2015-07-10 06:01:56 -07:00
}
2015-11-09 16:03:32 -08:00
2017-05-22 20:25:34 -07:00
void setIdleOffset(float value) {
engineConfiguration->idleRpmPid.offset = value;
showIdleInfo();
2015-10-19 19:02:51 -07:00
}
2017-01-06 13:03:41 -08:00
void setIdlePFactor(float value) {
2016-02-05 13:01:55 -08:00
engineConfiguration->idleRpmPid.pFactor = value;
2019-08-28 21:10:47 -07:00
applyPidSettings();
2015-10-19 19:02:51 -07:00
showIdleInfo();
}
2017-01-06 13:03:41 -08:00
void setIdleIFactor(float value) {
2016-02-05 13:01:55 -08:00
engineConfiguration->idleRpmPid.iFactor = value;
2019-08-28 21:10:47 -07:00
applyPidSettings();
2015-10-19 19:02:51 -07:00
showIdleInfo();
}
2017-01-06 13:03:41 -08:00
void setIdleDFactor(float value) {
2016-02-05 13:01:55 -08:00
engineConfiguration->idleRpmPid.dFactor = value;
2019-08-28 21:10:47 -07:00
applyPidSettings();
2015-10-19 19:02:51 -07:00
showIdleInfo();
}
2019-01-13 16:41:39 -08:00
/**
* Idle test would activate the solenoid for three seconds
*/
2016-12-26 18:04:16 -08:00
void startIdleBench(void) {
2021-10-16 21:27:12 -07:00
engine->timeToStopIdleTest = getTimeNowUs() + MS2US(3000); // 3 seconds
efiPrintf("idle valve bench test");
2015-12-26 13:01:56 -08:00
showIdleInfo();
2015-12-24 19:01:26 -08:00
}
2019-08-28 21:10:47 -07:00
#endif /* EFI_UNIT_TEST */
void startIdleThread(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
idleControllerInstance.inject(PASS_ENGINE_PARAMETER_SIGNATURE);
idleControllerInstance.init(&CONFIG(idleTimingPid));
industrialWithOverrideIdlePid.inject(PASS_ENGINE_PARAMETER_SIGNATURE);
2015-11-09 16:03:32 -08:00
2020-12-17 14:46:51 -08:00
ENGINE(idleController) = &idleControllerInstance;
getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->initPidClass(&engineConfiguration->idleRpmPid);
2019-08-28 21:10:47 -07:00
#if ! EFI_UNIT_TEST
// todo: we still have to explicitly init all hardware on start in addition to handling configuration change via
// 'applyNewHardwareSettings' todo: maybe unify these two use-cases?
initIdleHardware(PASS_ENGINE_PARAMETER_SIGNATURE);
#endif /* EFI_UNIT_TEST */
2015-07-10 06:01:56 -07:00
2021-10-16 20:55:29 -07:00
engine->idle.idleState = INIT;
engine->idle.baseIdlePosition = -100.0f;
engine->idle.currentIdlePosition = -100.0f;
#if ! EFI_UNIT_TEST
addConsoleAction("idleinfo", showIdleInfo);
addConsoleActionII("blipidle", blipIdle);
// split this whole file into manual controller and auto controller? move these commands into the file
// which would be dedicated to just auto-controller?
addConsoleAction("idlebench", startIdleBench);
#endif /* EFI_UNIT_TEST */
applyPidSettings(PASS_ENGINE_PARAMETER_SIGNATURE);
}
#endif /* EFI_IDLE_CONTROL */
void startPedalPins(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
2021-07-14 19:37:05 -07:00
#if EFI_PROD_CODE
// this is neutral/no gear switch input. on Miata it's wired both to clutch pedal and neutral in gearbox
2015-07-10 06:01:56 -07:00
// this switch is not used yet
if (isBrainPinValid(CONFIG(clutchDownPin))) {
efiSetPadMode("clutch down switch", CONFIG(clutchDownPin),
getInputMode(CONFIG(clutchDownPinMode)));
2017-05-15 02:08:17 -07:00
}
2015-07-10 06:01:56 -07:00
if (isBrainPinValid(CONFIG(clutchUpPin))) {
efiSetPadMode("clutch up switch", CONFIG(clutchUpPin),
getInputMode(CONFIG(clutchUpPinMode)));
2017-05-15 02:08:17 -07:00
}
if (isBrainPinValid(CONFIG(throttlePedalUpPin))) {
2019-07-21 17:11:12 -07:00
efiSetPadMode("throttle pedal up switch", CONFIG(throttlePedalUpPin),
getInputMode(CONFIG(throttlePedalUpPinMode)));
2019-07-21 14:06:16 -07:00
}
if (isBrainPinValid(engineConfiguration->brakePedalPin)) {
2017-05-15 05:40:54 -07:00
efiSetPadMode("brake pedal switch", engineConfiguration->brakePedalPin,
2017-05-15 02:08:17 -07:00
getInputMode(engineConfiguration->brakePedalPinMode));
}
2021-07-14 19:37:05 -07:00
#endif /* EFI_PROD_CODE */
}
2021-07-14 19:37:05 -07:00
void stopPedalPins(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
brain_pin_markUnused(activeConfiguration.clutchUpPin PASS_ENGINE_PARAMETER_SUFFIX);
brain_pin_markUnused(activeConfiguration.clutchDownPin PASS_ENGINE_PARAMETER_SUFFIX);
brain_pin_markUnused(activeConfiguration.throttlePedalUpPin PASS_ENGINE_PARAMETER_SUFFIX);
brain_pin_markUnused(activeConfiguration.brakePedalPin PASS_ENGINE_PARAMETER_SUFFIX);
2015-07-10 06:01:56 -07:00
}