rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
boost_control.cpp
Go to the documentation of this file.
1/*
2 * boost_control.cpp
3 *
4 * Created on: 13. des. 2019
5 * Author: Ola Ruud
6 */
7#include "pch.h"
8
9#if EFI_BOOST_CONTROL
10
11#include "boost_control.h"
12#include "electronic_throttle.h"
14
15#if defined(HAS_OS_ACCESS)
16#error "Unexpected OS ACCESS HERE"
17#endif
18
19namespace {
22 Map2D<BOOST_CURVE_SIZE, float, float> boostCltCorr { "clt" };
23 Map2D<BOOST_CURVE_SIZE, float, float> boostIatCorr { "iat" };
24 Map2D<BOOST_CURVE_SIZE, float, float> boostCltAdder { "clt (adder)" };
25 Map2D<BOOST_CURVE_SIZE, float, float> boostIatAdder { "iat (adder)" };
26 SimplePwm boostPwmControl("boost");
27}
28
30 IPwm* const pwm,
31 const ValueProvider3D* const openLoopMap,
32 const ValueProvider3D* const closedLoopTargetMap,
33 const ValueProvider2D& cltMultiplierProvider,
34 const ValueProvider2D& iatMultiplierProvider,
35 const ValueProvider2D& cltAdderProvider,
36 const ValueProvider2D& iatAdderProvider,
37 pid_s* const pidParams
38) {
39 m_pwm = pwm;
40 m_openLoopMap = openLoopMap;
41 m_closedLoopTargetMap = closedLoopTargetMap;
42 m_cltBoostCorrMap = &cltMultiplierProvider;
43 m_iatBoostCorrMap = &iatMultiplierProvider;
44 m_cltBoostAdderMap = &cltAdderProvider;
45 m_iatBoostAdderMap = &iatAdderProvider;
46
47 m_pid.initPidClass(pidParams);
48 resetLua();
49
50 hasInitBoost = true;
51}
52
58
60#if EFI_PROD_CODE
62#endif
63
64 if (!previousConfig || !m_pid.isSame(&previousConfig->boostPid)) {
65 m_shouldResetPid = true;
66 }
67}
68
70 expected<float> map = Sensor::get(SensorType::Map);
71 if (!map.Valid && engineConfiguration->boostType != CLOSED_LOOP) {
72 // if we're in open loop only let's somewhat operate even without valid Map sensor
73 map = 0;
74 }
75 isPlantValid = map.Valid;
76 return map;
77}
78
80 // If we're in open loop only mode, disregard any target computation.
81 // Open loop needs to work even in case of invalid closed loop config
83 if (isNotClosedLoop) {
86 }
87
89
92
93 if (isTpsInvalid) {
94 return unexpected;
95 }
96
97 efiAssert(ObdCode::OBD_PCM_Processor_Fault, m_closedLoopTargetMap != nullptr, "boost closed loop target", unexpected);
98
99 float target = m_closedLoopTargetMap->getValue(rpm, driverIntent.Value);
100#if EFI_ENGINE_CONTROL
101 // Add any blends if configured
102 for (size_t i = 0; i < efi::size(config->boostClosedLoopBlends); i++) {
103 auto result = calculateBlend(config->boostClosedLoopBlends[i], rpm, driverIntent.Value);
104
105 engine->outputChannels.boostClosedLoopBlendParameter[i] = result.BlendParameter;
108 engine->outputChannels.boostClosedLoopBlendYAxis[i] = result.TableYAxis;
109
110 target += result.Value;
111 }
112#endif //EFI_ENGINE_CONTROL
113
114 target *= luaTargetMult;
115 target += luaTargetAdd;
116 const std::optional<float> temperatureAdder = getBoostControlTargetTemperatureAdder();
117 if (temperatureAdder.has_value()) {
118 target += temperatureAdder.value();
119 }
120 return target;
121}
122
123expected<percent_t> BoostController::getOpenLoop(float target) {
124 // Boost control open loop doesn't care about target - only TPS/RPM
125 UNUSED(target);
126
129
130 isTpsInvalid = !driverIntent.Valid;
131
132 if (isTpsInvalid) {
133 return unexpected;
134 }
135
136 efiAssert(ObdCode::OBD_PCM_Processor_Fault, m_openLoopMap != nullptr, "boost open loop", unexpected);
137 efiAssert(ObdCode::OBD_PCM_Processor_Fault, m_cltBoostCorrMap != nullptr, "boost CLT multiplier", unexpected);
138 efiAssert(ObdCode::OBD_PCM_Processor_Fault, m_iatBoostCorrMap != nullptr, "boost IAT multiplier", unexpected);
139
142
143#if EFI_ENGINE_CONTROL
144 // Add any blends if configured
145 for (size_t i = 0; i < efi::size(config->boostOpenLoopBlends); i++) {
146 auto result = calculateBlend(config->boostOpenLoopBlends[i], rpm, driverIntent.Value);
147
148 engine->outputChannels.boostOpenLoopBlendParameter[i] = result.BlendParameter;
151 engine->outputChannels.boostOpenLoopBlendYAxis[i] = result.TableYAxis;
152
153 openLoop += result.Value;
154 }
155#endif // EFI_ENGINE_CONTROL
156
157 // Add gear-based adder
159 float gearAdder = engineConfiguration->gearBasedOpenLoopBoostAdder[static_cast<int>(gear) + 1];
160 openLoop += gearAdder;
161
162 openLoopPart = openLoop;
163 return openLoop;
164}
165
166percent_t BoostController::getClosedLoopImpl(float target, float manifoldPressure) {
167 // If we're in open loop only mode, make no closed loop correction.
169 if (isNotClosedLoop) {
170 return 0;
171 }
172
173 // Reset PID if requested
174 if (m_shouldResetPid) {
175 m_pid.reset();
176 m_shouldResetPid = false;
177 }
178
179 // If the engine isn't running, don't correct.
181 if (isZeroRpm) {
182 m_pid.reset();
183 return 0;
184 }
185
188 // We're below the CL threshold, inhibit CL for now
189 m_pid.reset();
190 return 0;
191 }
192
193 return m_pid.getOutput(target, manifoldPressure, FAST_CALLBACK_PERIOD_MS / 1000.0f);
194}
195
197 const float rpm,
198 const float driverIntent
199) const {
200 float result = m_openLoopMap->getValue(rpm, driverIntent);
201 std::optional<float> cltBoostMultiplier = getBoostTemperatureCorrection(SensorType::Clt, *m_cltBoostCorrMap);
202 if (cltBoostMultiplier.has_value()) {
203 result *= cltBoostMultiplier.value();
204 }
205 std::optional<float> iatBoostMultiplier = getBoostTemperatureCorrection(SensorType::Iat, *m_iatBoostCorrMap);
206 if (iatBoostMultiplier.has_value()) {
207 result *= iatBoostMultiplier.value();
208 }
209 return result;
210}
211
214 const std::optional<float> iatBoostAdder = getBoostTemperatureCorrection(SensorType::Iat, *m_iatBoostAdderMap);
215 if (iatBoostAdder.has_value()) {
216 if (result.has_value()) {
217 result.value() += iatBoostAdder.value();
218 } else {
219 result = iatBoostAdder;
220 }
221 }
222 return result;
223}
224
226 const SensorType sensorType,
227 const ValueProvider2D& correctionCurve
228) const {
229 const SensorResult temperature = Sensor::get(sensorType);
230 if (temperature.Valid) {
231 const std::optional<float> boostCorrection = correctionCurve.getValue(temperature.Value);
232 if (boostCorrection.has_value()) {
233 return std::make_optional<float>(boostCorrection.value());
234 }
235 }
236 return {};
237}
238
239
240expected<percent_t> BoostController::getClosedLoop(float target, float manifoldPressure) {
241 boostControllerClosedLoopPart = getClosedLoopImpl(target, manifoldPressure);
242
244
245 boostControlTarget = target;
246
247 return (float)boostControllerClosedLoopPart;
248}
249
253
254void BoostController::setOutput(expected<float> output) {
255 // this clamping is just for happier gauge #6339
256 boostOutput = clampPercentValue(output.value_or(engineConfiguration->boostControlSafeDutyCycle));
257
259 // If not enabled, force 0% output
260 boostOutput = 0;
261 }
262
263 float duty = PERCENT_TO_DUTY(boostOutput);
264
267 } else {
268#if EFI_ELECTRONIC_THROTTLE_BODY
269 // inject wastegate position into DC controllers, pretty weird workflow to be honest
270 // todo: should it be DC controller pulling?
272#endif // EFI_ELECTRONIC_THROTTLE_BODY
273 }
274}
275
277 if (!hasInitBoost) {
278 return;
279 }
280
281 m_pid.iTermMin = -20;
282 m_pid.iTermMax = 20;
283
287
289
290 if (!isBoostControlled) {
291 // Passing unexpected will use the safe duty cycle configured by the user
292 setOutput(unexpected);
293 } else {
295 }
296}
297
306
310
311 for (int loadIndex = 0; loadIndex < BOOST_LOAD_COUNT; loadIndex++) {
312 for (int rpmIndex = 0; rpmIndex < BOOST_RPM_COUNT; rpmIndex++) {
313 config->boostTableClosedLoop[loadIndex][rpmIndex] = (float)config->boostClosedLoopLoadBins[loadIndex];
314 }
315 }
316
317 // Defaults for ETB-style wastegate actuator
321}
322
324#if !EFI_UNIT_TEST
325 // Only init if a pin is set, no need to start PWM without a pin
327 return;
328 }
329
331 &boostPwmControl,
332 "Boost",
336 /*dutyCycle*/0
337 );
338#endif /* EFI_UNIT_TEST */
339}
340
342#if EFI_PROD_CODE
343 if (engine->module<BoostController>().unmock().hasInitBoost) {
344 // already initialized - nothing to do here
345 return;
346 }
347 // todo: why do we have 'isBoostControlEnabled' setting exactly?
348 // 'initVvtActuators' is an example of a subsystem without explicit enable
350 return;
351 }
352
353 bool hasAnyEtbWastegate = false;
354
355 for (size_t i = 0; i < efi::size(engineConfiguration->etbFunctions); i++) {
356 hasAnyEtbWastegate |= engineConfiguration->etbFunctions[i] == DC_Wastegate;
357 }
358
359 // If we have neither a boost PWM pin nor ETB wastegate, nothing more to do
360 if (!isBrainPinValid(engineConfiguration->boostControlPin) && !hasAnyEtbWastegate) {
361 return;
362 }
363#endif
364
365 // Set up open & closed loop tables
368 boostCltCorr.initTable(config->cltBoostCorr, config->cltBoostCorrBins);
369 boostIatCorr.initTable(config->iatBoostCorr, config->iatBoostCorrBins);
370 boostCltAdder.initTable(config->cltBoostAdder, config->cltBoostAdderBins);
371 boostIatAdder.initTable(config->iatBoostAdder, config->iatBoostAdderBins);
372
373 // Set up boost controller instance
374 engine->module<BoostController>().unmock().init(
375 &boostPwmControl,
376 &boostMapOpen,
377 &boostMapClosed,
378 boostCltCorr,
379 boostIatCorr,
380 boostCltAdder,
381 boostIatAdder,
383 );
384
385#if !EFI_UNIT_TEST
387#endif
388}
389
397
398#endif // EFI_BOOST_CONTROL
void initBoostCtrl()
void setDefaultBoostParameters()
bool isBoostControlSolenoidMode()
void startBoostPin()
void initBoostCtrl()
const ValueProvider3D * m_closedLoopTargetMap
const ValueProvider2D * m_cltBoostCorrMap
expected< percent_t > getClosedLoop(float target, float manifoldPressure) override
void setOutput(expected< percent_t > outputValue) override
const ValueProvider2D * m_iatBoostCorrMap
float getBoostControlDutyCycleWithTemperatureCorrections(const float rpm, const float driverIntent) const
percent_t getClosedLoopImpl(float target, float manifoldPressure)
std::optional< float > getBoostControlTargetTemperatureAdder() const
void setDefaultConfiguration() override
std::optional< float > getBoostTemperatureCorrection(const SensorType sensorType, const ValueProvider2D &correctionCurve) const
const ValueProvider2D * m_iatBoostAdderMap
const ValueProvider3D * m_openLoopMap
expected< percent_t > getOpenLoop(float target) override
const ValueProvider2D * m_cltBoostAdderMap
expected< float > getSetpoint() override
void onConfigurationChange(engine_configuration_s const *previousConfig) override
expected< float > observePlant() override
void init(IPwm *const pmw, const ValueProvider3D *const openLoopMap, const ValueProvider3D *const closedLoopTargetMap, const ValueProvider2D &cltMultiplierProvider, const ValueProvider2D &iatMultiplierProvider, const ValueProvider2D &cltAdderProvider, const ValueProvider2D &iatAdderProvider, pid_s *const pidParams)
void onFastCallback() override
SingleTimerExecutor scheduler
Definition engine.h:271
TunerStudioOutputChannels outputChannels
Definition engine.h:109
constexpr auto & module()
Definition engine.h:200
RegisteredOutputPin boostPin
Definition efi_gpio.h:111
Definition Map2D.h:15
float iTermMax
Definition efi_pid.h:69
float iTermMin
Definition efi_pid.h:68
bool isSame(const pid_s *parameters) const
Definition efi_pid.cpp:39
void postState(pid_status_s &pidStatus) const
Definition efi_pid.cpp:144
virtual void reset()
Definition efi_pid.cpp:103
void initPidClass(pid_s *parameters)
Definition efi_pid.cpp:24
float getOutput(float target, float input)
Definition efi_pid.cpp:56
virtual SensorResult get() const =0
static float getOrZero(SensorType type)
Definition sensor.h:83
virtual std::optional< float > getValue(float x) const =0
virtual float getValue(float xColumn, float yRow) const =0
EnginePins enginePins
Definition efi_gpio.cpp:24
void setEtbWastegatePosition(percent_t pos)
static EngineAccessor engine
Definition engine.h:413
static constexpr persistent_config_s * config
static constexpr engine_configuration_s * engineConfiguration
BlendResult calculateBlend(blend_table_s &cfg, float rpm, float load)
expected< float > readGppwmChannel(gppwm_channel_e channel)
static ProxySensor driverIntent(SensorType::DriverThrottleIntent)
UNUSED(samplingTimeSeconds)
@ OBD_PCM_Processor_Fault
bool isBrainPinValid(brain_pin_e brainPin)
void startSimplePwm(SimplePwm *state, const char *msg, Scheduler *executor, OutputPin *output, float frequency, float dutyCycle, pwm_gen_callback *callback)
expected< float > SensorResult
Definition sensor.h:46
SensorType
Definition sensor_type.h:18
@ DriverThrottleIntent
virtual void setSimplePwmDutyCycle(float dutyCycle)=0
scaled_channel< int16_t, 30, 1 > boostControlTarget
scaled_channel< int16_t, 10, 1 > openLoopYAxis
scaled_channel< int16_t, 100, 1 > boostOutput
scaled_channel< int8_t, 2, 1 > boostControllerClosedLoopPart
scaled_channel< int16_t, 2, 1 > luaTargetAdd
scaled_channel< int8_t, 2, 1 > gearBasedOpenLoopBoostAdder[TCU_GEAR_COUNT]
scaled_channel< int16_t, 10, 1 > boostClosedLoopBlendYAxis[BOOST_BLEND_COUNT]
scaled_channel< int16_t, 10, 1 > boostClosedLoopBlendParameter[BOOST_BLEND_COUNT]
scaled_channel< uint8_t, 2, 1 > boostClosedLoopBlendBias[BOOST_BLEND_COUNT]
scaled_channel< int16_t, 10, 1 > boostOpenLoopBlendParameter[BOOST_BLEND_COUNT]
scaled_channel< int16_t, 10, 1 > boostClosedLoopBlendOutput[BOOST_BLEND_COUNT]
scaled_channel< uint8_t, 2, 1 > boostOpenLoopBlendBias[BOOST_BLEND_COUNT]
int8_t boostOpenLoopBlendOutput[BOOST_BLEND_COUNT]
scaled_channel< int16_t, 10, 1 > boostOpenLoopBlendYAxis[BOOST_BLEND_COUNT]
scaled_channel< uint8_t, 1, 100 > boostRpmBins[BOOST_RPM_COUNT]
scaled_channel< uint8_t, 1, 2 > boostTableClosedLoop[BOOST_LOAD_COUNT][BOOST_RPM_COUNT]
scaled_channel< uint8_t, 2, 1 > boostTableOpenLoop[BOOST_LOAD_COUNT][BOOST_RPM_COUNT]
void setRpmTableBin(TValue(&array)[TSize])
void setLinearCurve(TValue(&array)[TSize], float from, float to, float precision=0.01f)
static float duty