/** * @file idle_controller.cpp * @brief Simple Idle Air Valve control algorithm * * This algorithm is trying to get current RPM to the desired 'target' value * by changing Idle Air Valve solenoid duty cycle. Depending on how far current RPM * is from the target RPM, the incremental change would be smaller or bigger. * * * todo: DEFAULT_IDLE_DUTY should be a field on the IdleValveState, not a constant * * @date May 22, 2013 * @author Andrey Belomutskiy, (c) 2012-2016 */ #include "main.h" #include "idle_controller.h" #include "efilib.h" #include "rpm_calculator.h" #include "tps.h" // 60% duty cycle by default #define DEFAULT_IDLE_DUTY 60 #define TPS_IDLE_LOCKOUT 5.0 // todo: is not this value a bit too high? #define IDLE_PERIOD 1000 static int lastGoodValue = DEFAULT_IDLE_DUTY; EXTERN_ENGINE ; IdleValveState::IdleValveState() { value = DEFAULT_IDLE_DUTY; timeOfLastIdleChange = 0; time = 0; targetRpmRangeLeft = targetRpmRangeRight = 0; } void IdleValveState::init(DECLARE_ENGINE_PARAMETER_F) { setTargetRpm(engineConfiguration->targetIdleRpm); } void IdleValveState::setTargetRpm(int targetRpm) { targetRpmRangeLeft = (int) (targetRpm * 0.93); targetRpmRangeRight = (int) (targetRpm * 1.07); } /** * @brief sets new idle valve duty cycle: checks the bounds and reports new value */ static percent_t setNewValue(IdleValveState *idle, int currentRpm, efitimems_t now, const char * msg, percent_t newValue) { newValue = maxF(newValue, MIN_IDLE); newValue = minF(newValue, MAX_IDLE); if (idle->value != newValue) { idleDebug(msg, currentRpm); idle->timeOfLastIdleChange = now; } idle->value = newValue; return newValue; } bool isTpsLockout(DECLARE_ENGINE_PARAMETER_F) { // if we have TPS sensor, then use it if (engineConfiguration->hasTpsSensor) return getTPS(PASS_ENGINE_PARAMETER_F) > TPS_IDLE_LOCKOUT; // TODO: if no TPS sensor then idle switch is our return true; } static percent_t changeValue(IdleValveState *idle, int currentRpm, int now, const char * msg, percent_t delta DECLARE_ENGINE_PARAMETER_S) { if (isTpsLockout(PASS_ENGINE_PARAMETER_F)) { // We are not supposed to be in idle mode. Don't touch anything idleDebug("TPS Lockout, TPS=", getTPS(PASS_ENGINE_PARAMETER_F)); return idle->value; } percent_t newValue = idle->value + delta; return setNewValue(idle, currentRpm, now, msg, newValue); } /** * now - current time in milliseconds */ percent_t IdleValveState::getIdle(int currentRpm, efitimems_t now DECLARE_ENGINE_PARAMETER_S) { if (currentRpm == 0 || isCranking()) { // todo: why hard-coded value during cranking return setNewValue(this, currentRpm, now, "cranking value: ", DEFAULT_IDLE_DUTY); } if (currentRpm < 0.7 * targetRpmRangeLeft) { return setNewValue(this, currentRpm, now, "RPMs are seriously low: ", lastGoodValue); } if (now - timeOfLastIdleChange < IDLE_PERIOD) { // too soon to adjust anything - exiting return value; } if (currentRpm > targetRpmRangeLeft && currentRpm < targetRpmRangeRight) { // current RPM is good enough // todo: need idle signal input //lastGoodValue = idle->value; return value; } if (currentRpm >= targetRpmRangeRight + 100) return changeValue(this, currentRpm, now, "idle control: rpm is too high: ", -IDLE_DECREASE_STEP PASS_ENGINE_PARAMETER); if (currentRpm >= targetRpmRangeRight) return changeValue(this, currentRpm, now, "idle control: rpm is a bit too high: ", -IDLE_DECREASE_SMALL_STEP PASS_ENGINE_PARAMETER); // we are here if RPM is low, let's see how low if (currentRpm < targetRpmRangeLeft - 100) { return changeValue(this, currentRpm, now, "idle control: RPMs are low: ", IDLE_INCREASE_STEP PASS_ENGINE_PARAMETER); } return changeValue(this, currentRpm, now, "idle control: RPMs are a bit low: ", IDLE_INCREASE_SMALL_STEP PASS_ENGINE_PARAMETER); }