refactoring - extracting idle state

This commit is contained in:
rusefillc 2021-10-17 00:27:12 -04:00
parent 25ff3fe2d7
commit 2431fa79b3
8 changed files with 222 additions and 54 deletions

View File

@ -25,8 +25,6 @@ StepperMotor iacMotor CCM_OPTIONAL;
static SimplePwm idleSolenoidOpen("idle open");
static SimplePwm idleSolenoidClose("idle close");
extern efitimeus_t timeToStopIdleTest;
void applyIACposition(percent_t position DECLARE_ENGINE_PARAMETER_SUFFIX) {
/**
* currently idle level is an percent value (0-100 range), and PWM takes a float in the 0..1 range
@ -44,7 +42,7 @@ void applyIACposition(percent_t position DECLARE_ENGINE_PARAMETER_SUFFIX) {
#endif /* EFI_UNIT_TEST */
} else {
// if not spinning or running a bench test, turn off the idle valve(s) to be quieter and save power
if (!engine->triggerCentral.engineMovedRecently() && timeToStopIdleTest == 0) {
if (!engine->triggerCentral.engineMovedRecently() && engine->timeToStopIdleTest == 0) {
idleSolenoidOpen.setSimplePwmDutyCycle(0);
idleSolenoidClose.setSimplePwmDutyCycle(0);
return;

View File

@ -7,5 +7,12 @@ custom percent_t 4 bits, F32, @OFFSET@, [0:2], "not important"
percent_t baseIdlePosition;current position without adjustments (iacByTpsTaper, afterCrankingIACtaperDuration)
int throttlePedalUpState;true in IDLE throttle pedal state, false if driver is touching the pedal\ntodo: better name for this field?
percent_t etbIdleAddition;ETB position adjustment related to idle RPM control
bit mightResetPid;The idea of 'mightResetPid' is to reset PID only once - each time when TPS > idlePidDeactivationTpsThreshold.\nThe throttle pedal can be pressed for a long time, making the PID data obsolete (thus the reset is required).\nWe set 'mightResetPid' to true only if PID was actually used (i.e. idlePid.getOutput() was called) to save some CPU resources.\nSee automaticIdleController().
bit shouldResetPid;
bit wasResetPid;This is needed to slowly turn on the PID back after it was reset.
bit mustResetPid;This is used when the PID configuration is changed, to guarantee the reset
! end of idle_state_s structure definition
end_struct

View File

@ -30,7 +30,109 @@ struct idle_state_s {
* offset 16
*/
percent_t etbIdleAddition = (percent_t)0;
/** total size 20*/
/**
* The idea of 'mightResetPid' is to reset PID only once - each time when TPS > idlePidDeactivationTpsThreshold.
* The throttle pedal can be pressed for a long time, making the PID data obsolete (thus the reset is required).
* We set 'mightResetPid' to true only if PID was actually used (i.e. idlePid.getOutput() was called) to save some CPU resources.
* See automaticIdleController().
offset 20 bit 0 */
bool mightResetPid : 1;
/**
offset 20 bit 1 */
bool shouldResetPid : 1;
/**
* This is needed to slowly turn on the PID back after it was reset.
offset 20 bit 2 */
bool wasResetPid : 1;
/**
* This is used when the PID configuration is changed, to guarantee the reset
offset 20 bit 3 */
bool mustResetPid : 1;
/**
offset 20 bit 4 */
bool unusedBit_9_4 : 1;
/**
offset 20 bit 5 */
bool unusedBit_9_5 : 1;
/**
offset 20 bit 6 */
bool unusedBit_9_6 : 1;
/**
offset 20 bit 7 */
bool unusedBit_9_7 : 1;
/**
offset 20 bit 8 */
bool unusedBit_9_8 : 1;
/**
offset 20 bit 9 */
bool unusedBit_9_9 : 1;
/**
offset 20 bit 10 */
bool unusedBit_9_10 : 1;
/**
offset 20 bit 11 */
bool unusedBit_9_11 : 1;
/**
offset 20 bit 12 */
bool unusedBit_9_12 : 1;
/**
offset 20 bit 13 */
bool unusedBit_9_13 : 1;
/**
offset 20 bit 14 */
bool unusedBit_9_14 : 1;
/**
offset 20 bit 15 */
bool unusedBit_9_15 : 1;
/**
offset 20 bit 16 */
bool unusedBit_9_16 : 1;
/**
offset 20 bit 17 */
bool unusedBit_9_17 : 1;
/**
offset 20 bit 18 */
bool unusedBit_9_18 : 1;
/**
offset 20 bit 19 */
bool unusedBit_9_19 : 1;
/**
offset 20 bit 20 */
bool unusedBit_9_20 : 1;
/**
offset 20 bit 21 */
bool unusedBit_9_21 : 1;
/**
offset 20 bit 22 */
bool unusedBit_9_22 : 1;
/**
offset 20 bit 23 */
bool unusedBit_9_23 : 1;
/**
offset 20 bit 24 */
bool unusedBit_9_24 : 1;
/**
offset 20 bit 25 */
bool unusedBit_9_25 : 1;
/**
offset 20 bit 26 */
bool unusedBit_9_26 : 1;
/**
offset 20 bit 27 */
bool unusedBit_9_27 : 1;
/**
offset 20 bit 28 */
bool unusedBit_9_28 : 1;
/**
offset 20 bit 29 */
bool unusedBit_9_29 : 1;
/**
offset 20 bit 30 */
bool unusedBit_9_30 : 1;
/**
offset 20 bit 31 */
bool unusedBit_9_31 : 1;
/** total size 24*/
};
// end

View File

@ -40,19 +40,6 @@
#include "stepper.h"
#endif
// todo: move all static vars to engine->idle?
static bool shouldResetPid = false;
// The idea of 'mightResetPid' is to reset PID only once - each time when TPS > idlePidDeactivationTpsThreshold.
// The throttle pedal can be pressed for a long time, making the PID data obsolete (thus the reset is required).
// We set 'mightResetPid' to true only if PID was actually used (i.e. idlePid.getOutput() was called) to save some CPU resources.
// See automaticIdleController().
static bool mightResetPid = false;
// This is needed to slowly turn on the PID back after it was reset.
static bool wasResetPid = false;
// This is used when the PID configuration is changed, to guarantee the reset
static bool mustResetPid = false;
static efitimeus_t restoreAfterPidResetTimeUs = 0;
static PidIndustrial industrialWithOverrideIdlePid;
@ -71,9 +58,6 @@ Pid * getIdlePid(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
return &industrialWithOverrideIdlePid;
}
static uint32_t lastCrankingCyclesCounter = 0;
static float lastCrankingIacPosition;
static iacPidMultiplier_t iacPidMultMap;
#if ! EFI_UNIT_TEST
@ -280,32 +264,27 @@ float IdleController::getIdleTimingAdjustment(int rpm, int targetRpm, Phase phas
return m_timingPid.getOutput(targetRpm, rpm, FAST_CALLBACK_PERIOD_MS / 1000.0f);
}
/**
* idle blip is a development tool: alternator PID research for instance have benefited from a repetitive change of RPM
*/
static percent_t blipIdlePosition;
static efitimeus_t timeToStopBlip = 0;
efitimeus_t timeToStopIdleTest = 0;
/**
* I use this questionable feature to tune acceleration enrichment
*/
static void blipIdle(int idlePosition, int durationMs) {
if (timeToStopBlip != 0) {
#if ! EFI_UNIT_TEST
if (engine->timeToStopBlip != 0) {
return; // already in idle blip
}
blipIdlePosition = idlePosition;
timeToStopBlip = getTimeNowUs() + 1000 * durationMs;
engine->blipIdlePosition = idlePosition;
engine->timeToStopBlip = getTimeNowUs() + 1000 * durationMs;
#endif // EFI_UNIT_TEST
}
static void finishIdleTestIfNeeded() {
if (timeToStopIdleTest != 0 && getTimeNowUs() > timeToStopIdleTest)
timeToStopIdleTest = 0;
static void finishIdleTestIfNeeded(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
if (engine->timeToStopIdleTest != 0 && getTimeNowUs() > engine->timeToStopIdleTest)
engine->timeToStopIdleTest = 0;
}
static void undoIdleBlipIfNeeded() {
if (timeToStopBlip != 0 && getTimeNowUs() > timeToStopBlip) {
timeToStopBlip = 0;
static void undoIdleBlipIfNeeded(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
if (engine->timeToStopBlip != 0 && getTimeNowUs() > engine->timeToStopBlip) {
engine->timeToStopBlip = 0;
}
}
@ -315,15 +294,15 @@ static void undoIdleBlipIfNeeded() {
float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos, int rpm, int targetRpm) {
auto idlePid = getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE);
if (shouldResetPid) {
if (engine->idle.shouldResetPid) {
// we reset only if I-term is negative, because the positive I-term is good - it keeps RPM from dropping too low
if (idlePid->getIntegration() <= 0 || mustResetPid) {
if (idlePid->getIntegration() <= 0 || engine->idle.mustResetPid) {
idlePid->reset();
mustResetPid = false;
engine->idle.mustResetPid = false;
}
// alternatorPidResetCounter++;
shouldResetPid = false;
wasResetPid = true;
engine->idle.shouldResetPid = false;
engine->idle.wasResetPid = true;
}
// todo: move this to pid_s one day
@ -335,9 +314,9 @@ float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos,
if (phase != IIdleController::Phase::Idling) {
// 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.
if (mightResetPid) {
mightResetPid = false;
shouldResetPid = true;
if (engine->idle.mightResetPid) {
engine->idle.mightResetPid = false;
engine->idle.shouldResetPid = true;
}
engine->idle.idleState = TPS_THRESHOLD;
@ -364,9 +343,9 @@ float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos,
}
// if PID was previously reset, we store the time when it turned on back (see errorAmpCoef correction below)
if (wasResetPid) {
if (engine->idle.wasResetPid) {
restoreAfterPidResetTimeUs = nowUs;
wasResetPid = false;
engine->idle.wasResetPid = false;
}
// increase the errorAmpCoef slowly to restore the process correctly after the PID reset
// todo: move restoreAfterPidResetTimeUs to idle?
@ -380,7 +359,7 @@ float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos,
engine->idle.idleState = PID_VALUE;
// the state of PID has been changed, so we might reset it now, but only when needed (see idlePidDeactivationTpsThreshold)
mightResetPid = true;
engine->idle.mightResetPid = true;
// Apply PID Multiplier if used
if (CONFIG(useIacPidMultTable)) {
@ -447,13 +426,13 @@ float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos,
getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->showPidStatus("idle");
}
finishIdleTestIfNeeded();
undoIdleBlipIfNeeded();
finishIdleTestIfNeeded(PASS_ENGINE_PARAMETER_SIGNATURE);
undoIdleBlipIfNeeded(PASS_ENGINE_PARAMETER_SIGNATURE);
percent_t iacPosition;
if (timeToStopBlip != 0) {
iacPosition = blipIdlePosition;
if (engine->timeToStopBlip != 0) {
iacPosition = engine->blipIdlePosition;
engine->idle.idleState = BLIP;
} else {
// Always apply closed loop correction
@ -539,8 +518,8 @@ void setDefaultIdleParameters(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
#if ! EFI_UNIT_TEST
void onConfigurationChangeIdleCallback(engine_configuration_s *previousConfiguration) {
shouldResetPid = !getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->isSame(&previousConfiguration->idleRpmPid);
mustResetPid = shouldResetPid;
engine->idle.shouldResetPid = !getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->isSame(&previousConfiguration->idleRpmPid);
engine->idle.mustResetPid = engine->idle.shouldResetPid;
}
void setTargetIdleRpm(int value) {
@ -576,7 +555,7 @@ void setIdleDFactor(float value) {
* Idle test would activate the solenoid for three seconds
*/
void startIdleBench(void) {
timeToStopIdleTest = getTimeNowUs() + MS2US(3000); // 3 seconds
engine->timeToStopIdleTest = getTimeNowUs() + MS2US(3000); // 3 seconds
efiPrintf("idle valve bench test");
showIdleInfo();
}

View File

@ -0,0 +1,10 @@
/*
* @file idle_thread_io.cpp
*
* @date Oct 17, 2021
* @author Andrey Belomutskiy, (c) 2012-2020
*/

View File

@ -323,6 +323,13 @@ public:
void onTriggerSignalEvent();
EngineState engineState;
idle_state_s idle;
/**
* idle blip is a development tool: alternator PID research for instance have benefited from a repetitive change of RPM
*/
percent_t blipIdlePosition;
efitimeus_t timeToStopBlip = 0;
efitimeus_t timeToStopIdleTest = 0;
SensorsState sensors;
efitick_t mainRelayBenchStartNt = 0;

View File

@ -17,6 +17,7 @@ CONTROLLERS_SRC_CPP = \
$(CONTROLLERS_DIR)/actuators/boost_control.cpp \
$(CONTROLLERS_DIR)/actuators/dc_motors.cpp \
$(CONTROLLERS_DIR)/actuators/fan_control.cpp \
$(CONTROLLERS_DIR)/actuators/idle_thread_io.cpp \
$(CONTROLLERS_DIR)/actuators/idle_hardware.cpp \
$(CONTROLLERS_DIR)/actuators/idle_thread.cpp \
$(CONTROLLERS_DIR)/actuators/pwm_tester.cpp \

View File

@ -11,11 +11,75 @@ public class IdleState {
public static final Field BASEIDLEPOSITION = Field.create("BASEIDLEPOSITION", 8, FieldType.FLOAT);
public static final Field THROTTLEPEDALUPSTATE = Field.create("THROTTLEPEDALUPSTATE", 12, FieldType.INT);
public static final Field ETBIDLEADDITION = Field.create("ETBIDLEADDITION", 16, FieldType.FLOAT);
public static final Field MIGHTRESETPID = Field.create("MIGHTRESETPID", 20, FieldType.BIT, 0);
public static final Field SHOULDRESETPID = Field.create("SHOULDRESETPID", 20, FieldType.BIT, 1);
public static final Field WASRESETPID = Field.create("WASRESETPID", 20, FieldType.BIT, 2);
public static final Field MUSTRESETPID = Field.create("MUSTRESETPID", 20, FieldType.BIT, 3);
public static final Field UNUSEDBIT_9_4 = Field.create("UNUSEDBIT_9_4", 20, FieldType.BIT, 4);
public static final Field UNUSEDBIT_9_5 = Field.create("UNUSEDBIT_9_5", 20, FieldType.BIT, 5);
public static final Field UNUSEDBIT_9_6 = Field.create("UNUSEDBIT_9_6", 20, FieldType.BIT, 6);
public static final Field UNUSEDBIT_9_7 = Field.create("UNUSEDBIT_9_7", 20, FieldType.BIT, 7);
public static final Field UNUSEDBIT_9_8 = Field.create("UNUSEDBIT_9_8", 20, FieldType.BIT, 8);
public static final Field UNUSEDBIT_9_9 = Field.create("UNUSEDBIT_9_9", 20, FieldType.BIT, 9);
public static final Field UNUSEDBIT_9_10 = Field.create("UNUSEDBIT_9_10", 20, FieldType.BIT, 10);
public static final Field UNUSEDBIT_9_11 = Field.create("UNUSEDBIT_9_11", 20, FieldType.BIT, 11);
public static final Field UNUSEDBIT_9_12 = Field.create("UNUSEDBIT_9_12", 20, FieldType.BIT, 12);
public static final Field UNUSEDBIT_9_13 = Field.create("UNUSEDBIT_9_13", 20, FieldType.BIT, 13);
public static final Field UNUSEDBIT_9_14 = Field.create("UNUSEDBIT_9_14", 20, FieldType.BIT, 14);
public static final Field UNUSEDBIT_9_15 = Field.create("UNUSEDBIT_9_15", 20, FieldType.BIT, 15);
public static final Field UNUSEDBIT_9_16 = Field.create("UNUSEDBIT_9_16", 20, FieldType.BIT, 16);
public static final Field UNUSEDBIT_9_17 = Field.create("UNUSEDBIT_9_17", 20, FieldType.BIT, 17);
public static final Field UNUSEDBIT_9_18 = Field.create("UNUSEDBIT_9_18", 20, FieldType.BIT, 18);
public static final Field UNUSEDBIT_9_19 = Field.create("UNUSEDBIT_9_19", 20, FieldType.BIT, 19);
public static final Field UNUSEDBIT_9_20 = Field.create("UNUSEDBIT_9_20", 20, FieldType.BIT, 20);
public static final Field UNUSEDBIT_9_21 = Field.create("UNUSEDBIT_9_21", 20, FieldType.BIT, 21);
public static final Field UNUSEDBIT_9_22 = Field.create("UNUSEDBIT_9_22", 20, FieldType.BIT, 22);
public static final Field UNUSEDBIT_9_23 = Field.create("UNUSEDBIT_9_23", 20, FieldType.BIT, 23);
public static final Field UNUSEDBIT_9_24 = Field.create("UNUSEDBIT_9_24", 20, FieldType.BIT, 24);
public static final Field UNUSEDBIT_9_25 = Field.create("UNUSEDBIT_9_25", 20, FieldType.BIT, 25);
public static final Field UNUSEDBIT_9_26 = Field.create("UNUSEDBIT_9_26", 20, FieldType.BIT, 26);
public static final Field UNUSEDBIT_9_27 = Field.create("UNUSEDBIT_9_27", 20, FieldType.BIT, 27);
public static final Field UNUSEDBIT_9_28 = Field.create("UNUSEDBIT_9_28", 20, FieldType.BIT, 28);
public static final Field UNUSEDBIT_9_29 = Field.create("UNUSEDBIT_9_29", 20, FieldType.BIT, 29);
public static final Field UNUSEDBIT_9_30 = Field.create("UNUSEDBIT_9_30", 20, FieldType.BIT, 30);
public static final Field UNUSEDBIT_9_31 = Field.create("UNUSEDBIT_9_31", 20, FieldType.BIT, 31);
public static final Field[] VALUES = {
IDLESTATE,
CURRENTIDLEPOSITION,
BASEIDLEPOSITION,
THROTTLEPEDALUPSTATE,
ETBIDLEADDITION,
MIGHTRESETPID,
SHOULDRESETPID,
WASRESETPID,
MUSTRESETPID,
UNUSEDBIT_9_4,
UNUSEDBIT_9_5,
UNUSEDBIT_9_6,
UNUSEDBIT_9_7,
UNUSEDBIT_9_8,
UNUSEDBIT_9_9,
UNUSEDBIT_9_10,
UNUSEDBIT_9_11,
UNUSEDBIT_9_12,
UNUSEDBIT_9_13,
UNUSEDBIT_9_14,
UNUSEDBIT_9_15,
UNUSEDBIT_9_16,
UNUSEDBIT_9_17,
UNUSEDBIT_9_18,
UNUSEDBIT_9_19,
UNUSEDBIT_9_20,
UNUSEDBIT_9_21,
UNUSEDBIT_9_22,
UNUSEDBIT_9_23,
UNUSEDBIT_9_24,
UNUSEDBIT_9_25,
UNUSEDBIT_9_26,
UNUSEDBIT_9_27,
UNUSEDBIT_9_28,
UNUSEDBIT_9_29,
UNUSEDBIT_9_30,
UNUSEDBIT_9_31,
};
}