rusefi-1/firmware/controllers/idle_thread.cpp

347 lines
11 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 is has the hardware & scheduling logic, desired idle level lives separately
*
*
* @date May 23, 2013
2017-01-03 03:05:22 -08:00
* @author Andrey Belomutskiy, (c) 2012-2017
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 "main.h"
#include "rpm_calculator.h"
#include "pwm_generator.h"
#include "idle_thread.h"
#include "pin_repository.h"
#include "engine_configuration.h"
#include "engine.h"
#include "stepper.h"
#if EFI_IDLE_CONTROL || defined(__DOXYGEN__)
static THD_WORKING_AREA(ivThreadStack, UTILITY_THREAD_STACK_SIZE);
static Logging *logger;
2016-02-13 12:03:37 -08:00
extern TunerStudioOutputChannels tsOutputChannels;
2015-07-10 06:01:56 -07:00
EXTERN_ENGINE
;
2015-11-09 16:03:32 -08:00
// todo: extract interface for idle valve hardware, with solenoid and stepper implementations?
2015-07-10 06:01:56 -07:00
static SimplePwm idleSolenoid;
static StepperMotor iacMotor;
/**
* that's the position with CLT and IAT corrections
*/
static percent_t actualIdlePosition = -100.0f;
2015-07-10 06:01:56 -07:00
void idleDebug(const char *msg, percent_t value) {
2015-08-15 17:01:21 -07:00
scheduleMsg(logger, "idle debug: %s%f", msg, value);
2015-07-10 06:01:56 -07:00
}
static void showIdleInfo(void) {
2017-01-28 22:04:16 -08:00
const char * idleModeStr = getIdle_mode_e(engineConfiguration->idleMode);
scheduleMsg(logger, "idleMode=%s position=%f isStepper=%s", idleModeStr,
2015-10-18 11:01:37 -07:00
getIdlePosition(), boolToString(boardConfiguration->useStepperIdle));
2017-05-16 17:52:52 -07:00
2015-07-10 06:01:56 -07:00
if (boardConfiguration->useStepperIdle) {
2017-01-28 22:04:16 -08:00
scheduleMsg(logger, "directionPin=%s reactionTime=%f", hwPortname(boardConfiguration->idle.stepperDirectionPin),
2015-07-10 06:01:56 -07:00
engineConfiguration->idleStepperReactionTime);
2017-01-28 22:04:16 -08:00
scheduleMsg(logger, "stepPin=%s steps=%d", hwPortname(boardConfiguration->idle.stepperStepPin),
2015-07-10 06:01:56 -07:00
engineConfiguration->idleStepperTotalSteps);
2017-01-28 22:04:16 -08:00
scheduleMsg(logger, "enablePin=%s/%d", hwPortname(engineConfiguration->stepperEnablePin),
engineConfiguration->stepperEnablePinMode);
2015-07-10 06:01:56 -07:00
} else {
scheduleMsg(logger, "idle valve freq=%d on %s", boardConfiguration->idle.solenoidFrequency,
hwPortname(boardConfiguration->idle.solenoidPin));
}
2017-05-16 17:52:52 -07:00
if (engineConfiguration->idleMode == IM_AUTO) {
2017-01-28 22:04:16 -08:00
scheduleMsg(logger, "idle P=%f I=%f D=%f dT=%d", engineConfiguration->idleRpmPid.pFactor,
engineConfiguration->idleRpmPid.iFactor,
engineConfiguration->idleRpmPid.dFactor,
engineConfiguration->idleDT);
}
2015-07-10 06:01:56 -07:00
}
2017-05-16 17:52:52 -07:00
void setIdleMode(idle_mode_e value) {
2015-07-10 06:01:56 -07:00
engineConfiguration->idleMode = value ? IM_AUTO : IM_MANUAL;
showIdleInfo();
}
static void setIdleValvePwm(percent_t value) {
/**
* currently idle level is an percent value (0-100 range), and PWM takes a float in the 0..1 range
* todo: unify?
*/
idleSolenoid.setSimplePwmDutyCycle(value / 100);
}
2017-04-29 11:40:55 -07:00
static void manualIdleController() {
int positionPercent = boardConfiguration->manIdlePosition;
2015-07-10 06:01:56 -07:00
2016-06-26 19:02:00 -07:00
if (isCranking()) {
positionPercent += engineConfiguration->crankingIdleAdjustment;
}
percent_t cltCorrectedPosition = interpolate2d(engine->sensors.clt, config->cltIdleCorrBins, config->cltIdleCorr,
2016-02-14 10:02:00 -08:00
CLT_CURVE_SIZE) / PERCENT_MULT * positionPercent;
2015-07-10 06:01:56 -07:00
// let's put the value into the right range
cltCorrectedPosition = maxF(cltCorrectedPosition, 0.01);
cltCorrectedPosition = minF(cltCorrectedPosition, 99.9);
2017-02-12 18:04:18 -08:00
if (engineConfiguration->debugMode == DBG_IDLE) {
2016-02-15 15:02:03 -08:00
tsOutputChannels.debugFloatField1 = actualIdlePosition;
}
2015-07-10 06:01:56 -07:00
if (absF(cltCorrectedPosition - actualIdlePosition) < 1) {
return; // value is pretty close, let's leave the poor valve alone
}
actualIdlePosition = cltCorrectedPosition;
2016-02-13 12:03:37 -08:00
2015-07-10 06:01:56 -07:00
if (boardConfiguration->useStepperIdle) {
iacMotor.setTargetPosition(cltCorrectedPosition / 100 * engineConfiguration->idleStepperTotalSteps);
} else {
setIdleValvePwm(cltCorrectedPosition);
}
}
2017-01-06 14:01:28 -08:00
void setIdleValvePosition(int positionPercent) {
2015-07-10 06:01:56 -07:00
if (positionPercent < 1 || positionPercent > 99)
return;
scheduleMsg(logger, "setting idle valve position %d", positionPercent);
showIdleInfo();
2017-04-29 11:40:55 -07:00
// todo: this is not great that we have to write into configuration here
boardConfiguration->manIdlePosition = positionPercent;
manualIdleController();
2015-07-10 06:01:56 -07:00
}
2015-09-01 20:02:49 -07:00
static int idlePositionBeforeBlip;
static efitimeus_t timeToStopBlip = 0;
2015-12-24 19:01:26 -08:00
static efitimeus_t timeToStopIdleTest = 0;
2015-09-01 20:02:49 -07:00
/**
* I use this questionable feature to tune acceleration enrichment
*/
static void blipIdle(int idlePosition, int durationMs) {
2015-11-09 16:03:32 -08:00
// todo: add 'blip' feature for automatic target control
2015-09-01 20:02:49 -07:00
if (timeToStopBlip != 0) {
return; // already in idle blip
}
2015-10-18 11:01:37 -07:00
idlePositionBeforeBlip = boardConfiguration->manIdlePosition;
2015-09-01 20:02:49 -07:00
setIdleValvePosition(idlePosition);
timeToStopBlip = getTimeNowUs() + 1000 * durationMs;
}
2015-12-24 19:01:26 -08:00
static void finishIdleTestIfNeeded() {
if (timeToStopIdleTest != 0 && getTimeNowUs() > timeToStopIdleTest)
timeToStopIdleTest = 0;
}
2015-09-01 20:02:49 -07:00
static void undoIdleBlipIfNeeded() {
if (timeToStopBlip != 0 && getTimeNowUs() > timeToStopBlip) {
timeToStopBlip = 0;
setIdleValvePosition(idlePositionBeforeBlip);
}
}
2015-10-18 11:01:37 -07:00
static percent_t currentIdleValve = -1;
percent_t getIdlePosition(void) {
if (engineConfiguration->idleMode == IM_AUTO) {
return currentIdleValve;
} else {
return boardConfiguration->manIdlePosition;
}
}
2016-06-26 18:02:40 -07:00
static void autoIdle() {
efitimems_t now = currentTimeMillis();
2017-05-16 17:18:28 -07:00
percent_t newValue = 0;//idlePositionController.getIdle(getRpmE(engine), now PASS_ENGINE_PARAMETER_SUFFIX);
2016-06-26 18:02:40 -07:00
if (currentIdleValve != newValue) {
currentIdleValve = newValue;
// todo: looks like in auto mode stepper value is not set, only solenoid?
setIdleValvePwm(newValue);
}
}
2015-07-10 06:01:56 -07:00
static msg_t ivThread(int param) {
(void) param;
chRegSetThreadName("IdleValve");
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
*/
2015-07-10 06:01:56 -07:00
while (true) {
chThdSleepMilliseconds(boardConfiguration->idleThreadPeriod);
// this value is not used yet
if (boardConfiguration->clutchDownPin != GPIO_UNASSIGNED) {
2017-05-15 02:08:17 -07:00
engine->clutchDownState = efiReadPin(boardConfiguration->clutchDownPin);
2015-07-10 06:01:56 -07:00
}
if (boardConfiguration->clutchUpPin != GPIO_UNASSIGNED) {
2017-05-15 02:08:17 -07:00
engine->clutchUpState = efiReadPin(boardConfiguration->clutchUpPin);
}
if (engineConfiguration->brakePedalPin != GPIO_UNASSIGNED) {
engine->brakePedalState = efiReadPin(engineConfiguration->brakePedalPin);
2015-07-10 06:01:56 -07:00
}
2015-12-24 19:01:26 -08:00
finishIdleTestIfNeeded();
2015-09-01 20:02:49 -07:00
undoIdleBlipIfNeeded();
2016-06-26 18:02:40 -07:00
if (engineConfiguration->idleMode == IM_MANUAL) {
2015-07-10 06:01:56 -07:00
// let's re-apply CLT correction
2017-04-29 11:40:55 -07:00
manualIdleController();
2016-06-26 18:02:40 -07:00
} else {
autoIdle();
2015-07-10 06:01:56 -07:00
}
}
#if defined __GNUC__
return -1;
#endif
}
2017-01-06 14:01:28 -08:00
void setTargetIdleRpm(int value) {
2017-05-16 17:18:28 -07:00
engineConfiguration->targetIdleRpm = value;
2015-07-10 06:01:56 -07:00
scheduleMsg(logger, "target idle RPM %d", value);
}
2015-11-09 16:03:32 -08:00
2015-10-19 19:02:51 -07:00
static void apply(void) {
// idleMath.updateFactors(engineConfiguration->idlePFactor, engineConfiguration->idleIFactor, engineConfiguration->idleDFactor, engineConfiguration->idleDT);
}
2017-01-06 13:03:41 -08:00
void setIdlePFactor(float value) {
2016-02-05 13:01:55 -08:00
engineConfiguration->idleRpmPid.pFactor = value;
2015-10-19 19:02:51 -07:00
apply();
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;
2015-10-19 19:02:51 -07:00
apply();
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;
2015-10-19 19:02:51 -07:00
apply();
showIdleInfo();
}
2017-01-06 13:03:41 -08:00
void setIdleDT(int value) {
2015-10-19 19:02:51 -07:00
engineConfiguration->idleDT = value;
apply();
showIdleInfo();
}
2016-12-26 18:04:16 -08:00
void startIdleBench(void) {
2015-12-24 19:01:26 -08:00
timeToStopIdleTest = getTimeNowUs() + MS2US(3000); // 3 seconds
2015-12-26 13:01:56 -08:00
scheduleMsg(logger, "idle valve bench test");
showIdleInfo();
2015-12-24 19:01:26 -08:00
}
2015-10-19 19:02:51 -07:00
void setDefaultIdleParameters(void) {
2016-02-05 13:01:55 -08:00
engineConfiguration->idleRpmPid.pFactor = 0.1f;
engineConfiguration->idleRpmPid.iFactor = 0.05f;
engineConfiguration->idleRpmPid.dFactor = 0.0f;
2015-10-19 19:02:51 -07:00
engineConfiguration->idleDT = 10;
}
2015-07-10 06:01:56 -07:00
static void applyIdleSolenoidPinState(PwmConfig *state, int stateIndex) {
efiAssertVoid(stateIndex < PWM_PHASE_MAX_COUNT, "invalid stateIndex");
efiAssertVoid(state->multiWave.waveCount == 1, "invalid idle waveCount");
OutputPin *output = state->outputPins[0];
int value = state->multiWave.waves[0].pinStates[stateIndex];
2015-12-26 13:01:56 -08:00
if (!value /* always allow turning solenoid off */ ||
2015-12-24 19:01:26 -08:00
(engine->rpmCalculator.rpmValue != 0 || timeToStopIdleTest != 0) /* do not run solenoid unless engine is spinning or bench testing in progress */
2016-02-05 13:01:55 -08:00
) {
2015-07-10 06:01:56 -07:00
output->setValue(value);
2016-02-05 13:01:55 -08:00
}
2015-07-10 06:01:56 -07:00
}
2015-11-09 16:03:32 -08:00
static void initIdleHardware() {
2015-07-10 06:01:56 -07:00
if (boardConfiguration->useStepperIdle) {
iacMotor.initialize(boardConfiguration->idle.stepperStepPin, boardConfiguration->idle.stepperDirectionPin,
engineConfiguration->idleStepperReactionTime, engineConfiguration->idleStepperTotalSteps,
engineConfiguration->stepperEnablePin);
} else {
/**
* Start PWM for idleValvePin
*/
2016-09-13 21:03:14 -07:00
startSimplePwmExt(&idleSolenoid, "Idle Valve", boardConfiguration->idle.solenoidPin, &enginePins.idleSolenoidPin,
2015-10-18 11:01:37 -07:00
boardConfiguration->idle.solenoidFrequency, boardConfiguration->manIdlePosition / 100,
2015-07-10 06:01:56 -07:00
applyIdleSolenoidPinState);
}
2015-11-09 16:03:32 -08:00
}
void startIdleThread(Logging*sharedLogger) {
2015-11-09 16:03:32 -08:00
logger = sharedLogger;
// todo: re-initialize idle pins on the fly
initIdleHardware();
2015-07-10 06:01:56 -07:00
2017-05-16 17:18:28 -07:00
//scheduleMsg(logger, "initial idle %d", idlePositionController.value);
2015-07-10 06:01:56 -07:00
chThdCreateStatic(ivThreadStack, sizeof(ivThreadStack), NORMALPRIO, (tfunc_t) ivThread, NULL);
// this is idle switch INPUT - sometimes there is a switch on the throttle pedal
// this switch is not used yet
2017-05-15 02:08:17 -07:00
if (boardConfiguration->clutchDownPin != GPIO_UNASSIGNED) {
2017-05-15 05:40:54 -07:00
efiSetPadMode("clutch down switch", boardConfiguration->clutchDownPin,
2015-07-10 06:01:56 -07:00
getInputMode(boardConfiguration->clutchDownPinMode));
2017-05-15 02:08:17 -07:00
}
2015-07-10 06:01:56 -07:00
2017-05-15 02:08:17 -07:00
if (boardConfiguration->clutchUpPin != GPIO_UNASSIGNED) {
2017-05-15 05:40:54 -07:00
efiSetPadMode("clutch up switch", boardConfiguration->clutchUpPin,
2015-07-10 06:01:56 -07:00
getInputMode(boardConfiguration->clutchUpPinMode));
2017-05-15 02:08:17 -07:00
}
if (engineConfiguration->brakePedalPin != GPIO_UNASSIGNED) {
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));
}
2015-07-10 06:01:56 -07:00
addConsoleAction("idleinfo", showIdleInfo);
2015-09-01 20:02:49 -07:00
addConsoleActionII("blipidle", blipIdle);
2015-10-19 19:02:51 -07:00
2015-11-09 16:03:32 -08:00
// split this whole file into manual controller and auto controller? move these commands into the file
// which would be dedicated to just auto-controller?
2015-10-19 19:02:51 -07:00
2015-12-24 19:01:26 -08:00
addConsoleAction("idlebench", startIdleBench);
2015-10-19 19:02:51 -07:00
apply();
2015-07-10 06:01:56 -07:00
}
#endif