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#define NO_PIN_PERIOD 500
16
17#if defined(HAS_OS_ACCESS)
18#error "Unexpected OS ACCESS HERE"
19#endif
20
21namespace {
24 Map2D<BOOST_CURVE_SIZE, float, float> boostCltCorr { "clt" };
25 Map2D<BOOST_CURVE_SIZE, float, float> boostIatCorr { "iat" };
26 Map2D<BOOST_CURVE_SIZE, float, float> boostCltAdder { "clt (adder)" };
27 Map2D<BOOST_CURVE_SIZE, float, float> boostIatAdder { "iat (adder)" };
28 SimplePwm boostPwmControl("boost");
29}
30
32 IPwm* const pwm,
33 const ValueProvider3D* const openLoopMap,
34 const ValueProvider3D* const closedLoopTargetMap,
35 const ValueProvider2D& cltMultiplierProvider,
36 const ValueProvider2D& iatMultiplierProvider,
37 const ValueProvider2D& cltAdderProvider,
38 const ValueProvider2D& iatAdderProvider,
39 pid_s* const pidParams
40) {
41 m_pwm = pwm;
42 m_openLoopMap = openLoopMap;
43 m_closedLoopTargetMap = closedLoopTargetMap;
44 m_cltBoostCorrMap = &cltMultiplierProvider;
45 m_iatBoostCorrMap = &iatMultiplierProvider;
46 m_cltBoostAdderMap = &cltAdderProvider;
47 m_iatBoostAdderMap = &iatAdderProvider;
48
49 m_pid.initPidClass(pidParams);
50 resetLua();
51
52 hasInitBoost = true;
53}
54
60
62#if EFI_PROD_CODE
64#endif
65
66 if (!previousConfig || !m_pid.isSame(&previousConfig->boostPid)) {
67 m_shouldResetPid = true;
68 }
69}
70
72 expected<float> map = Sensor::get(SensorType::Map);
73 if (!map.Valid && engineConfiguration->boostType != CLOSED_LOOP) {
74 // if we're in open loop only let's somewhat operate even without valid Map sensor
75 map = 0;
76 }
77 isPlantValid = map.Valid;
78 return map;
79}
80
82 // If we're in open loop only mode, disregard any target computation.
83 // Open loop needs to work even in case of invalid closed loop config
85 if (isNotClosedLoop) {
88 }
89
91
94
95 if (isTpsInvalid) {
96 return unexpected;
97 }
98
99 efiAssert(ObdCode::OBD_PCM_Processor_Fault, m_closedLoopTargetMap != nullptr, "boost closed loop target", unexpected);
100
101 float target = m_closedLoopTargetMap->getValue(rpm, driverIntent.Value);
102#if EFI_ENGINE_CONTROL
103 // Add any blends if configured
104 for (size_t i = 0; i < efi::size(config->boostClosedLoopBlends); i++) {
105 auto result = calculateBlend(config->boostClosedLoopBlends[i], rpm, driverIntent.Value);
106
107 engine->outputChannels.boostClosedLoopBlendParameter[i] = result.BlendParameter;
110 engine->outputChannels.boostClosedLoopBlendYAxis[i] = result.TableYAxis;
111
112 target += result.Value;
113 }
114#endif //EFI_ENGINE_CONTROL
115
116 target *= luaTargetMult;
117 target += luaTargetAdd;
118 const std::optional<float> temperatureAdder = getBoostControlTargetTemperatureAdder();
119 if (temperatureAdder.has_value()) {
120 target += temperatureAdder.value();
121 }
122 return target;
123}
124
125expected<percent_t> BoostController::getOpenLoop(float target) {
126 // Boost control open loop doesn't care about target - only TPS/RPM
127 UNUSED(target);
128
131
132 isTpsInvalid = !driverIntent.Valid;
133
134 if (isTpsInvalid) {
135 return unexpected;
136 }
137
138 efiAssert(ObdCode::OBD_PCM_Processor_Fault, m_openLoopMap != nullptr, "boost open loop", unexpected);
139 efiAssert(ObdCode::OBD_PCM_Processor_Fault, m_cltBoostCorrMap != nullptr, "boost CLT multiplier", unexpected);
140 efiAssert(ObdCode::OBD_PCM_Processor_Fault, m_iatBoostCorrMap != nullptr, "boost IAT multiplier", unexpected);
141
143
144#if EFI_ENGINE_CONTROL
145 // Add any blends if configured
146 for (size_t i = 0; i < efi::size(config->boostOpenLoopBlends); i++) {
147 auto result = calculateBlend(config->boostOpenLoopBlends[i], rpm, driverIntent.Value);
148
149 engine->outputChannels.boostOpenLoopBlendParameter[i] = result.BlendParameter;
152 engine->outputChannels.boostOpenLoopBlendYAxis[i] = result.TableYAxis;
153
154 openLoop += result.Value;
155 }
156#endif // EFI_ENGINE_CONTROL
157
158 // Add gear-based adder
160 float gearAdder = engineConfiguration->gearBasedOpenLoopBoostAdder[static_cast<int>(gear) + 1];
161 openLoop += gearAdder;
162
163 openLoopPart = openLoop;
164 return openLoop;
165}
166
167percent_t BoostController::getClosedLoopImpl(float target, float manifoldPressure) {
168 // If we're in open loop only mode, make no closed loop correction.
170 if (isNotClosedLoop) {
171 return 0;
172 }
173
174 // Reset PID if requested
175 if (m_shouldResetPid) {
176 m_pid.reset();
177 m_shouldResetPid = false;
178 }
179
180 // If the engine isn't running, don't correct.
182 if (isZeroRpm) {
183 m_pid.reset();
184 return 0;
185 }
186
189 // We're below the CL threshold, inhibit CL for now
190 m_pid.reset();
191 return 0;
192 }
193
194 return m_pid.getOutput(target, manifoldPressure, FAST_CALLBACK_PERIOD_MS / 1000.0f);
195}
196
198 const float rpm,
199 const float driverIntent
200) const {
201 float result = m_openLoopMap->getValue(rpm, driverIntent);
202 std::optional<float> cltBoostMultiplier = getBoostTemperatureCorrection(SensorType::Clt, *m_cltBoostCorrMap);
203 if (cltBoostMultiplier.has_value()) {
204 result *= cltBoostMultiplier.value();
205 }
206 std::optional<float> iatBoostMultiplier = getBoostTemperatureCorrection(SensorType::Iat, *m_iatBoostCorrMap);
207 if (iatBoostMultiplier.has_value()) {
208 result *= iatBoostMultiplier.value();
209 }
210 return result;
211}
212
215 const std::optional<float> iatBoostAdder = getBoostTemperatureCorrection(SensorType::Iat, *m_iatBoostAdderMap);
216 if (iatBoostAdder.has_value()) {
217 if (result.has_value()) {
218 result.value() += iatBoostAdder.value();
219 } else {
220 result = iatBoostAdder;
221 }
222 }
223 return result;
224}
225
227 const SensorType sensorType,
228 const ValueProvider2D& correctionCurve
229) const {
230 const SensorResult temperature = Sensor::get(sensorType);
231 if (temperature.Valid) {
232 const std::optional<float> boostCorrection = correctionCurve.getValue(temperature.Value);
233 if (boostCorrection.has_value()) {
234 return std::make_optional<float>(boostCorrection.value());
235 }
236 }
237 return {};
238}
239
240
241expected<percent_t> BoostController::getClosedLoop(float target, float manifoldPressure) {
242 boostControllerClosedLoopPart = getClosedLoopImpl(target, manifoldPressure);
243
245
246 boostControlTarget = target;
247
248 return (float)boostControllerClosedLoopPart;
249}
250
254
255void BoostController::setOutput(expected<float> output) {
256 // this clamping is just for happier gauge #6339
257 boostOutput = clampPercentValue(output.value_or(engineConfiguration->boostControlSafeDutyCycle));
258
260 // If not enabled, force 0% output
261 boostOutput = 0;
262 }
263
264 float duty = PERCENT_TO_DUTY(boostOutput);
265
268 } else {
269#if EFI_ELECTRONIC_THROTTLE_BODY
270 // inject wastegate position into DC controllers, pretty weird workflow to be honest
271 // todo: should it be DC controller pulling?
273#endif // EFI_ELECTRONIC_THROTTLE_BODY
274 }
275}
276
278 if (!hasInitBoost) {
279 return;
280 }
281
282 m_pid.iTermMin = -20;
283 m_pid.iTermMax = 20;
284
288
290
291 if (!isBoostControlled) {
292 // Passing unexpected will use the safe duty cycle configured by the user
293 setOutput(unexpected);
294 } else {
296 }
297}
298
307
311
312 for (int loadIndex = 0; loadIndex < BOOST_LOAD_COUNT; loadIndex++) {
313 for (int rpmIndex = 0; rpmIndex < BOOST_RPM_COUNT; rpmIndex++) {
314 config->boostTableClosedLoop[loadIndex][rpmIndex] = (float)config->boostClosedLoopLoadBins[loadIndex];
315 }
316 }
317
318 // Defaults for ETB-style wastegate actuator
322}
323
325#if !EFI_UNIT_TEST
326 // Only init if a pin is set, no need to start PWM without a pin
328 return;
329 }
330
332 &boostPwmControl,
333 "Boost",
337 /*dutyCycle*/0
338 );
339#endif /* EFI_UNIT_TEST */
340}
341
343#if EFI_PROD_CODE
344 if (engine->module<BoostController>().unmock().hasInitBoost) {
345 // already initialized - nothing to do here
346 return;
347 }
348 // todo: why do we have 'isBoostControlEnabled' setting exactly?
349 // 'initVvtActuators' is an example of a subsystem without explicit enable
351 return;
352 }
353
354 bool hasAnyEtbWastegate = false;
355
356 for (size_t i = 0; i < efi::size(engineConfiguration->etbFunctions); i++) {
357 hasAnyEtbWastegate |= engineConfiguration->etbFunctions[i] == DC_Wastegate;
358 }
359
360 // If we have neither a boost PWM pin nor ETB wastegate, nothing more to do
361 if (!isBrainPinValid(engineConfiguration->boostControlPin) && !hasAnyEtbWastegate) {
362 return;
363 }
364#endif
365
366 // Set up open & closed loop tables
369 boostCltCorr.initTable(config->cltBoostCorr, config->cltBoostCorrBins);
370 boostIatCorr.initTable(config->iatBoostCorr, config->iatBoostCorrBins);
371 boostCltAdder.initTable(config->cltBoostAdder, config->cltBoostAdderBins);
372 boostIatAdder.initTable(config->iatBoostAdder, config->iatBoostAdderBins);
373
374 // Set up boost controller instance
375 engine->module<BoostController>().unmock().init(
376 &boostPwmControl,
377 &boostMapOpen,
378 &boostMapClosed,
379 boostCltCorr,
380 boostIatCorr,
381 boostCltAdder,
382 boostIatAdder,
384 );
385
386#if !EFI_UNIT_TEST
388#endif
389}
390
398
399#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, 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