replace ETB index with function enum (#1807)
* two throttles one thread * look at all this RAM! * add enum * switch from index to function * test fixup * improve init logic * remove old vw idle mode bit
This commit is contained in:
parent
c8c673b8e0
commit
f1c04efefa
|
@ -99,56 +99,55 @@ static bool startupPositionError = false;
|
||||||
|
|
||||||
#define STARTUP_NEUTRAL_POSITION_ERROR_THRESHOLD 5
|
#define STARTUP_NEUTRAL_POSITION_ERROR_THRESHOLD 5
|
||||||
|
|
||||||
static SensorType indexToTpsSensor(size_t index, bool dcMotorIdleValve) {
|
static SensorType functionToPositionSensor(etb_function_e func) {
|
||||||
if (dcMotorIdleValve) {
|
switch(func) {
|
||||||
return SensorType::Tps2;
|
case ETB_Throttle1: return SensorType::Tps1;
|
||||||
}
|
case ETB_Throttle2: return SensorType::Tps2;
|
||||||
|
case ETB_IdleValve: return SensorType::IdlePosition;
|
||||||
switch(index) {
|
case ETB_Wastegate: return SensorType::WastegatePosition;
|
||||||
case 0: return SensorType::Tps1;
|
default: return SensorType::Invalid;
|
||||||
default: return SensorType::Tps2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static SensorType indexToTpsSensorPrimary(size_t index) {
|
static SensorType functionToTpsSensorPrimary(etb_function_e func) {
|
||||||
switch(index) {
|
switch(func) {
|
||||||
case 0: return SensorType::Tps1Primary;
|
case ETB_Throttle1: return SensorType::Tps1Primary;
|
||||||
default: return SensorType::Tps2Primary;
|
default: return SensorType::Tps2Primary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static SensorType indexToTpsSensorSecondary(size_t index) {
|
static SensorType functionToTpsSensorSecondary(etb_function_e func) {
|
||||||
switch(index) {
|
switch(func) {
|
||||||
case 0: return SensorType::Tps1Secondary;
|
case ETB_Throttle1: return SensorType::Tps1Secondary;
|
||||||
default: return SensorType::Tps2Secondary;
|
default: return SensorType::Tps2Secondary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if EFI_TUNER_STUDIO
|
#if EFI_TUNER_STUDIO
|
||||||
static TsCalMode indexToCalModePriMin(size_t index) {
|
static TsCalMode functionToCalModePriMin(etb_function_e func) {
|
||||||
switch (index) {
|
switch (func) {
|
||||||
case 0: return TsCalMode::Tps1Min;
|
case ETB_Throttle1: return TsCalMode::Tps1Min;
|
||||||
default: return TsCalMode::Tps2Min;
|
default: return TsCalMode::Tps2Min;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static TsCalMode indexToCalModePriMax(size_t index) {
|
static TsCalMode functionToCalModePriMax(etb_function_e func) {
|
||||||
switch (index) {
|
switch (func) {
|
||||||
case 0: return TsCalMode::Tps1Max;
|
case ETB_Throttle1: return TsCalMode::Tps1Max;
|
||||||
default: return TsCalMode::Tps2Max;
|
default: return TsCalMode::Tps2Max;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static TsCalMode indexToCalModeSecMin(size_t index) {
|
static TsCalMode functionToCalModeSecMin(etb_function_e func) {
|
||||||
switch (index) {
|
switch (func) {
|
||||||
case 0: return TsCalMode::Tps1SecondaryMin;
|
case ETB_Throttle1: return TsCalMode::Tps1SecondaryMin;
|
||||||
default: return TsCalMode::Tps2SecondaryMin;
|
default: return TsCalMode::Tps2SecondaryMin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static TsCalMode indexToCalModeSecMax(size_t index) {
|
static TsCalMode functionToCalModeSecMax(etb_function_e func) {
|
||||||
switch (index) {
|
switch (func) {
|
||||||
case 0: return TsCalMode::Tps1SecondaryMax;
|
case ETB_Throttle1: return TsCalMode::Tps1SecondaryMax;
|
||||||
default: return TsCalMode::Tps2SecondaryMax;
|
default: return TsCalMode::Tps2SecondaryMax;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,12 +160,19 @@ static percent_t currentEtbDuty;
|
||||||
// this macro clamps both positive and negative percentages from about -100% to 100%
|
// this macro clamps both positive and negative percentages from about -100% to 100%
|
||||||
#define ETB_PERCENT_TO_DUTY(x) (clampF(-ETB_DUTY_LIMIT, 0.01f * (x), ETB_DUTY_LIMIT))
|
#define ETB_PERCENT_TO_DUTY(x) (clampF(-ETB_DUTY_LIMIT, 0.01f * (x), ETB_DUTY_LIMIT))
|
||||||
|
|
||||||
void EtbController::init(SensorType positionSensor, DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) {
|
bool EtbController::init(etb_function_e function, DcMotor *motor, pid_s *pidParameters, const ValueProvider3D* pedalMap) {
|
||||||
m_positionSensor = positionSensor;
|
if (function == ETB_None) {
|
||||||
|
// if not configured, don't init.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_function = function;
|
||||||
|
m_positionSensor = functionToPositionSensor(function);
|
||||||
m_motor = motor;
|
m_motor = motor;
|
||||||
m_myIndex = ownIndex;
|
|
||||||
m_pid.initPidClass(pidParameters);
|
m_pid.initPidClass(pidParameters);
|
||||||
m_pedalMap = pedalMap;
|
m_pedalMap = pedalMap;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EtbController::reset() {
|
void EtbController::reset() {
|
||||||
|
@ -192,18 +198,37 @@ void EtbController::setIdlePosition(percent_t pos) {
|
||||||
}
|
}
|
||||||
|
|
||||||
expected<percent_t> EtbController::getSetpoint() const {
|
expected<percent_t> EtbController::getSetpoint() const {
|
||||||
// A few extra preconditions if throttle control is invalid
|
switch (m_function) {
|
||||||
if (startupPositionError) {
|
case ETB_Throttle1:
|
||||||
|
case ETB_Throttle2:
|
||||||
|
return getSetpointEtb();
|
||||||
|
case ETB_IdleValve:
|
||||||
|
return getSetpointIdleValve();
|
||||||
|
case ETB_Wastegate:
|
||||||
|
return getSetpointWastegate();
|
||||||
|
default:
|
||||||
return unexpected;
|
return unexpected;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expected<percent_t> EtbController::getSetpointIdleValve() const {
|
||||||
// VW ETB idle mode uses an ETB only for idle (a mini-ETB sets the lower stop, and a normal cable
|
// VW ETB idle mode uses an ETB only for idle (a mini-ETB sets the lower stop, and a normal cable
|
||||||
// can pull the throttle up off the stop.), so we directly control the throttle with the idle position.
|
// can pull the throttle up off the stop.), so we directly control the throttle with the idle position.
|
||||||
if (CONFIG(dcMotorIdleValve)) {
|
|
||||||
#if EFI_TUNER_STUDIO
|
#if EFI_TUNER_STUDIO
|
||||||
tsOutputChannels.etbTarget = m_idlePosition;
|
tsOutputChannels.etbTarget = m_idlePosition;
|
||||||
#endif // EFI_TUNER_STUDIO
|
#endif // EFI_TUNER_STUDIO
|
||||||
return clampF(0, m_idlePosition, 100);
|
return clampF(0, m_idlePosition, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
expected<percent_t> EtbController::getSetpointWastegate() const {
|
||||||
|
// TODO: implement me!
|
||||||
|
return unexpected;
|
||||||
|
}
|
||||||
|
|
||||||
|
expected<percent_t> EtbController::getSetpointEtb() const {
|
||||||
|
// A few extra preconditions if throttle control is invalid
|
||||||
|
if (startupPositionError) {
|
||||||
|
return unexpected;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the pedal map hasn't been set, we can't provide a setpoint.
|
// If the pedal map hasn't been set, we can't provide a setpoint.
|
||||||
|
@ -236,7 +261,7 @@ expected<percent_t> EtbController::getSetpoint() const {
|
||||||
percent_t targetPosition = interpolateClamped(0, etbIdleAddition, 100, 100, targetFromTable);
|
percent_t targetPosition = interpolateClamped(0, etbIdleAddition, 100, 100, targetFromTable);
|
||||||
|
|
||||||
#if EFI_TUNER_STUDIO
|
#if EFI_TUNER_STUDIO
|
||||||
if (m_myIndex == 0) {
|
if (m_function == ETB_Throttle1) {
|
||||||
tsOutputChannels.etbTarget = targetPosition;
|
tsOutputChannels.etbTarget = targetPosition;
|
||||||
}
|
}
|
||||||
#endif // EFI_TUNER_STUDIO
|
#endif // EFI_TUNER_STUDIO
|
||||||
|
@ -360,15 +385,17 @@ expected<percent_t> EtbController::getClosedLoop(percent_t target, percent_t obs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only report the 0th throttle
|
// Only report the 0th throttle
|
||||||
if (m_myIndex == 0) {
|
if (m_function == ETB_Throttle1) {
|
||||||
#if EFI_TUNER_STUDIO
|
#if EFI_TUNER_STUDIO
|
||||||
// Error is positive if the throttle needs to open further
|
// Error is positive if the throttle needs to open further
|
||||||
tsOutputChannels.etb1Error = target - observation;
|
tsOutputChannels.etb1Error = target - observation;
|
||||||
#endif /* EFI_TUNER_STUDIO */
|
#endif /* EFI_TUNER_STUDIO */
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only allow autotune with stopped engine
|
// Only allow autotune with stopped engine, and on the first throttle
|
||||||
if (GET_RPM() == 0 && engine->etbAutoTune) {
|
if (GET_RPM() == 0
|
||||||
|
&& engine->etbAutoTune
|
||||||
|
&& m_function == ETB_Throttle1) {
|
||||||
return getClosedLoopAutotune(observation);
|
return getClosedLoopAutotune(observation);
|
||||||
} else {
|
} else {
|
||||||
// Normal case - use PID to compute closed loop part
|
// Normal case - use PID to compute closed loop part
|
||||||
|
@ -379,7 +406,7 @@ expected<percent_t> EtbController::getClosedLoop(percent_t target, percent_t obs
|
||||||
void EtbController::setOutput(expected<percent_t> outputValue) {
|
void EtbController::setOutput(expected<percent_t> outputValue) {
|
||||||
#if EFI_TUNER_STUDIO
|
#if EFI_TUNER_STUDIO
|
||||||
// Only report first-throttle stats
|
// Only report first-throttle stats
|
||||||
if (m_myIndex == 0) {
|
if (m_function == ETB_Throttle1) {
|
||||||
tsOutputChannels.etb1DutyCycle = outputValue.value_or(0);
|
tsOutputChannels.etb1DutyCycle = outputValue.value_or(0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -396,9 +423,14 @@ void EtbController::setOutput(expected<percent_t> outputValue) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EtbController::update() {
|
void EtbController::update() {
|
||||||
|
// If we didn't get initialized, fail fast
|
||||||
|
if (!m_motor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#if EFI_TUNER_STUDIO
|
#if EFI_TUNER_STUDIO
|
||||||
// Only debug throttle #0
|
// Only debug throttle #1
|
||||||
if (m_myIndex == 0) {
|
if (m_function == ETB_Throttle1) {
|
||||||
// set debug_mode 17
|
// set debug_mode 17
|
||||||
if (engineConfiguration->debugMode == DBG_ELECTRONIC_THROTTLE_PID) {
|
if (engineConfiguration->debugMode == DBG_ELECTRONIC_THROTTLE_PID) {
|
||||||
m_pid.postState(&tsOutputChannels);
|
m_pid.postState(&tsOutputChannels);
|
||||||
|
@ -474,7 +506,10 @@ DISPLAY(DISPLAY_IF(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
void EtbController::autoCalibrateTps() {
|
void EtbController::autoCalibrateTps() {
|
||||||
|
// Only auto calibrate throttles
|
||||||
|
if (m_function == ETB_Throttle1 || m_function == ETB_Throttle2) {
|
||||||
m_isAutocal = true;
|
m_isAutocal = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !EFI_UNIT_TEST
|
#if !EFI_UNIT_TEST
|
||||||
|
@ -500,14 +535,14 @@ struct EtbImpl final : public EtbController {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t myIndex = getMyIndex();
|
auto myFunction = getFunction();
|
||||||
|
|
||||||
// First grab open
|
// First grab open
|
||||||
motor->set(0.5f);
|
motor->set(0.5f);
|
||||||
motor->enable();
|
motor->enable();
|
||||||
chThdSleepMilliseconds(1000);
|
chThdSleepMilliseconds(1000);
|
||||||
float primaryMax = Sensor::getRaw(indexToTpsSensorPrimary(myIndex)) * TPS_TS_CONVERSION;
|
float primaryMax = Sensor::getRaw(functionToTpsSensorPrimary(myFunction)) * TPS_TS_CONVERSION;
|
||||||
float secondaryMax = Sensor::getRaw(indexToTpsSensorSecondary(myIndex)) * TPS_TS_CONVERSION;
|
float secondaryMax = Sensor::getRaw(functionToTpsSensorSecondary(myFunction)) * TPS_TS_CONVERSION;
|
||||||
|
|
||||||
// Let it return
|
// Let it return
|
||||||
motor->set(0);
|
motor->set(0);
|
||||||
|
@ -516,24 +551,24 @@ struct EtbImpl final : public EtbController {
|
||||||
// Now grab closed
|
// Now grab closed
|
||||||
motor->set(-0.5f);
|
motor->set(-0.5f);
|
||||||
chThdSleepMilliseconds(1000);
|
chThdSleepMilliseconds(1000);
|
||||||
float primaryMin = Sensor::getRaw(indexToTpsSensorPrimary(myIndex)) * TPS_TS_CONVERSION;
|
float primaryMin = Sensor::getRaw(functionToTpsSensorPrimary(myFunction)) * TPS_TS_CONVERSION;
|
||||||
float secondaryMin = Sensor::getRaw(indexToTpsSensorSecondary(myIndex)) * TPS_TS_CONVERSION;
|
float secondaryMin = Sensor::getRaw(functionToTpsSensorSecondary(myFunction)) * TPS_TS_CONVERSION;
|
||||||
|
|
||||||
// Finally disable and reset state
|
// Finally disable and reset state
|
||||||
motor->disable();
|
motor->disable();
|
||||||
|
|
||||||
// Write out the learned values to TS, waiting briefly after setting each to let TS grab it
|
// Write out the learned values to TS, waiting briefly after setting each to let TS grab it
|
||||||
tsOutputChannels.calibrationMode = indexToCalModePriMax(myIndex);
|
tsOutputChannels.calibrationMode = functionToCalModePriMax(myFunction);
|
||||||
tsOutputChannels.calibrationValue = primaryMax;
|
tsOutputChannels.calibrationValue = primaryMax;
|
||||||
chThdSleepMilliseconds(500);
|
chThdSleepMilliseconds(500);
|
||||||
tsOutputChannels.calibrationMode = indexToCalModePriMin(myIndex);
|
tsOutputChannels.calibrationMode = functionToCalModePriMin(myFunction);
|
||||||
tsOutputChannels.calibrationValue = primaryMin;
|
tsOutputChannels.calibrationValue = primaryMin;
|
||||||
chThdSleepMilliseconds(500);
|
chThdSleepMilliseconds(500);
|
||||||
|
|
||||||
tsOutputChannels.calibrationMode = indexToCalModeSecMax(myIndex);
|
tsOutputChannels.calibrationMode = functionToCalModeSecMax(myFunction);
|
||||||
tsOutputChannels.calibrationValue = secondaryMax;
|
tsOutputChannels.calibrationValue = secondaryMax;
|
||||||
chThdSleepMilliseconds(500);
|
chThdSleepMilliseconds(500);
|
||||||
tsOutputChannels.calibrationMode = indexToCalModeSecMin(myIndex);
|
tsOutputChannels.calibrationMode = functionToCalModeSecMin(myFunction);
|
||||||
tsOutputChannels.calibrationValue = secondaryMin;
|
tsOutputChannels.calibrationValue = secondaryMin;
|
||||||
chThdSleepMilliseconds(500);
|
chThdSleepMilliseconds(500);
|
||||||
|
|
||||||
|
@ -787,18 +822,14 @@ void doInitElectronicThrottle(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
||||||
addConsoleActionI("etb_freq", setEtbFrequency);
|
addConsoleActionI("etb_freq", setEtbFrequency);
|
||||||
#endif /* EFI_PROD_CODE */
|
#endif /* EFI_PROD_CODE */
|
||||||
|
|
||||||
// If you don't have a pedal (or VW idle valve mode), we have no business here.
|
// If you don't have a pedal we have no business here.
|
||||||
if (!CONFIG(dcMotorIdleValve) && !Sensor::hasSensor(SensorType::AcceleratorPedalPrimary)) {
|
if (!Sensor::hasSensor(SensorType::AcceleratorPedalPrimary)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pedal2tpsMap.init(config->pedalToTpsTable, config->pedalToTpsPedalBins, config->pedalToTpsRpmBins);
|
pedal2tpsMap.init(config->pedalToTpsTable, config->pedalToTpsPedalBins, config->pedalToTpsRpmBins);
|
||||||
|
|
||||||
if (CONFIG(dcMotorIdleValve)) {
|
|
||||||
engine->etbActualCount = 1;
|
|
||||||
} else {
|
|
||||||
engine->etbActualCount = Sensor::hasSensor(SensorType::Tps2) ? 2 : 1;
|
engine->etbActualCount = Sensor::hasSensor(SensorType::Tps2) ? 2 : 1;
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0 ; i < engine->etbActualCount; i++) {
|
for (int i = 0 ; i < engine->etbActualCount; i++) {
|
||||||
auto motor = initDcMotor(i, CONFIG(etb_use_two_wires) PASS_ENGINE_PARAMETER_SUFFIX);
|
auto motor = initDcMotor(i, CONFIG(etb_use_two_wires) PASS_ENGINE_PARAMETER_SUFFIX);
|
||||||
|
@ -806,8 +837,10 @@ void doInitElectronicThrottle(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
||||||
// If this motor is actually set up, init the etb
|
// If this motor is actually set up, init the etb
|
||||||
if (motor)
|
if (motor)
|
||||||
{
|
{
|
||||||
auto positionSensor = indexToTpsSensor(i, CONFIG(dcMotorIdleValve));
|
// TODO: configure per-motor in config so wastegate/VW idle works
|
||||||
engine->etbControllers[i]->init(positionSensor, motor, i, &engineConfiguration->etb, &pedal2tpsMap);
|
auto func = i == 0 ? ETB_Throttle1 : ETB_Throttle2;
|
||||||
|
|
||||||
|
engine->etbControllers[i]->init(func, motor, &engineConfiguration->etb, &pedal2tpsMap);
|
||||||
INJECT_ENGINE_REFERENCE(engine->etbControllers[i]);
|
INJECT_ENGINE_REFERENCE(engine->etbControllers[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,10 @@ class Logging;
|
||||||
class IEtbController : public ClosedLoopController<percent_t, percent_t> {
|
class IEtbController : public ClosedLoopController<percent_t, percent_t> {
|
||||||
public:
|
public:
|
||||||
DECLARE_ENGINE_PTR;
|
DECLARE_ENGINE_PTR;
|
||||||
virtual void init(SensorType positionSensor, DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) = 0;
|
|
||||||
|
// Initialize the throttle.
|
||||||
|
// returns true if the throttle was initialized, false otherwise.
|
||||||
|
virtual bool init(etb_function_e function, DcMotor *motor, pid_s *pidParameters, const ValueProvider3D* pedalMap) = 0;
|
||||||
virtual void reset() = 0;
|
virtual void reset() = 0;
|
||||||
virtual void setIdlePosition(percent_t pos) = 0;
|
virtual void setIdlePosition(percent_t pos) = 0;
|
||||||
virtual void update() = 0;
|
virtual void update() = 0;
|
||||||
|
@ -36,7 +39,7 @@ public:
|
||||||
|
|
||||||
class EtbController : public IEtbController {
|
class EtbController : public IEtbController {
|
||||||
public:
|
public:
|
||||||
void init(SensorType positionSensor, DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) override;
|
bool init(etb_function_e function, DcMotor *motor, pid_s *pidParameters, const ValueProvider3D* pedalMap) override;
|
||||||
void setIdlePosition(percent_t pos) override;
|
void setIdlePosition(percent_t pos) override;
|
||||||
void reset() override;
|
void reset() override;
|
||||||
|
|
||||||
|
@ -52,7 +55,11 @@ public:
|
||||||
|
|
||||||
// Helpers for individual parts of throttle control
|
// Helpers for individual parts of throttle control
|
||||||
expected<percent_t> observePlant() const override;
|
expected<percent_t> observePlant() const override;
|
||||||
|
|
||||||
expected<percent_t> getSetpoint() const override;
|
expected<percent_t> getSetpoint() const override;
|
||||||
|
expected<percent_t> getSetpointEtb() const;
|
||||||
|
expected<percent_t> getSetpointWastegate() const;
|
||||||
|
expected<percent_t> getSetpointIdleValve() const;
|
||||||
|
|
||||||
expected<percent_t> getOpenLoop(percent_t target) const override;
|
expected<percent_t> getOpenLoop(percent_t target) const override;
|
||||||
expected<percent_t> getClosedLoop(percent_t setpoint, percent_t observation) override;
|
expected<percent_t> getClosedLoop(percent_t setpoint, percent_t observation) override;
|
||||||
|
@ -70,11 +77,11 @@ protected:
|
||||||
// This is set if an automatic TPS calibration should be run
|
// This is set if an automatic TPS calibration should be run
|
||||||
bool m_isAutocal = false;
|
bool m_isAutocal = false;
|
||||||
|
|
||||||
int getMyIndex() const { return m_myIndex; }
|
etb_function_e getFunction() const { return m_function; }
|
||||||
DcMotor* getMotor() { return m_motor; }
|
DcMotor* getMotor() { return m_motor; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_myIndex = 0;
|
etb_function_e m_function = ETB_None;
|
||||||
SensorType m_positionSensor = SensorType::Invalid;
|
SensorType m_positionSensor = SensorType::Invalid;
|
||||||
DcMotor *m_motor = nullptr;
|
DcMotor *m_motor = nullptr;
|
||||||
Pid m_pid;
|
Pid m_pid;
|
||||||
|
|
|
@ -224,10 +224,6 @@ void applyIACposition(percent_t position DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
||||||
} else if (CONFIG(useStepperIdle)) {
|
} else if (CONFIG(useStepperIdle)) {
|
||||||
iacMotor.setTargetPosition(duty * engineConfiguration->idleStepperTotalSteps);
|
iacMotor.setTargetPosition(duty * engineConfiguration->idleStepperTotalSteps);
|
||||||
#endif /* EFI_UNIT_TEST */
|
#endif /* EFI_UNIT_TEST */
|
||||||
} else if (CONFIG(dcMotorIdleValve)) {
|
|
||||||
#if EFI_ELECTRONIC_THROTTLE_BODY
|
|
||||||
setEtbIdlePosition(position PASS_ENGINE_PARAMETER_SUFFIX);
|
|
||||||
#endif // EFI_ELECTRONIC_THROTTLE_BODY
|
|
||||||
} else {
|
} else {
|
||||||
if (!CONFIG(isDoubleSolenoidIdle)) {
|
if (!CONFIG(isDoubleSolenoidIdle)) {
|
||||||
idleSolenoidOpen.setSimplePwmDutyCycle(duty);
|
idleSolenoidOpen.setSimplePwmDutyCycle(duty);
|
||||||
|
|
|
@ -997,3 +997,11 @@ typedef enum __attribute__ ((__packed__)) {
|
||||||
AFR_AccPedal = 3,
|
AFR_AccPedal = 3,
|
||||||
AFR_CylFilling = 4,
|
AFR_CylFilling = 4,
|
||||||
} afr_override_e;
|
} afr_override_e;
|
||||||
|
|
||||||
|
typedef enum __attribute__ ((__packed__)) {
|
||||||
|
ETB_None = 0,
|
||||||
|
ETB_Throttle1 = 1,
|
||||||
|
ETB_Throttle2 = 2,
|
||||||
|
ETB_IdleValve = 3,
|
||||||
|
ETB_Wastegate = 4,
|
||||||
|
} etb_function_e;
|
||||||
|
|
|
@ -895,7 +895,7 @@ custom maf_sensor_type_e 4 bits, S32, @OFFSET@, [0:1], @@maf_sensor_type_e_enum@
|
||||||
bit enableInnovateLC2
|
bit enableInnovateLC2
|
||||||
bit showHumanReadableWarning
|
bit showHumanReadableWarning
|
||||||
bit stftIgnoreErrorMagnitude;+If enabled, adjust at a constant rate instead of a rate proportional to the current lambda error. This mode may be easier to tune, and more tolerant of sensor noise. Use of this mode is required if you have a narrowband O2 sensor.;
|
bit stftIgnoreErrorMagnitude;+If enabled, adjust at a constant rate instead of a rate proportional to the current lambda error. This mode may be easier to tune, and more tolerant of sensor noise. Use of this mode is required if you have a narrowband O2 sensor.;
|
||||||
bit dcMotorIdleValve;+Used on some German vehicles around late 90s: cable-operated throttle and DC motor idle air valve.\nSet the primary TPS to the cable-operated throttle's sensor\nSet the secondary TPS to the mini ETB's position sensor(s).
|
bit unused976b11;
|
||||||
bit enableSoftwareKnock
|
bit enableSoftwareKnock
|
||||||
bit verboseVVTDecoding;enable vvt_details
|
bit verboseVVTDecoding;enable vvt_details
|
||||||
bit invertCamVVTSignal;get invertCamVVTSignal
|
bit invertCamVVTSignal;get invertCamVVTSignal
|
||||||
|
|
|
@ -3036,12 +3036,11 @@ cmd_set_engine_type_default = "@@TS_IO_TEST_COMMAND_char@@\x00\x31\x00\x00"
|
||||||
|
|
||||||
dialog = etbDialogLeft
|
dialog = etbDialogLeft
|
||||||
field = "https://rusefi.com/s/etb"
|
field = "https://rusefi.com/s/etb"
|
||||||
field = "Late 90s German DC Motor idle", dcMotorIdleValve, { throttlePedalPositionAdcChannel == @@ADC_CHANNEL_NONE@@ }
|
|
||||||
field = "Detailed status in console", isVerboseETB
|
field = "Detailed status in console", isVerboseETB
|
||||||
field = "Disable ETB Motor", pauseEtbControl
|
field = "Disable ETB Motor", pauseEtbControl
|
||||||
; we need the term about stepper idle in here, because there's a bug in TS that you can't have different visibility
|
; we need the term about stepper idle in here, because there's a bug in TS that you can't have different visibility
|
||||||
; criteria for the same panel when used in multiple places
|
; criteria for the same panel when used in multiple places
|
||||||
panel = hbridgeHardware, { throttlePedalPositionAdcChannel != @@ADC_CHANNEL_NONE@@ || (useStepperIdle && useHbridges) || dcMotorIdleValve }
|
panel = hbridgeHardware, { throttlePedalPositionAdcChannel != @@ADC_CHANNEL_NONE@@ || (useStepperIdle && useHbridges) }
|
||||||
|
|
||||||
dialog = etbAutotune, "PID Autotune"
|
dialog = etbAutotune, "PID Autotune"
|
||||||
field = "First step: calibrate TPS and hit 'Burn'"
|
field = "First step: calibrate TPS and hit 'Burn'"
|
||||||
|
@ -3057,7 +3056,7 @@ cmd_set_engine_type_default = "@@TS_IO_TEST_COMMAND_char@@\x00\x31\x00\x00"
|
||||||
|
|
||||||
dialog = etbDialogRight
|
dialog = etbDialogRight
|
||||||
panel = etbIdleDialog, { throttlePedalPositionAdcChannel != @@ADC_CHANNEL_NONE@@ }
|
panel = etbIdleDialog, { throttlePedalPositionAdcChannel != @@ADC_CHANNEL_NONE@@ }
|
||||||
panel = etbPidDialog, { (throttlePedalPositionAdcChannel != @@ADC_CHANNEL_NONE@@) || dcMotorIdleValve }
|
panel = etbPidDialog, { (throttlePedalPositionAdcChannel != @@ADC_CHANNEL_NONE@@) }
|
||||||
panel = etbAutotune
|
panel = etbAutotune
|
||||||
|
|
||||||
; Neutral position handling not yet implemented!
|
; Neutral position handling not yet implemented!
|
||||||
|
|
|
@ -13,7 +13,7 @@ public:
|
||||||
// IEtbController mocks
|
// IEtbController mocks
|
||||||
MOCK_METHOD(void, reset, (), ());
|
MOCK_METHOD(void, reset, (), ());
|
||||||
MOCK_METHOD(void, update, (), (override));
|
MOCK_METHOD(void, update, (), (override));
|
||||||
MOCK_METHOD(void, init, (SensorType positionSensor, DcMotor* motor, int ownIndex, pid_s* pidParameters, const ValueProvider3D* pedalMap), (override));
|
MOCK_METHOD(bool, init, (etb_function_e function, DcMotor* motor, pid_s* pidParameters, const ValueProvider3D* pedalMap), (override));
|
||||||
MOCK_METHOD(void, setIdlePosition, (percent_t pos), (override));
|
MOCK_METHOD(void, setIdlePosition, (percent_t pos), (override));
|
||||||
MOCK_METHOD(void, autoCalibrateTps, (), (override));
|
MOCK_METHOD(void, autoCalibrateTps, (), (override));
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,8 @@ TEST(etb, initializationSingleThrottle) {
|
||||||
Sensor::setMockValue(SensorType::AcceleratorPedal, 0);
|
Sensor::setMockValue(SensorType::AcceleratorPedal, 0);
|
||||||
Sensor::setMockValue(SensorType::AcceleratorPedalPrimary, 0);
|
Sensor::setMockValue(SensorType::AcceleratorPedalPrimary, 0);
|
||||||
|
|
||||||
// Expect mock0 to be init with TPS 1, index 0, and PID params
|
// Expect mock0 to be init as throttle 1, and PID params
|
||||||
EXPECT_CALL(mocks[0], init(SensorType::Tps1, _, 0, &engineConfiguration->etb, Ne(nullptr)));
|
EXPECT_CALL(mocks[0], init(ETB_Throttle1, _, &engineConfiguration->etb, Ne(nullptr))).WillOnce(Return(true));
|
||||||
EXPECT_CALL(mocks[0], reset);
|
EXPECT_CALL(mocks[0], reset);
|
||||||
|
|
||||||
// We do not expect throttle #2 to be initialized
|
// We do not expect throttle #2 to be initialized
|
||||||
|
@ -70,42 +70,27 @@ TEST(etb, initializationDualThrottle) {
|
||||||
// The presence of a second TPS indicates dual throttle
|
// The presence of a second TPS indicates dual throttle
|
||||||
Sensor::setMockValue(SensorType::Tps2, 25.0f);
|
Sensor::setMockValue(SensorType::Tps2, 25.0f);
|
||||||
|
|
||||||
// Expect mock0 to be init with TPS 1, index 0, and PID params
|
// Expect mock0 to be init as throttle 1, and PID params
|
||||||
EXPECT_CALL(mocks[0], init(SensorType::Tps1, _, 0, &engineConfiguration->etb, Ne(nullptr)));
|
EXPECT_CALL(mocks[0], init(ETB_Throttle1, _, &engineConfiguration->etb, Ne(nullptr))).WillOnce(Return(true));
|
||||||
EXPECT_CALL(mocks[0], reset);
|
EXPECT_CALL(mocks[0], reset);
|
||||||
|
|
||||||
// Expect mock1 to be init with TPS 2, index 1, and PID params
|
// Expect mock1 to be init as throttle 2, and PID params
|
||||||
EXPECT_CALL(mocks[1], init(SensorType::Tps2, _, 1, &engineConfiguration->etb, Ne(nullptr)));
|
EXPECT_CALL(mocks[1], init(ETB_Throttle2, _, &engineConfiguration->etb, Ne(nullptr))).WillOnce(Return(true));
|
||||||
EXPECT_CALL(mocks[1], reset);
|
EXPECT_CALL(mocks[1], reset);
|
||||||
|
|
||||||
doInitElectronicThrottle(PASS_ENGINE_PARAMETER_SIGNATURE);
|
doInitElectronicThrottle(PASS_ENGINE_PARAMETER_SIGNATURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(etb, initializationDcMotorIdleValveMode) {
|
TEST(etb, initializationNoFunction) {
|
||||||
StrictMock<MockEtb> mocks[ETB_COUNT];
|
StrictMock<MockMotor> motor;
|
||||||
|
|
||||||
WITH_ENGINE_TEST_HELPER(TEST_ENGINE);
|
EtbController dut;
|
||||||
|
|
||||||
// Enable VW idle mode
|
// When init called with ETB_None, should ignore the provided params and return false
|
||||||
engineConfiguration->dcMotorIdleValve = true;
|
EXPECT_FALSE(dut.init(ETB_None, &motor, nullptr, nullptr));
|
||||||
|
|
||||||
for (int i = 0; i < ETB_COUNT; i++) {
|
// This should no-op, it shouldn't call motor.set(float duty)
|
||||||
engine->etbControllers[i] = &mocks[i];
|
dut.setOutput(0.5f);
|
||||||
EXPECT_CALL(mocks[i], setIdlePosition(33.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
// No accelerator pedal configured - this mode doesn't use it
|
|
||||||
|
|
||||||
// Expect mock0 to be init with TPS 2, index 0, and PID params
|
|
||||||
EXPECT_CALL(mocks[0], init(SensorType::Tps2, _, 0, &engineConfiguration->etb, Ne(nullptr)));
|
|
||||||
EXPECT_CALL(mocks[0], reset);
|
|
||||||
|
|
||||||
// We do not expect throttle #2 to be initialized
|
|
||||||
|
|
||||||
doInitElectronicThrottle(PASS_ENGINE_PARAMETER_SIGNATURE);
|
|
||||||
|
|
||||||
|
|
||||||
applyIACposition(33.0f PASS_ENGINE_PARAMETER_SUFFIX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(etb, idlePlumbing) {
|
TEST(etb, idlePlumbing) {
|
||||||
|
@ -144,7 +129,7 @@ TEST(etb, testSetpointOnlyPedal) {
|
||||||
// Uninitialized ETB must return unexpected (and not deference a null pointer)
|
// Uninitialized ETB must return unexpected (and not deference a null pointer)
|
||||||
EXPECT_EQ(etb.getSetpoint(), unexpected);
|
EXPECT_EQ(etb.getSetpoint(), unexpected);
|
||||||
|
|
||||||
etb.init(SensorType::Invalid, nullptr, 0, nullptr, &pedalMap);
|
etb.init(ETB_Throttle1, nullptr, nullptr, &pedalMap);
|
||||||
|
|
||||||
// Check endpoints and midpoint
|
// Check endpoints and midpoint
|
||||||
Sensor::setMockValue(SensorType::AcceleratorPedal, 0.0f);
|
Sensor::setMockValue(SensorType::AcceleratorPedal, 0.0f);
|
||||||
|
@ -194,7 +179,7 @@ TEST(etb, setpointIdle) {
|
||||||
.WillRepeatedly([](float xRpm, float y) {
|
.WillRepeatedly([](float xRpm, float y) {
|
||||||
return y;
|
return y;
|
||||||
});
|
});
|
||||||
etb.init(SensorType::Invalid, nullptr, 0, nullptr, &pedalMap);
|
etb.init(ETB_Throttle1, nullptr, nullptr, &pedalMap);
|
||||||
|
|
||||||
// No idle range, should just pass pedal
|
// No idle range, should just pass pedal
|
||||||
Sensor::setMockValue(SensorType::AcceleratorPedal, 0.0f);
|
Sensor::setMockValue(SensorType::AcceleratorPedal, 0.0f);
|
||||||
|
@ -231,14 +216,10 @@ TEST(etb, setpointIdle) {
|
||||||
EXPECT_FLOAT_EQ(55, etb.getSetpoint().value_or(-1));
|
EXPECT_FLOAT_EQ(55, etb.getSetpoint().value_or(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(etb, idleVolkswagenMode) {
|
TEST(etb, setpointIdleValveController) {
|
||||||
WITH_ENGINE_TEST_HELPER(TEST_ENGINE);
|
|
||||||
|
|
||||||
// In this mode the idle position should be passed thru as the setpoint directly
|
|
||||||
engineConfiguration->dcMotorIdleValve = true;
|
|
||||||
|
|
||||||
EtbController etb;
|
EtbController etb;
|
||||||
INJECT_ENGINE_REFERENCE(&etb);
|
|
||||||
|
etb.init(ETB_IdleValve, nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
etb.setIdlePosition(0);
|
etb.setIdlePosition(0);
|
||||||
EXPECT_FLOAT_EQ(0, etb.getSetpoint().value_or(-1));
|
EXPECT_FLOAT_EQ(0, etb.getSetpoint().value_or(-1));
|
||||||
|
@ -258,20 +239,36 @@ TEST(etb, etbTpsSensor) {
|
||||||
// Throw some distinct values on the TPS sensors so we can identify that we're getting the correct one
|
// Throw some distinct values on the TPS sensors so we can identify that we're getting the correct one
|
||||||
Sensor::setMockValue(SensorType::Tps1, 25.0f);
|
Sensor::setMockValue(SensorType::Tps1, 25.0f);
|
||||||
Sensor::setMockValue(SensorType::Tps2, 75.0f);
|
Sensor::setMockValue(SensorType::Tps2, 75.0f);
|
||||||
|
Sensor::setMockValue(SensorType::WastegatePosition, 33.0f);
|
||||||
|
Sensor::setMockValue(SensorType::IdlePosition, 66.0f);
|
||||||
|
|
||||||
// Test first throttle
|
// Test first throttle
|
||||||
{
|
{
|
||||||
EtbController etb;
|
EtbController etb;
|
||||||
etb.init(SensorType::Tps1, nullptr, 0, nullptr, nullptr);
|
etb.init(ETB_Throttle1, nullptr, nullptr, nullptr);
|
||||||
EXPECT_EQ(etb.observePlant().Value, 25.0f);
|
EXPECT_EQ(etb.observePlant().Value, 25.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test second throttle
|
// Test second throttle
|
||||||
{
|
{
|
||||||
EtbController etb;
|
EtbController etb;
|
||||||
etb.init(SensorType::Tps2, nullptr, 1, nullptr, nullptr);
|
etb.init(ETB_Throttle2, nullptr, nullptr, nullptr);
|
||||||
EXPECT_EQ(etb.observePlant().Value, 75.0f);
|
EXPECT_EQ(etb.observePlant().Value, 75.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test wastegate control
|
||||||
|
{
|
||||||
|
EtbController etb;
|
||||||
|
etb.init(ETB_Wastegate, nullptr, nullptr, nullptr);
|
||||||
|
EXPECT_EQ(etb.observePlant().Value, 33.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test idle valve control
|
||||||
|
{
|
||||||
|
EtbController etb;
|
||||||
|
etb.init(ETB_IdleValve, nullptr, nullptr, nullptr);
|
||||||
|
EXPECT_EQ(etb.observePlant().Value, 66.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(etb, setOutputInvalid) {
|
TEST(etb, setOutputInvalid) {
|
||||||
|
@ -281,7 +278,7 @@ TEST(etb, setOutputInvalid) {
|
||||||
|
|
||||||
EtbController etb;
|
EtbController etb;
|
||||||
INJECT_ENGINE_REFERENCE(&etb);
|
INJECT_ENGINE_REFERENCE(&etb);
|
||||||
etb.init(SensorType::Invalid, &motor, 0, nullptr, nullptr);
|
etb.init(ETB_Throttle1, &motor, nullptr, nullptr);
|
||||||
|
|
||||||
// Should be disabled in case of unexpected
|
// Should be disabled in case of unexpected
|
||||||
EXPECT_CALL(motor, disable());
|
EXPECT_CALL(motor, disable());
|
||||||
|
@ -295,7 +292,7 @@ TEST(etb, setOutputValid) {
|
||||||
|
|
||||||
EtbController etb;
|
EtbController etb;
|
||||||
INJECT_ENGINE_REFERENCE(&etb);
|
INJECT_ENGINE_REFERENCE(&etb);
|
||||||
etb.init(SensorType::Invalid, &motor, 0, nullptr, nullptr);
|
etb.init(ETB_Throttle1, &motor, nullptr, nullptr);
|
||||||
|
|
||||||
// Should be enabled and value set
|
// Should be enabled and value set
|
||||||
EXPECT_CALL(motor, enable());
|
EXPECT_CALL(motor, enable());
|
||||||
|
@ -311,7 +308,7 @@ TEST(etb, setOutputValid2) {
|
||||||
|
|
||||||
EtbController etb;
|
EtbController etb;
|
||||||
INJECT_ENGINE_REFERENCE(&etb);
|
INJECT_ENGINE_REFERENCE(&etb);
|
||||||
etb.init(SensorType::Invalid, &motor, 0, nullptr, nullptr);
|
etb.init(ETB_Throttle1, &motor, nullptr, nullptr);
|
||||||
|
|
||||||
// Should be enabled and value set
|
// Should be enabled and value set
|
||||||
EXPECT_CALL(motor, enable());
|
EXPECT_CALL(motor, enable());
|
||||||
|
@ -327,7 +324,7 @@ TEST(etb, setOutputOutOfRangeHigh) {
|
||||||
|
|
||||||
EtbController etb;
|
EtbController etb;
|
||||||
INJECT_ENGINE_REFERENCE(&etb);
|
INJECT_ENGINE_REFERENCE(&etb);
|
||||||
etb.init(SensorType::Invalid, &motor, 0, nullptr, nullptr);
|
etb.init(ETB_Throttle1, &motor, nullptr, nullptr);
|
||||||
|
|
||||||
// Should be enabled and value set
|
// Should be enabled and value set
|
||||||
EXPECT_CALL(motor, enable());
|
EXPECT_CALL(motor, enable());
|
||||||
|
@ -343,7 +340,7 @@ TEST(etb, setOutputOutOfRangeLow) {
|
||||||
|
|
||||||
EtbController etb;
|
EtbController etb;
|
||||||
INJECT_ENGINE_REFERENCE(&etb);
|
INJECT_ENGINE_REFERENCE(&etb);
|
||||||
etb.init(SensorType::Invalid, &motor, 0, nullptr, nullptr);
|
etb.init(ETB_Throttle1, &motor, nullptr, nullptr);
|
||||||
|
|
||||||
// Should be enabled and value set
|
// Should be enabled and value set
|
||||||
EXPECT_CALL(motor, enable());
|
EXPECT_CALL(motor, enable());
|
||||||
|
@ -359,7 +356,7 @@ TEST(etb, setOutputPauseControl) {
|
||||||
|
|
||||||
EtbController etb;
|
EtbController etb;
|
||||||
INJECT_ENGINE_REFERENCE(&etb);
|
INJECT_ENGINE_REFERENCE(&etb);
|
||||||
etb.init(SensorType::Invalid, &motor, 0, nullptr, nullptr);
|
etb.init(ETB_Throttle1, &motor, nullptr, nullptr);
|
||||||
|
|
||||||
// Pause control - should get no output
|
// Pause control - should get no output
|
||||||
engineConfiguration->pauseEtbControl = true;
|
engineConfiguration->pauseEtbControl = true;
|
||||||
|
@ -377,7 +374,7 @@ TEST(etb, closedLoopPid) {
|
||||||
pid.minValue = -60;
|
pid.minValue = -60;
|
||||||
|
|
||||||
EtbController etb;
|
EtbController etb;
|
||||||
etb.init(SensorType::Invalid, nullptr, 0, &pid, nullptr);
|
etb.init(ETB_Throttle1, nullptr, &pid, nullptr);
|
||||||
|
|
||||||
// Disable autotune for now
|
// Disable autotune for now
|
||||||
Engine e;
|
Engine e;
|
||||||
|
|
Loading…
Reference in New Issue